Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cpp-ub-list 55. 空指针解引用 #52

Open
Mq-b opened this issue Mar 7, 2024 · 15 comments
Open

cpp-ub-list 55. 空指针解引用 #52

Mq-b opened this issue Mar 7, 2024 · 15 comments

Comments

@Mq-b
Copy link
Contributor

Mq-b commented Mar 7, 2024

示例

struct T {
   int i;

   int foo() { return i; }
   int bar() { return 0; }

   static int baz();
};

T* p = nullptr;

p->foo();   // Undefined behavior
p->bar();   // Undefined behavior
p->baz();   // Well-defined, ‘baz’ is a static member

p->baz() 为什么会是良定义?对于内建类型,表达式 E1->E2(*E1).E2 严格等价,任何指针类型都是内建类型。。

这里应该等价为:

(*p).baz();

(*p) 解引用空指针。

@brotherbeer
Copy link
Collaborator

这个问题有一定争议,不过在标准中有这样一段话:

ISO/IEC 14882:2017 12.2.2 Non-static member functions(2)
If a non-static member function of a class X is called for an object that is not of type X, or of a type derived
from X, the behavior is undefined.

这段话将静态成员函数排除在外,言外之意是通过空指针、野指针以及名不符实的对象访问静态成员不是 Undefined,对此一些比较权威的机构也有解释,可以参见:

315. Is call of static member function through null pointer undefined?

232. Is indirection through a null pointer undefined behavior?

等等,就是如果 p 是空指针,*p 又没有被转换为右值,就不应承担作为右值的责任,如:

T* p = nullptr;
*p;
int* q = &(*p);
(*p).static_member();

这些情况都应是合法的。

@Mq-b
Copy link
Contributor Author

Mq-b commented Mar 7, 2024

如果 p 是空指针,*p 又没有被转换为右值

是指?

@brotherbeer
Copy link
Collaborator

就是指针指向对象的值没有被用到,就不算解引用

@Mq-b
Copy link
Contributor Author

Mq-b commented Mar 7, 2024

@Mq-b
Copy link
Contributor Author

Mq-b commented Mar 7, 2024

就是指针指向对象的值没有被用到,就不算解引用

那这太夸张了,岂不是空指针访问非静态成员函数也不是 UB 了?

@Mq-b
Copy link
Contributor Author

Mq-b commented Mar 7, 2024

T* p = nullptr;
*p;
int* q = &(*p);
(*p).static_member();

这些情况都应是合法的。

这个我表示质疑。

@Mq-b
Copy link
Contributor Author

Mq-b commented Mar 7, 2024

https://eel.is/c++draft/expr.unary.op

一元 * 运算符执行间接操作其操作数应为“指向 T 的指针”类型的 prvalue,其中 T 是对象或函数类型运算符产生 TItype 的左值如果操作数指向对象或函数,则结果表示该对象或函数;否则,除非在 [expr.typeid] 中指定,否则行为是未定义的

The unary * operator performs indirection. Its operand shall be a prvalue of type “pointer to T”, where T is an object or function type. The operator yields an lvalue of type T. If the operand points to an object or function, the result denotes that object or function; otherwise, the behavior is undefined except as specified in [expr.typeid].

@Mq-b
Copy link
Contributor Author

Mq-b commented Mar 7, 2024

@frederick-vs-ja

@brotherbeer
Copy link
Collaborator

就是指针指向对象的值没有被用到,就不算解引用

那这太夸张了,岂不是空指针访问非静态成员函数也不是 UB 了?

这确实是不准确的说法,准确的说法在标准中较为分散,我有时间的时候再整理一下,这也确实值得整理

@Mq-b
Copy link
Contributor Author

Mq-b commented Mar 7, 2024

就是指针指向对象的值没有被用到,就不算解引用

那这太夸张了,岂不是空指针访问非静态成员函数也不是 UB 了?

这确实是不准确的说法,准确的说法在标准中较为分散,我有时间的时候再整理一下,这也确实值得整理

OK。

@brotherbeer
Copy link
Collaborator

T* p = nullptr;
*p;
int* q = &(*p);
(*p).static_member();

这些情况都应是合法的。

这个我表示质疑。

空指针解引用出问题的本质原因在“左值转右值”的过程中,读取错误地址上的数据当然会出问题。

