看这个C语言输出2还是3?

科技   2024-12-13 12:01   北京  

今天群里的激烈讨论代码

#include "stdio.h"

#include "stdio.h"

int main()
{
  int i = 0;
  int value = ++i+i++;
  printf("%d\n", value);
  return 0;
}

我们先从正常的逻辑讨论下这个问题

这里需要考虑下i运算符的优先级

然后,并没有什么用处

  • ++ 的优先级高于 +
  • ++ 和 + 的优先级高于 =

那++i 和 i++ 都被执行了一次1 + 2 = 3.

我写了一个脚本,用来转换并执行汇编代码的

# 1125.sh
#gcc -O0 -S 1125.c
as -o 1125.o 1125.s
ld -m elf_x86_64 -dynamic-linker /lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 "1125.o" /usr/lib/x86_64-linux-gnu/crt1.o /usr/lib/x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/5/crtbegin.o /usr/lib/gcc/x86_64-linux-gnu/5/crtend.o /usr/lib/x86_64-linux-gnu/crtn.o -lc -o "1125_64"
sudo chmod 777 1125_64
./1125_64

在生成的汇编代码中,我们可以修改汇编,然后再执行看结果

	.file	"1125.c"
.text
.section .rodata
.LC0:
.string "%d\n"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movl $0, -8(%rbp)
addl $1, -8(%rbp)
movl -8(%rbp), %eax
leal 1(%rax), %edx
movl %edx, -8(%rbp)
movl -8(%rbp), %edx
addl %edx, %eax
movl %eax, -4(%rbp)
movl -4(%rbp), %eax
movl %eax, %esi
movl $.LC0, %edi
movl $0, %eax
call printf
movl $0, %eax
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu 9.4.0-1ubuntu1~16.04) 9.4.0"
.section .note.GNU-stack,"",@progbits

核心代码其实就下面几句

  • i 刚开始在 -8(%rbp) 这个寄存器中
  • -8(%rbp) 寄存器 自加1
  • 再把-8(%rbp) 传给 %eax寄存器
  • %eax 寄存器 自加1
  • 再把两个寄存器的值相加得到结果 3
 subq $16, %rsp
 movl $0-8(%rbp)
 addl $1-8(%rbp) #add i + 1 = 2
 movl -8(%rbp), %eax
 leal 1(%rax), %edx
 movl %edx, -8(%rbp)
 movl -8(%rbp), %edx
 addl %edx, %eax   #add 1 + 2 = 3
 movl %eax, -4(%rbp)
 movl -4(%rbp), %eax
 movl %eax, %esi
 movl $.LC0, %edi

如果我们把这部分修改成这样再执行呢?

 movl $0-8(%rbp)
 movl -8(%rbp), %eax
 leal 1(%rax), %edx
 movl %edx, -8(%rbp)
 movl -8(%rbp), %edx
 addl %edx, %eax   #add 1 + 2 = 3
 movl %eax, -4(%rbp)
 movl -4(%rbp), %eax

结果是

这样调戏汇编代码还是挺有意思的哦。


那如果是这样的C 代码呢?

#include "stdio.h"

int main()
{
  int i = 0;
  int value = i++ + ++i;
  printf("%d\n", value);
  return 0;
}

对应的关键汇编代码

    .cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movl $0, -8(%rbp)
movl -8(%rbp), %eax
leal 1(%rax), %edx
movl %edx, -8(%rbp)
addl $1, -8(%rbp)
movl -8(%rbp), %edx
addl %edx, %eax
movl %eax, -4(%rbp)
movl -4(%rbp), %eax
movl %eax, %esi
movl $.LC0, %edi
movl $0, %eax
call printf
movl $0, %eax
leave

看汇编的意思是,先把 i 自加成1,然后把两个i 相加。




参考

https://zh.cppreference.com/w/c/language/eval_order


END

来源:嵌入式Linux

版权归原作者所有,如有侵权,请联系删除

推荐阅读
一个面向对象的C语言框架!
FreeRTOS 单核、多核的调度策略
一个高效、安全、可靠的串口通讯开源方案

→点关注,不迷路←

嵌入式微处理器
关注嵌入式相关技术和资讯,你想知道的都在这里。
 最新文章