在定义明确的c ++中,从引用中获取的指针是否可以为null?

时间:2019-06-25 14:50:34

标签: c++ pointers reference language-lawyer null-pointer

此问题是由clang和gcc针对nullptr检查指针值的处理不当引起的。对于this,它们都发出警告,但是对于在对象上使用address-of运算符获取的指针,它们保持安静。

我非常确定这样的指针应该一直有效,因为现代编译器从快乐的90年代实际上取消了对c ++代码的检查以来,我们已经遇到了错误。

令我感到困惑的是,为什么编译器在一般情况下保持安静。 if是否有可能被触发,或者仅仅是两个主要编译器中的设计决定?在我开始编写补丁或调试编译器开发人员之前,我想确定一下我没有错过什么。

Toy example

#include <iostream>
class A {
    void f(){
        if(!this) {
            std::cout << "This can't trigger, and compilers warn about it.";
        }
    }
};

void f(A& a){
    A* ptr = &a;
    if(ptr == nullptr) {
        std::cout << "Can this trigger? Because gcc and clang are silent.";
    }
}

尽管这个问题看起来很愚蠢,但我发现它很实用。如果确实使用臭代码,则此优化将导致致命的结果,因此警告将是非常有用的诊断。

补充情况。 clang和gcc都知道检查具有不变的评估能力,因为即使对于干净的代码:

void g(A* a){
    A* ptr = a;
    if(ptr == nullptr) {
        std::cout << "Gee, can this trigger? Be cause gcc and clang are silent.";
    }
}

void g(A& a) {
    g(&a);
}

它们生成g的两个版本,if中省略了g(A& a),因此两者都可以确定并假定不可为空,以供参考。 gcc生成易于阅读的汇编:

f(A&):
        ret
.LC0:
        .string "Can this trigger? Be cause gcc and clang are silent."
g(A*):
        test    rdi, rdi
        je      .L5
        ret
.L5:
        mov     edx, 52
        mov     esi, OFFSET FLAT:.LC0
        mov     edi, OFFSET FLAT:_ZSt4cout
        jmp     std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
g(A&):
        ret

据我了解,程序集msvc /O2icc -fast将检查留在原处。

编辑:我错过了!中的A::f(),已将其修复。

1 个答案:

答案 0 :(得分:5)

  

从引用中获取的指针在定义良好的c ++中是否可以为null?

不。此答案中的标准引号:Is null reference possible?

尽管如此,在特定情况下,使用重载的类类型的operator&获取指针可以返回任何内容,包括null。

  

if是否有可能触发?

不在A::f::f中。可以在g(A*)中触发,但不能从g(A&)调用时触发。

  

警告将是非常有用的诊断。

如您所见,GCC和Clang不够聪明,无法在那种情况下检测到错误,但是它们确实可以检测到同一错误的简单版本:

  

海湾合作委员会

warning: the compiler can assume that the address of 'a' will never be NULL [-Waddress]
     if(&a == nullptr) {
        ~~~^~~~~~~~~~
warning: nonnull argument 'a' compared to NULL [-Wnonnull-compare]
     if(&a == nullptr) {
     ^~
     

C

warning: reference cannot be bound to dereferenced null pointer in well-defined C++ code; comparison may be assumed to always evaluate to false [-Wtautological-undefined-compare]
   if(&a == nullptr) {