【问题标题】:What happens when you dereference a postincrement C当您取消引用后增量 C 时会发生什么
【发布时间】:2016-06-14 11:52:56
【问题描述】:

我收到了很多关于此的相互矛盾的答案。但正如我一直理解的那样。

当我们在 C 语言中有一个指针并在后自增语句中使用它时,后自增总是在代码行解析之后发生。

int array[6] = {0,1,2,3,4,5};
int* p = array;
printf("%d", *p++); // This will output 0 then increment pointer to 1

输出:

0

非常简单的东西。现在,在人们告诉我的信息和我自己的经历中,我感到有些不和谐。

// Same code as Before
int array[0] = {0,1,2,3,4,5};
int* p = array;
printf("%d", *(p++)); // Issue with this line

输出:

0

现在,当我运行代码的第二个版本时,结果是它将输出 0 然后增加指针。括号暗示的操作顺序似乎被违反了。但是,该站点上的其他一些答案告诉我,应该发生的正确事情是增量应该在取消引用之前发生。所以我想我的问题是:我的理解正确吗?后增量语句是否总是在行尾执行?

附加信息:

我正在使用 gcc 版本 ubuntu 4.8.4 在 linux mint 上使用 gcc 进行编译

我还在 debian 4.7.2 版本的 gcc 上对此进行了测试

【问题讨论】:

  • 括号隐含的运算顺序 - () 是括号,不是括号。我认为这就是您从上下文中所指的内容。正确的术语对于良好的沟通至关重要。
  • 这真的编译没有错误吗?您的数组初始化不正确,您的注释斜线错误。
  • 我已经对帖子进行了明显的更正。我还应该修复数组声明吗?
  • i = *p++ 等价于i = *p++ 也等价于(i = *p, p = p + 1, i)

标签: c pointers gcc operator-precedence


【解决方案1】:

OP 的“结果是它会输出 0 然后增加指针。”是不正确的。

后缀增量返回指针的值。将此值视为原始指针值的副本。指针递增,不影响副本。

后缀 ++ 运算符的结果是操作数的值。作为副作用,操作数对象的值会增加。 ... C11dr 6.5.2.4 2

然后指针的副本被取消引用并返回0。这就是功能事件序列。

由于递增指针和取消引用该指针副本的副作用不会相互影响,因此首先发生的副作用无关紧要。编译器可以随意优化。

“行尾”不涉及代码。重要的是表达式的结尾。

【讨论】:

  • 重要的是表达式的结尾...不完全适合这个讨论,顺序点才是最重要的。
  • 该死的,很好的答案现在很有意义。所以 ++ 运算符实际上返回了操作数的值。但是操作数的值实际上是递增的。那么,如果是这种情况,是否意味着这样的比较总是错误的? if(i++ == 1 && i == 1);但是如果(i == 1 && i++ == 1);可以返回 true 吗?
  • @user2601592 同意这两种情况。然而,代码如履薄冰。在&&|| 的各个方面操作i 很容易以非预期的方式进行编码。请记住,如果代码难以理解,则很难维护,并且您之后的人(能力较差)可能会修复它。
【解决方案2】:

*p++*(p++) 在含义上没有区别。

这是因为后缀运算符的优先级高于一元运算符。

这两个表达式的意思是“p 增加,其先前的值被取消引用”。

如果你想增加指针所引用的对象,那么你需要通过写(*p)++来覆盖优先级。

没有任何版本的代码可以产生输出,然后增加p。原因是p 在一个参数表达式 中递增,它产生一个传递给printf 的值。在 C 中,序列点出现在函数被调用之前。所以p 的新值必须在printf 执行之前就位。并且在调用printf 之前无法进行输出。

现在,您必须对上述内容持保留态度。由于p 是一个局部变量,因此修改它不是外部可见的效果。如果p 的新值没有在任何地方使用,增量可以完全优化掉。但是假设我们在文件范围内有一个int * volatile p;,并使用它来代替。那么表达式printf("...", *p++) 必须在调用printf 之前增加p

【讨论】:

  • 后缀增量是一元运算符
  • @nicomp 确实,在计算机科学领域,这是真的,你在学校时能专心致志是件好事。但是,在 ISO C 术语中,我们有“一元表达式”和“后缀表达式”。请参阅 C99 6.5.2“后缀运算符”和 6.5.3“一元表达式”。 C 中“一元运算符”的重要意义在于它是一元表达式中可能的运算符之一,扮演着该语法中描述的角色(它出现在左侧)。
  • 感谢您的验证。从现在开始,我将只使用 ISO C 术语。
  • @nincomp 没必要;我的观点只是我没有错。我只是习惯于使用围绕其定义的(有时是特殊的)词汇来讨论 C 语法。
  • @John Bode C11 6.5.3 一元运算符列出了 unary-expressionunary-operator。第一个包括p++ 中使用的postfix-expressions,第二个包括- * & 等。将 ++ 称为 一元运算符 似乎是合理且 C-ish,因为它列在该名称的规范部分下。
【解决方案3】:

表达式p++ 有一个resultp 在增量之前的值)和一个副作用p 的值更新为指向int 类型的下一个对象)。

后缀++的优先级高于一元*,所以*p++已经被解析为*(p++);你会发现这两种形式的行为没有区别。 IOW,解引用运算符应用于p++result;线

printf("%d", *p++);

大致相当于

printf("%d", *p);
p++;

需要注意的是,p 实际上会在调用 printf1 之前更新。

但是,(*p)++ 有所不同;不是增加指针,而是增加 p 指向的东西。


1。 ++-- 运算符的副作用必须在下一个 序列点 之前应用,在这种特殊情况下,它发生在计算函数参数和调用函数本身之间。

【讨论】:

    【解决方案4】:

    这是我对此的看法。让我们完全忽略 printf 函数,让事情变得更简单。

    如果我们说

    int i;
    int p=0;
    i = p++;
    

    那么 i 将等于 0,因为 p 等于 0 但现在 p 已增加 1;所以现在 i 仍然等于 0,p 等于 1。

    忽略 i 和 p 作为整数的声明,如果我们将其包装为示例中的i = *(p++),则会发生相同的操作,但 i 现在包含 p 指向的值,其值为 0。但是,现在 p 的值增加了 1。

    【讨论】:

    • 您的解释通过混合intint* 类型为p 增加了混乱。为什么不直接说i = *(p++) 等价于(i = *p, p = p + 1, i)
    • @chqrlie 每个解释都应该尽可能简单。
    • 确实!我的适合 1 行评论 ;-)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-21
    • 2016-01-10
    • 2019-01-05
    • 2015-02-24
    • 1970-01-01
    相关资源
    最近更新 更多