混淆关于return语句的内容
关于return语句的困惑和解决方法
在下面的内容中,有人对于return语句的执行顺序产生了疑问,他想知道在返回1之后,为什么还会执行++操作?
这是因为C标准中对后缀递增运算符的定义如下:
6.5.2.4 后缀递增和递减运算符
[...]
2 后缀++运算符的结果是操作数的值。在得到结果之后,操作数的值会自增。(也就是说,相应类型的值1会被加到操作数上)
因此,在执行return语句之前,变量i会被递增,但由于后缀运算的结果是“原始”值,return语句返回的是这个“原始”值。这就是为什么在返回1之后还会执行++操作的原因。
为了解决这个问题,可以使用前缀递增运算符来改变return语句的执行顺序。前缀递增运算符会先使操作数自增,然后返回自增后的值。这样,return语句就可以返回递增后的值了。例如,可以将代码修改为:
return ++i;
这样就可以保证在返回1之前先执行++操作,然后返回递增后的值。这样就解决了关于return语句执行顺序的困惑。
关于返回语句的困惑
在理解这个问题之前,有三点需要记住:
static
变量在函数中只要被创建一次,就会在整个程序的运行期间持续存在。- 返回的变量还有后缀的++操作符,意思是:“使用这个值(即返回它),然后在之后对它进行递增”:递增后的值不会被返回。
这就是为什么这个变量会记住发生的事情并递增的原因。
为什么你看到的是"3 2 1"而不是"1 2 3"呢?
参数的求值顺序是不确定的,由编译器决定,参考https://stackoverflow.com/a/12960263/1938163
如果你真的想知道这个值是如何先返回再递增的,可以查看生成的汇编代码:
demo(): # () movl demo()::i, %eax # move i and put it into eax movl %eax, %ecx # Move eax into ecx -> eax will be used/returned! addl $1, %ecx # Increment ecx movl %ecx, demo()::i # save ecx into i -> this is for the next round! ret # returns! main: # pushq %rbp movq %rsp, %rbp subq $16, %rsp movl $0, -4(%rbp) callq demo() # Call demo() movl %eax, -8(%rbp) # save eax in rbp-8 (contains 1, demo::i is 2 for the next round) callq demo() # Call demo() movl %eax, -12(%rbp) # save eax in rbp-12 (contains 2, demo::i is 3 for the next round) callq demo() # Call demo() leaq .L.str, %rdi # load str address movl -8(%rbp), %esi # esi points to 1 movl -12(%rbp), %edx # edx points to 2 movl %eax, %ecx # move eax (3) into ecx (demo::i is 4 but doesn't get used) movb $0, %al # needed by the ABI to call printf callq printf # call printf() and display 3 2 1 movl $0, %ecx movl %eax, -16(%rbp) movl %ecx, %eax addq $16, %rsp popq %rbp ret demo()::i: .L.str: .asciz "%d %d %d\n"
64位ABI使用寄存器(RDI,RSI,RDX,RCX,R8和R9)而不是堆栈来传递参数。
请告诉我eax
是什么意思?
eax是一个寄存器,一个足够容纳一个值或一个地址的内存位置。它们不是变量,当你设置一个寄存器时,它会一直持续到下一次改变,所以在上面的代码中,它被用来在静态变量之间传递值。