将nullptr赋值给成员指针



谁能告诉我为什么分配给Class::*类型的数据成员指针的nullptr的内部表示为-1对于MSVC, clang和g++?对于64位系统,(size_t)1 << 63将是最好的,因为如果您使用nullptr成员指针,那么您肯定会触及内核内存并导致崩溃,因此这将是一个很好的调试帮助。

-1背后是否有更深层次的原因?

示例:

struct X
{
int x, y;
};
using member_ptr = int X::*;
member_ptr f()
{
return nullptr;
}

…使用g++生成以下二进制文件:

movq    $-1, %rax
ret

~(0LLU)更可取的原因有三个:

  1. 成员指针可以是0到结构体或类的大小之间的任意值。使用~(0LLU)与实际有效的成员指针发生冲突的风险最小。你不能有一个像size_t:

    这样大小的结构体
    <source>:2:21: error: size '9223372036854775808' of array 'x' exceeds maximum object size '9223372036854775807'
    2 |     long long x[1LLU<<63];
    <source>:2:15: error: size of array 'x' exceeds maximum object size '9223372036854775807'
    2 |     long long x[1LLU<<62];
    

    注意限制是(1LLU<<63) - 1。这样就否定了这个论点。在16位系统上可能会有所不同。

  2. 在x86_64上加载0,~(1LLU)1LLU << 63变成

    31 ff                            xor    %edi,%edi
    48 c7 c7 ff ff ff ff             mov    $0xffffffffffffffff,%rdi
    48 bf 00 00 00 00 00 00 00 80    movabs $0x8000000000000000,%rdi
    

    加载0最快。加载1LLU << 63是最长的操作码,这本身就有性能成本。因此,使用~(0LLU)作为成员指针nullptr具有轻微的性能优势。

    在许多体系结构中都是类似的。在Mips64上,最后一个需要一个额外的操作码:https://godbolt.org/z/3nehjcoM6

  3. 从旧C时代开始,函数返回-1~(0LLU)作为错误代码,除非指针使用0。成员指针不能使用0.

我个人认为编译器开发人员只是在遵循老习惯(原因3)。它也更快只是运气(或者那些老C怪知道他们在做什么选择他们的错误代码:)。

关于为什么编译器在优化时不能使用~(0LLU),在调试时不能使用1LLU << 63:你可以把一些翻译单元编译为优化代码,一些编译为调试代码。它们将遵循不兼容的abi,并且不能连接在一起。

相关内容

  • 没有找到相关文章

最新更新