首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在已经寻址数组基的指针上应用后减量是否会调用未定义的行为?

在已经寻址数组基的指针上应用后减量是否会调用未定义的行为?
EN

Stack Overflow用户
提问于 2015-05-28 16:52:50
回答 1查看 514关注 0票数 7

在寻找了一个与以下相关的或重复的问题,但没有结果(我只能做边际正义来描述指针-算术和减少后问题的数量与C标记,但只需说“船载”严重不公正的结果集计数),我扔在拳击圈,希望澄清或推荐给一个副本,我逃避。

如果将后减量运算符应用于指针,如下面所示,数组序列的简单反向迭代,下面的代码会调用未定义的行为吗?

代码语言:javascript
复制
#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加法算子

  1. 如果指针操作数和结果都指向同一个数组对象的元素,或者指向数组对象的最后一个元素,则计算值不会产生溢出;否则,行为将未定义。

它与.的紧密耦合关系。

6.5.2.4后缀增量和递减操作符约束

  1. 后缀增量或递减运算符的操作数应具有原子型、限定型或非限定实数或指针类型,并且应为可修改的lvalue。

语义学

  1. 后缀++操作符的结果是操作数的值。副作用是,操作数对象的值增加(也就是说,适当类型的值1被添加到它中)。有关约束、类型和转换以及操作对指针的影响的信息,请参见加法运算符和复合赋值的讨论。在更新操作数存储值的副作用之前,对结果的值计算进行排序。对于不确定顺序的函数调用,后缀++的操作是一个单一的评估。对具有原子类型的对象的后缀++是一个具有memory_order_seq_cst内存顺序语义的读-修改-写入操作。
  2. 后缀--操作符类似于后缀++运算符,只是操作数的值会减少(即相应类型的值1将从中减去)。

正向引用:加法算子(6.5.6),复合赋值(6.5.16.2)。

在post示例中使用后递减运算符的根本原因是避免根据数组的基址计算最终无效的地址值。例如,上面的代码是以下内容的重构:

代码语言:javascript
复制
#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进行比较以终止循环。可能会更糟。它可能是天真的:

代码语言:javascript
复制
    size_t len = strlen(s) - 1;
    char *t = s + len;

如果s是一个零长度字符串,那么它就会被写得一团糟。这个问题的重新分析代码是为了解决所有这些问题。但是..。

我的妄想症可能会影响到我,但如果他们真的想抓住你,那就不是偏执狂。因此,根据标准(这些部分,或者其他部分),原始代码(如果您忘记了它现在的样子)是否确实调用了未定义的行为(滚动到本小说的顶部)?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2015-05-28 17:12:30

我非常肯定,在这种情况下,后衰退的结果确实是不明确的行为。后递减明显地从指向对象开头的指针中减去一个,因此结果不会指向同一个数组的元素,而指针算术的定义(如OP中所引用的6.5.6/8节)是未定义的行为。您从不使用结果指针这一事实与此无关。

怎么回事:

代码语言:javascript
复制
char *t = s + strlen(s);
while (t > s) fputc(*--t, stdout);

有趣但不相关的事实:标准C++库中反向迭代器的实现通常在反向迭代器中持有指向目标元素的指针。这允许正常使用反向迭代器,而不需要包含指向容器的“开始之前的一个”的指针,这将是UB,如上面所示。

票数 7
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/30512669

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档