【问题标题】:Yet another sequence point query: how does *p++ = getchar() work?另一个序列点查询:*p++ = getchar() 是如何工作的?
【发布时间】:2013-04-29 09:57:32
【问题描述】:

§5.1.2.4.16

示例 7 表达式的分组并不能完全确定它的求值。在以下片段中:

#include <stdio.h>
int sum;
char *p;
/*
...
*/
sum = sum * 10 - '0' + (*p++ = getchar());

表达式语句被分组,就像它被写成一样

sum = (((sum * 10) - '0') + ((*(p++)) = (getchar())));

但是 p 的实际增量可以发生在前一个序列点和下一个序列点( ; )之间的任何时间,并且对 getchar 的调用可以在需要其返回值之前的任何时间发生。

所以基本上我将其理解为 未指定的行为 - *p = getchar(); p++;p++; *p = getchar()。注意;暗示了一个序列点,但整个表达式中没有其他序列点。

所以这个语法是没用的。而且,对于指针赋值,++ 和 -- 几乎是没用的。 对吗?

【问题讨论】:

  • p++ 后增量何时发生并不重要,因为 p 在表达式中只计算一次。
  • (*p++ = getchar()) 中,p++ 只是一个副作用,p 永远不会在同一语句中的第二个位置引用,所以 p++ 不能导致序列点错误。 (除非它指向sum,或标准输入文件缓冲区内的某个位置)
  • p* = getchar()更改为*p = getchar()

标签: c sequence-points


【解决方案1】:

*p = getchar(); p++;p++; p* = getchar() [sic]

其实没有。要么:

*p = getchar(); p++

old_p = p++; *old_p = getchar()

在您的解释中,您可以写信至*p*(p + 1),这是不正确的。在这两种解释中,您都写入p 最初指向的位置,而不管编译器何时决定放置更改p 的指令。如果它决定将它放在写入*p 之前,那么它必须确保它保留旧值以便以后写入 (*old_p)。

后缀--++ 运算符实际上是相当不错的快捷方式。举个例子:

size_t strlen(const char *str)
{
    size_t len = 0;
    while (str[len++]);
    return len - 1;
}

这是strlen 的一个非常简洁的实现,使用后缀++


来自 C11,6.5.2.4.2(强调我的):

后缀++运算符的结果是操作数的值。作为一个副作用, 操作数对象的值递增(即相应类型的值 1 为 添加到它)。参见关于加法运算符和复合赋值的讨论 有关约束、类型和转换以及操作对 指针。 结果的值计算在副作用之前排序 更新操作数的存储值。关于不确定序列 函数调用,postfix++的操作是单次求值。对象上的后缀 ++ 原子类型是具有 memory_order_seq_cst 的读-修改-写操作 内存顺序语义。

【讨论】:

  • 听起来有一些规则是后缀运算符在 表达式 完成后生效。这令人费解。该标准规定“所有副作用必须在下一个序列点之前完成”。但这里似乎要求在 某些事件之后应用 ++。什么活动?
  • 你应该return len-1; 想象一下在"" 作为str 参数的情况下会发生什么。
  • @Vorac:没有这样的规则。当 Shahbaz 告诉你 p++ 的值是 p 的旧值时,请相信他。 p 何时修改无关紧要,因为无论哪种方式,旧值都是相同的,因此无论何时增加 p*p++ 指的是相同的 char
  • “听起来好像有一些规则是后缀运算符在表达式完成后生效”——不,它没有。为什么不试着阅读和理解你得到的答案呢?或者就此而言阅读 C 标准,该标准清楚明确地将 *p++ 中的 p 的值定义为递增之前的值,无论何时递增。不要嘲笑史蒂夫的有效评论。
  • @Steve Jessop,“请相信 Shahbaz”哈哈哈 :) 不错!
【解决方案2】:

词法分组无关紧要。重要的是p++价值

该语句与以下两个语句相同:

sum = sum * 10 - '0' + (*p = getchar());
++p;

【讨论】:

  • 我仍然不明白为什么以下情况没有发生:1) 评估 p++ 2) 评估 getchar() 3) 将返回值分配给现在递增的 p 指针。后缀增量什么时候出现?
  • @Vorac 你刚刚被告知。 when p++ 发生无关紧要...不会影响*pp ...根据定义,该值是, p 在增量发生之前的值。
  • @Vorac: 评估 p++ 给出的值与评估 p 完全相同(只有 p++ 是右值,而 p 是左值)。
【解决方案3】:
sum = (((sum * 10) - '0') + ((*(p++)) = (getchar())));

等价于

*p = getchar();
sum = ( ((sum * 10) - '0') + *p );
p++;

它被减少到 1 行。而且它不是没用的,因为以这种方式编写代码可以减少代码的大小和复杂性

sum = (((sum * 10) - '0') + ((*(++p)) = (getchar())));

等价于

p++;
*p = getchar();
sum = ( ((sum * 10) - '0') + *p );

【讨论】:

  • 当有两个getchar()调用时不能说“等价”
  • 我们不能说它总是正确的。
  • 这两个 cmets 和反对票到底是怎么回事?只有一个 getchar() 调用,我们可以说它们总是等价的,因为 C 标准说它们是等价的。
【解决方案4】:

使用 -- 和 ++ 进行指针赋值实际上并不是没用的。考虑一个要读取 10 个字符并在末尾添加转义字符的循环:

int i, sum;
char * p;
p = (char*) malloc(11 * sizeof(char));

for (i = 0; i < 10; i++){
   sum = sum * 10 - '0' + (*p++ = getchar());
}
*p = '\0';

【讨论】:

  • 什么是扣环?无论如何,这并不能解决 OP 的问题。
  • @JimBalter bucle = 循环(语言问题,抱歉)。在他的帖子的最后,他说:“所以这种语法是没用的。而且,++ 和 -- for 指针赋值几乎没用。对吧?”
  • 为了不丢失 p 以备将来使用,您可以在 *p = '\0'; 之后添加 for(; sum; sum/=10) { p--;} ;-) 对于 (sum%10 == 0) 情况,这将失败 ...
  • 它没有解决 OP 认为它没用的原因......这就是它的全部意义。
猜你喜欢
  • 1970-01-01
  • 2017-12-08
  • 1970-01-01
  • 2018-11-25
  • 1970-01-01
  • 2017-05-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多