【问题标题】:Is it undefined behavior to take the address of an uninitialized pointer?获取未初始化指针的地址是未定义的行为吗?
【发布时间】:2015-04-19 15:43:40
【问题描述】:

N1570 声明这是未定义的行为:

§J.2/1 使用具有自动存储持续时间的对象的值 虽然它是不确定的(6.2.4、6.7.9、6.8)。

在这种情况下,我们的指针有一个不确定的值:

§6.7.9/10 如果具有自动存储持续时间的对象不是 显式初始化,它的值是不确定的。如果一个对象 具有静态或线程存储持续时间未显式初始化, 那么:

——如果是指针类型,则初始化为空指针;

然后我假设以下测试程序表现出未定义的行为:

#include <stdio.h>

int main(void) {
    char * ptr;
    printf("%p", (void*)&ptr);
}

我最关心的是strtol 函数。首先引用N1570中与endptr参数相关的部分:

§7.22.1.4/5 如果主题序列具有预期的形式并且 base 的值为零,字符序列以 第一个数字被解释为一个整数常量,根据 6.4.4.1 的规则。 [...]指向最终字符串的指针存储在 endptr 指向的对象,前提是 endptr 不为空 指针。

§7.22.1.4/7 如果主题序列为空或没有 预期形式,不进行转换; nptr 的值为 存储在endptr指向的对象中,前提是endptr是 不是空指针。

这意味着endptr 需要指向一个对象,并且endptr 在某些时候被取消引用。例如this implementation does so:

if (endptr != 0)
    *endptr = (char *)(any ? s - 1 : nptr);

然而,this highly upvoted answerthis man page 都显示 endptr 被传递给 strtol 未初始化。是否有例外使这种行为不是未定义的?

【问题讨论】:

  • 您使用的不是指针的值,而是存储指针值的变量的地址。这是明确定义的。

标签: c pointers language-lawyer undefined-behavior


【解决方案1】:

指针的值和它的地址不一样。

void *foo;

那个指针有一个未定义的值,但是foo的地址,即&amp;foo的值,必须是确定好的(否则我们无法访问它)。

至少这是我的直觉理解,我现在没有挖掘标准,我只是觉得你看错了。

在谈论代码时,两者有时会混淆(“那个指针的地址是什么?”可以表示“那个指针的值是什么,它指向什么地址?”)但是它们真的很不同。

【讨论】:

    【解决方案2】:

    在这个表达式中:

    &amp;ptr

    &amp; 操作数的地址,即。例如,ptr 对象的地址已生成,但 ptr 对象从未被评估。

    (C11, 6.3.2.1p2) "除非它是sizeof运算符、一元&运算符、++运算符、--运算符或.运算符或赋值运算符的左操作数,将不具有数组类型的左值转换为存储在指定对象中的值(不再是左值);这称为左值转换。”

    【讨论】:

      【解决方案3】:

      看这个例子

      char * ptr; 
      

      由于ptr 没有指向任何对象,取消引用它会调用未定义的行为。但是当你将它的地址传递给strtol 时,有语法

      long int strtol(const char *nptr, char **endptr, int base);  
      

      在声明中

      long parsed = strtol("11110111", &ptr, 2);   
      

      strtolendptr 参数指向对象 ptr,取消引用它不会调用任何 UB。

      【讨论】:

        【解决方案4】:

        没有。这不是未定义的行为。只有ptr 未初始化并且具有不确定的值,但&amp;ptr 具有正确的值。

        strtol 上的标准报价是什么意思:

        ...如果主题序列为空或不具有预期形式,则不进行转换; nptr的值存储在endptr指向的对象中,前提是endptr不是空指针。

        上面的引用是在谈论这样的电话:

        strtol(str, 0, 10);
        

        手册页中的调用和链接的答案非常好。

        【讨论】:

        • 因为NULL 通常是#defined 为0,并且标准明确允许使用**endptr 调用strtol 作为NULL,你为什么不能这样做strtol(str, 0, 10); ?
        • 我的回答有点误导......这是对的回复......前提是endptr不是空指针。当然,它确实允许@ 987654331@。我会编辑那部分。
        【解决方案5】:

        我只能访问 N1256,但如果有任何实质性变化,我会感到惊讶。

        最相关的部分是“6.5.3.2 地址和间接运算符”

        尤其是第 3 段(我的重点):

        语义

        3 一元 & 运算符产生其操作数的地址。 如果 操作数的类型为''type'',结果的类型为''pointer to type''。如果 操作数是一元 * 运算符的结果,也不是该运算符 也没有评估 & 运算符,结果就像两者都是 省略,除了对运算符的约束仍然适用并且 结果不是左值。同样,如果操作数是结果 [] 运算符,既不是 & 运算符也不是一元 * [] 所暗示的被评估,结果就像 & 运算符 被删除, [] 运算符更改为 + 运算符。 否则,结果是指向对象或函数的指针 由其操作数指定。

        OP 中引用的段落均不适用,因为正如许多人所指出的那样。某物的价值和它的地址是很不一样的。

        我认为,对于获取地址的未初始化值,没有任何限制是允许的(因为没有禁止)。

        注意:我们都知道这很好,但很少在这些标准中找到明确表示“是的,你可以这样做”的声明。

        【讨论】:

          猜你喜欢
          • 2013-08-12
          • 1970-01-01
          • 2020-11-01
          • 1970-01-01
          • 2018-11-05
          • 1970-01-01
          • 2016-01-28
          • 1970-01-01
          • 2012-02-25
          相关资源
          最近更新 更多