简体   繁体   English

获取未初始化指针的地址是未定义的行为吗?

[英]Is it undefined behavior to take the address of an uninitialized pointer?

N1570 states that this is undefined behavior: N1570声明这是未定义的行为:

§J.2/1 The value of an object with automatic storage duration is used while it is indeterminate (6.2.4, 6.7.9, 6.8). §J.2/ 1具有自动存储持续时间的对象的值在不确定时使用(6.2.4,6.7.9,6.8)。

And in this case, our pointer has an indeterminate value: 在这种情况下,我们的指针具有不确定的值:

§6.7.9/10 If an object that has automatic storage duration is not initialized explicitly, its value is indeterminate. §6.7.9/ 10如果没有明确初始化具有自动存储持续时间的对象,则其值是不确定的。 If an object that has static or thread storage duration is not initialized explicitly, then: 如果未显式初始化具有静态或线程存储持续时间的对象,则:

— if it has pointer type, it is initialized to a null pointer; - 如果它有指针类型,则将其初始化为空指针;

I then presume the following test program exhibits undefined behavior: 然后,我假设以下测试程序显示未定义的行为:

#include <stdio.h>

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

My motivating concern is the strtol function. 我的动机是strtol功能。 First, let me quote the parts of N1570 related to the endptr parameter: 首先,让我引用与endptr参数相关的endptr

§7.22.1.4/5 If the subject sequence has the expected form and the value of base is zero, the sequence of characters starting with the first digit is interpreted as an integer constant according to the rules of 6.4.4.1. §7.22.1.4/ 5如果主题序列具有预期形式且base的值为零,则根据6.4.4.1的规则将以第一个数字开头的字符序列解释为整数常量。 [...] A pointer to the final string is stored in the object pointed to by endptr , provided that endptr is not a null pointer. [...]指向最终字符串的指针存储在endptr指向的对象中,前提是endptr不是空指针。

§7.22.1.4/7 If the subject sequence is empty or does not have the expected form, no conversion is performed; §7.22.1.4/ 7如果主题序列为空或者没有预期的形式,则不进行转换; the value of nptr is stored in the object pointed to by endptr , provided that endptr is not a null pointer. 如果endptr不是空指针,则nptr的值存储在endptr指向的对象中。

This implies that endptr needs to be pointing to an object, and also that endptr is at some point dereferenced. 这意味着endptr需要指向一个对象,并且该endptr在某个时候被解除引用。 For example, this implementation does so : 例如, 此实现是这样做的

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

Yet, this highly upvoted answer as well as this man page both show endptr being passed to strtol uninitialized. 然而, 这个高度赞成的答案以及这个手册页都显示endptr被传递给strtol未初始化。 Is there an exception which makes this not undefined behavior? 是否有一个例外,这使得这不是未定义的行为?

The pointer's value and its address are not the same. 指针的值和地址不一样。

void *foo;

That pointer has an undefined value, but the address of foo , ie the value of &foo , must be well-determined (since otherwise we can't access it). 该指针有一个未定义的值,但foo的地址,即&foo的值,必须很好地确定(因为否则我们无法访问它)。

At least that's my intuitive understanding, I didn't dig up the standard now, I just think you're mis-reading it. 至少这是我的直觉理解,我现在没有挖掘标准,我只是认为你误读了它。

When talking about code, the two are sometimes confused ("what's the address of that pointer?" can mean "what's the value of that pointer, what address is it pointing to?") but they are really distinct. 在谈论代码时,两者有时会混淆(“指针的地址是什么?” 可能意味着“指针的值是什么,它指向的是什么地址?”)但它们确实是截然不同的。

In this expression: 在这个表达式中:

&ptr

the address of the & operand, ie, the address of the ptr object is yielded but the ptr object is never evaluated. &操作数的地址,即ptr对象的地址被生成,但ptr对象永远不会被计算。

(C11, 6.3.2.1p2) "Except when it is the operand of the sizeof operator, the unary & operator, the ++ operator, the -- operator, or the left operand of the . operator or an assignment operator, an lvalue that does not have array type is converted to the value stored in the designated object (and is no longer an lvalue); this is called lvalue conversion." (C11,6.3.2.1p2)“除非它是sizeof运算符,一元&运算符,++运算符, - 运算符或。运算符或赋值运算符的左操作数的操作数,否则它是左值没有数组类型的数据被转换为存储在指定对象中的值(并且不再是左值);这称为左值转换。“

No. It's not undefined behaviour. 不,这不是未定义的行为。 Only ptr is uninitialized and has indeterminate value but &ptr has a proper value. 只有ptr是未初始化的并且具有不确定的值,但是&ptr具有适当的值。

What the standard quote on strtol says: strtol上的标准报价是什么:

...If the subject sequence is empty or does not have the expected form, no conversion is performed; ...如果主题序列为空或没有预期的形式,则不执行转换; the value of nptr is stored in the object pointed to by endptr, provided that endptr is not a null pointer. 如果endptr不是空指针,则nptr的值存储在endptr指向的对象中。

That above quote is talks about a call like this: 以上引用是关于这样的调用的讨论:

strtol(str, 0, 10);

The call in the man page and the linked answer are perfectly fine. 手册页中的调用和链接的答案完全没问题。

See this example 看这个例子

char * ptr; 

Since ptr is not pointing to any object, dereferencing it invokes undefined behavior. 由于ptr没有指向任何对象,因此解除引用它会调用未定义的行为。 But when you pass its address to strtol , having syntax 但是当你将其地址传递给strtol ,有了语法

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

in statement 在声明中

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

the endptr parameter of strtol is pointing to object ptr and derefeencing it will not invoke any UB. strtolendptr参数指向对象ptr并且derefeencing它不会调用任何UB。

I only have access to N1256 but I will be surprised if there is any material change. 我只能访问N1256,但如果有任何重大变化,我会感到惊讶。

The most relevant section is "6.5.3.2 Address and indirection operators" 最相关的部分是“6.5.3.2地址和间接操作符”

And in particular paragraph 3 (my emphasis): 特别是第3段(我的重点):

Semantics 语义

3 The unary & operator yields the address of its operand. 3 一元&运算符产生其操作数的地址。 If the operand has type ''type'', the result has type ''pointer to type''. 如果操作数具有类型''type'',则结果具有类型''指向类型''的指针。 If the operand is the result of a unary * operator, neither that operator nor the & operator is evaluated and the result is as if both were omitted, except that the constraints on the operators still apply and the result is not an lvalue. 如果操作数是一元*运算符的结果,则不会对该运算符和&运算符进行求值,结果就好像两者都被省略,除了对运算符的约束仍然适用且结果不是左值。 Similarly, if the operand is the result of a [] operator, neither the & operator nor the unary * that is implied by the [] is evaluated and the result is as if the & operator were removed and the [] operator were changed to a + operator. 类似地,如果操作数是[]运算符的结果,则[]运算符和[]所暗示的一元*都不会被计算,结果就像删除了&运算符并且[]运算符被更改为a +运算符。 Otherwise, the result is a pointer to the object or function designated by its operand. 否则,结果是指向由其操作数指定的对象或函数的指针。

None of the cited paragraphs in the OP apply because as many have pointed out. 正如许多人所指出的那样,OP中引用的段落均未适用。 The value of something and its address are very different. 某物的价值及其地址是非常不同的。

I would argue that the absence of any restriction on & regarding uninitialized values taking their address is permitted (by the absence of prohibition). 我认为,允许(通过没有禁令)对未提出的未初始化值的限制没有任何限制。

NOTE: We all know it's fine but it's very rare to find statements in these standards that explicitly say "Yes you can do so & so." 注意:我们都知道这很好,但很难在这些标准中找到明确说“是的,你可以这样做”的陈述。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM