计算算术数组下标表达式作为 C 中条件运算符的操作数的一部分

Evaluation of arithmetic array subscript expression as part of operand to the conditional-operator in C

大家好,为什么这个程序 return 2 而不是 3

当我们做这个算术时'a' <= (s[++i]) && (s[++i]) <= 'z' ?(s[++i]) - 'a' + 'A': (s[++i])

首先我们测试 s[++i] = 1 ,第二个要测试的 s[++i] 应该是 2 并且这个测试失败并且最后一个发生 s[++i] == 3 ,所以为什么编译器 return = 2?

感谢您的回答。

int main()
{
  int i=0; 
  char s[10];

  strcpy(s, "0123");
  putchar(('a' <= (s[++i]) && (s[++i]) <= 'z' ?(s[++i]) - 'a' + 'A': (s[++i])));

  printf("\n");

  return 0;
}

&& 的右侧 && (s[++i]) <= 'z' 未计算,因为左侧已经为假('a' <= (s[++i]) 解析为 'a' <= '1',即假)。

因此,仅评估 && 的左侧以及 : 的右侧。

初始状态:

s = "0123"
i = 0

表达式的执行步骤:

'a' <= (s[++i]) && (s[++i]) <= 'z' ? (s[++i]) - 'a' + 'A' : (s[++i])
// execute ++i -> i = 1
'a' <= s[i] && (s[++i]) <= 'z' ? (s[++i]) - 'a' + 'A' : (s[++i])
'a' <= s[1] && (s[++i]) <= 'z' ? (s[++i]) - 'a' + 'A' : (s[++i])
'a' <=  '1' && (s[++i]) <= 'z' ? (s[++i]) - 'a' + 'A' : (s[++i])
// we look at http://www.asciitable.com/ Note1
 97 <=   49 && (s[++i]) <= 'z' ? (s[++i]) - 'a' + 'A' : (s[++i])
     0      && (s[++i]) <= 'z' ? (s[++i]) - 'a' + 'A' : (s[++i]))
// because of short-circuiting in && operator we do not evaluate right side of &&
// Note `a && b ? c : d` is equal to `(a && b) ? c : d`
// Note `&&` introduces a sequence point
              0                ? (s[++i]) - 'a' + 'A' : (s[++i])
// Note `?` introduces a sequence point
(s[++i])
s[++i]
// execute ++i -> i = 2
s[2]
'2'

注 1:C 标准没有说系统使用 ascii 编码,但当今使用的大多数平台都使用 ascii。

First we test s[++i] = 1 , second going to be tested s[++i] should be 2

不,因为 &&short-circuit evaluated。如果 && 的左边为 false 那么 && 的右边将不会被执行。

首先让我们像这样拆分三元运算符

'a' <= (s[++i]) && (s[++i]) <= 'z' ?
  (s[++i]) - 'a' + 'A':
  (s[++i])

并评估第一个条件 'a' <= (s[++i]) && (s[++i]) <= 'z' 很好 'a' <= ([s++i]) returns 错误 因为 'a'97 大于 s[++i] i0 所以 ++i returns 1s[1]'1'49 所以 97 <= 49 是假的 因此,&& 停在那里,returns 0 (false) 这使得三元运算符 语句执行 else 条件 然后 returns s[++i] 记住 i 已经 当第一个条件是时增加一次 评估这意味着 s[++i]s[2] 这 是 '2' 并且这是应该返回的值

我们来分析一下这个语句:

putchar(('a' <= (s[++i]) && (s[++i]) <= 'z' ?(s[++i]) - 'a' + 'A': (s[++i])));

我们可以先去掉多余的括号:

putchar('a' <= s[++i] && s[++i] <= 'z' ? s[++i] - 'a' + 'A' : s[++i]);

参数表达式是一个三元表达式。让我们将其转换为等效的 if / else 语句:

if ('a' <= s[++i] && s[++i] <= 'z')
    putchar(s[++i] - 'a' + 'A');
else
    putchar(s[++i]);

计算 && 运算符的左侧部分:将 'a's[1] 进行比较,并递增 i。在 ASCII 字符集中,'a' 大于 '1',因此此比较的计算结果为 0,而 i 设置为 1

&& 的右侧部分 未被评估 因为左侧部分为假。

if 语句的 else 分支被评估: s[2] 传递给 putchar() 并且 i 递增并设置为 2.

因此程序输出 2 并且 i 设置为 2

请注意,表达式的不同部分由 &&?: 处的序列点分隔,并且有一个 ++ 运算符递增 i 在每个部分中,因此表达式没有乍看之下的未定义行为。

该代码显示了多次计算其参数的宏会发生什么。例如,可以将 islowertoupper 定义为宏:

// These macros are incorrect and have multiple evaluation of their argument
// do not use these, they are only used to illustrate the problems
#define islower(c)  'a' <= (c) && (c) <= 'z'
#define toupper(c)  (islower(c) ?(c) - 'a' + 'A': (c))

根据这些定义,putchar(toupper(s[++i])); 将扩展为:

putchar(('a' <= (s[++i]) && (s[++i]) <= 'z' ?(s[++i]) - 'a' + 'A': (s[++i])));

有多种副作用,不能正确测试 s[1] 也不能输出 s[1].