如果 p 是空指针,*p 是左值,但如果 *p 没有被转为右值就不会出问题

*p; 作为单独一个语句的时候不转右值,是没问题的

&(*p) 中的 *p 也不会转为右值,在 C 标准中干脆直说了 &(*p) 和 p 等价,&p[n] 与 p + n 等价

*p = 1; 这种的会出问题,但问题不是出现在 *p 上,而是出现在 = 1 上

这些在标准文档中都有说明,不过得细找找了

@SainoNamkho
Copy link

SainoNamkho commented Mar 7, 2024

T* p = nullptr;
*p;
int* q = &(*p);
(*p).static_member();

这些情况都应是合法的。

这个我表示质疑。

空指针解引用出问题的本质原因在“左值转右值”的过程中,读取错误地址上的数据当然会出问题。

如果 p 是空指针,*p 是左值,但如果 *p 没有被转为右值就不会出问题

*p; 作为单独一个语句的时候不转右值,是没问题的

&(*p) 中的 *p 也不会转为右值,在 C 标准中干脆直说了 &(*p) 和 p 等价,&p[n] 与 p + n 等价

*p = 1; 这种的会出问题,但问题不是出现在 *p 上,而是出现在 = 1 上

这些在标准文档中都有说明,不过得细找找了

之前确实有争议,但 https://cplusplus.github.io/CWG/issues/2823.html
目前委员会的态度看起来还是很明确这就是ub。

@frederick-vs-ja
Copy link

这个问题有一定争议,不过在标准中有这样一段话:

ISO/IEC 14882:2017 12.2.2 Non-static member functions(2)
If a non-static member function of a class X is called for an object that is not of type X, or of a type derived
from X, the behavior is undefined.

这段话将静态成员函数排除在外,言外之意是通过空指针、野指针以及名不符实的对象访问静态成员不是 Undefined,对此一些比较权威的机构也有解释,可以参见:

315. Is call of static member function through null pointer undefined?

232. Is indirection through a null pointer undefined behavior?

等等,就是如果 p 是空指针,*p 又没有被转换为右值,就不应承担作为右值的责任,如:

T* p = nullptr;
*p;
int* q = &(*p);
(*p).static_member();

这些情况都应是合法的。

如楼上所说,一个最近的缺陷报告已经明确规定了存在 UB。

@brotherbeer
Copy link
Collaborator

这个问题有一定争议,不过在标准中有这样一段话:

ISO/IEC 14882:2017 12.2.2 Non-static member functions(2)
If a non-static member function of a class X is called for an object that is not of type X, or of a type derived
from X, the behavior is undefined.

这段话将静态成员函数排除在外,言外之意是通过空指针、野指针以及名不符实的对象访问静态成员不是 Undefined,对此一些比较权威的机构也有解释,可以参见:
315. Is call of static member function through null pointer undefined?
232. Is indirection through a null pointer undefined behavior?
等等,就是如果 p 是空指针,*p 又没有被转换为右值,就不应承担作为右值的责任,如:

T* p = nullptr;
*p;
int* q = &(*p);
(*p).static_member();

这些情况都应是合法的。

如楼上所说,一个最近的缺陷报告已经明确规定了存在 UB。

缺陷报告发出来看看呗~

@frederick-vs-ja
Copy link

这个问题有一定争议,不过在标准中有这样一段话:

ISO/IEC 14882:2017 12.2.2 Non-static member functions(2)
If a non-static member function of a class X is called for an object that is not of type X, or of a type derived
from X, the behavior is undefined.

这段话将静态成员函数排除在外,言外之意是通过空指针、野指针以及名不符实的对象访问静态成员不是 Undefined,对此一些比较权威的机构也有解释,可以参见:
315. Is call of static member function through null pointer undefined?
232. Is indirection through a null pointer undefined behavior?
等等,就是如果 p 是空指针,*p 又没有被转换为右值,就不应承担作为右值的责任,如:

T* p = nullptr;
*p;
int* q = &(*p);
(*p).static_member();

这些情况都应是合法的。

如楼上所说,一个最近的缺陷报告已经明确规定了存在 UB。

缺陷报告发出来看看呗~

楼上已经贴出来了

https://cplusplus.github.io/CWG/issues/2823.html

[Accepted as a DR at the November, 2023 meeting.]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants