【问题标题】:Why short circuiting gains priority over increment in C? [duplicate]为什么短路优先于 C 中的增量? [复制]
【发布时间】:2014-06-14 23:00:27
【问题描述】:
void main()
{
  int i=-3, j=2, k=0,m;
  m = ++i && ++j || ++k;
  printf("\n%d %d %d %d",i,j,k,m);
}

这个 sn-p 打印 -2 3 0 1 但为什么不打印 -2 3- 1 1 。 尽管 k 的 ++ 运算符优先于逻辑运算符,但为什么 k 不会因为短路而递增? 更一般地说,当存在逻辑运算符和副作用时如何应用优先级 参与?

【问题讨论】:

  • 停止将“优先级”视为“评估顺序”

标签: c side-effects


【解决方案1】:

短路 这里的意思是如果逻辑AND 运算符&& 的左操作数计算为false,则它不会计算其右操作数。同样,如果逻辑OR 运算符|| 的左操作数计算为true,则不计算其右操作数。这是因为,无论正确的操作数可能评估为什么,结果都不会受到影响。

另外,&& 的优先级高于 || 运算符。

因此,表达式m = ++i && ++j || ++k; 等价于

m = (++i) && (++j || ++k);

左操作数++i 的计算结果为-2,即true(非零)。因此,现在计算第二个表达式(++j || ++k)|| 运算符的左操作数是 ++j,其计算结果为 3,即 true。这意味着|| 的右操作数,即表达式++k 不会被计算。

因此,只有子表达式 ++i++j 被评估。整个表达式的计算结果为1,因为它是true

【讨论】:

  • 不是 && 运算符的优先级更高吗?
  • @muru 是的,&& 的优先级高于 ||
【解决方案2】:

首先,main 函数(除非你使用古老的 Turbo C)应该返回 int

其次,一个体面的编译器(对我来说是clang)抱怨可疑的表达:

clang++ -S -mllvm --x86-asm-syntax=intel ls.cpp 
ls.cpp:5:11: warning: '&&' within '||' [-Wlogical-op-parentheses]
  m = ++i && ++j || ++k;
      ~~~~^~~~~~ ~~
ls.cpp:5:11: note: place parentheses around the '&&' expression to silence this warning
  m = ++i && ++j || ++k;
          ^
      (         )
1 warning generated.

第三,我们看看汇编输出:

    mov dword ptr [rbp - 8], -3       # this is i
    mov dword ptr [rbp - 12], 2       # this is j
    mov dword ptr [rbp - 16], 0       # this is k

    mov eax, dword ptr [rbp - 8]      # this is i++
    add eax, 1
    mov dword ptr [rbp - 8], eax
    cmp eax, 0                        # is i++ == 0?
    je  .LBB0_2
# BB#1:                               # no, i++ is NOT 0
    mov al, 1
    mov ecx, dword ptr [rbp - 12]     # this is j++
    add ecx, 1
    mov dword ptr [rbp - 12], ecx     
    cmp ecx, 0
    mov byte ptr [rbp - 21], al       # store al as a flag somewhere else
                                      # it will be the final result of the operation
    jne .LBB0_3                       # if j++ was NOT zero, go down to LBB0_3


.LBB0_2:                             # Yes, i++ is 0. Did you remark that
                                         # it didn't increment j in this case?
                                         # but it needs to increment k since the first
                                         # part of the OR evaluated to false.
    mov eax, dword ptr [rbp - 16]    # This is k++
    add eax, 1
    mov dword ptr [rbp - 16], eax
    cmp eax, 0                       # is c++ 0?
    setne   cl                       # if yes, set cl as a flag to 1
    mov byte ptr [rbp - 21], cl # 1-byte Spill

    # and here we get if j++ was not zero, 
    # so did you remark that k was not incremented?
.LBB0_3:
    mov al, byte ptr [rbp - 21] # 1-byte Reload
    lea rdi, qword ptr [.L.str]
    and al, 1
    movzx   ecx, al
    mov dword ptr [rbp - 20], ecx

    # there goes the printf    
    mov esi, dword ptr [rbp - 8]
    mov edx, dword ptr [rbp - 12]
    mov ecx, dword ptr [rbp - 16]
    mov r8d, dword ptr [rbp - 20]
    mov al, 0
    call    printf

【讨论】:

  • 你真的认为在不懂基本运算符的人面前倾倒汇编会有帮助吗?
  • 连同解释我认为这是一个很好的开始。但让我们看看未来会带来什么。
猜你喜欢
  • 1970-01-01
  • 2021-06-03
  • 1970-01-01
  • 2012-06-17
  • 2017-07-18
  • 1970-01-01
  • 2012-12-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多