在寻找了一个与以下相关的或重复的问题,但没有结果(我只能做边际正义来描述指针-算术和减少后问题的数量与C标记,但只需说“船载”严重不公正的结果集计数),我扔在拳击圈,希望澄清或推荐给一个副本,我逃避。
如果将后减量运算符应用于指针,如下面所示,数组序列的简单反向迭代,下面的代码会调用未定义的行为吗?
#include <stdio.h>
#include <string.h>
int main()
{
char s[] = "some string";
const char *t = s + strlen(s);
while(t-->s)
fputc(*t, stdout);
fputc('\n', stdout);
return 0;
}最近有人向我建议,6.5.6.p8加法运算符与6.5.2.p4、后缀增量和递减运算符一起,在t上指定甚至在包含s的基址调用未定义的行为时执行减后操作,而不管t的结果值(而不是t--表达式结果)是否被计算。我只是想知道是否真的是这样。
标准中引用的部分如下:
6.5.6加法算子
它与.的紧密耦合关系。
6.5.2.4后缀增量和递减操作符约束
语义学
正向引用:加法算子(6.5.6),复合赋值(6.5.16.2)。
在post示例中使用后递减运算符的根本原因是避免根据数组的基址计算最终无效的地址值。例如,上面的代码是以下内容的重构:
#include <stdio.h>
#include <string.h>
int main()
{
char s[] = "some string";
size_t len = strlen(s);
char *t = s + len - 1;
while(t >= s)
{
fputc(*t, stdout);
t = t - 1;
}
fputc('\n', stdout);
}暂时忘了这对于s来说有一个非零长度的字符串,这个通用算法显然有问题(对某些人来说可能不那么清楚)。如果s[]是"",那么t将被分配一个s-1的值,通过其一个过去的地址,它本身并不在s的有效范围内,并且与随后的s进行比较的评估也是没有用的。如果s的长度为非零,这就解决了最初的s-1问题,但只是暂时的,因为最终它仍然依赖于该值(不管它是什么)是否有效,以便与s进行比较以终止循环。可能会更糟。它可能是天真的:
size_t len = strlen(s) - 1;
char *t = s + len;如果s是一个零长度字符串,那么它就会被写得一团糟。这个问题的重新分析代码是为了解决所有这些问题。但是..。
我的妄想症可能会影响到我,但如果他们真的想抓住你,那就不是偏执狂。因此,根据标准(这些部分,或者其他部分),原始代码(如果您忘记了它现在的样子)是否确实调用了未定义的行为(滚动到本小说的顶部)?
发布于 2015-05-28 17:12:30
我非常肯定,在这种情况下,后衰退的结果确实是不明确的行为。后递减明显地从指向对象开头的指针中减去一个,因此结果不会指向同一个数组的元素,而指针算术的定义(如OP中所引用的6.5.6/8节)是未定义的行为。您从不使用结果指针这一事实与此无关。
怎么回事:
char *t = s + strlen(s);
while (t > s) fputc(*--t, stdout);有趣但不相关的事实:标准C++库中反向迭代器的实现通常在反向迭代器中持有指向目标元素的指针。这允许正常使用反向迭代器,而不需要包含指向容器的“开始之前的一个”的指针,这将是UB,如上面所示。
https://stackoverflow.com/questions/30512669
复制相似问题