是否未定义行为来读取和比较POD类型的填充字节?

Vit*_*meo 12 c++ padding undefined-behavior language-lawyer standard-layout

今天我遇到了一些大致类似于以下代码段的代码.二者valgrindUndefinedBehaviorSanitizer检测读取未初始化的数据.

template <typename T>
void foo(const T& x)
{
    static_assert(std::is_pod_v<T> && sizeof(T) > 1);
    auto p = reinterpret_cast<const char*>(&x);

    std::size_t i = 1; 
    for(; i < sizeof(T); ++i)
    {
        if(p[i] != p[0]) { break; }
    }

    // ...
}
Run Code Online (Sandbox Code Playgroud)

p[i] != p[0]当传递包含填充字节的对象时,上述工具抱怨比较foo.例:

struct obj { char c; int* i; };
foo(obj{'b', nullptr});
Run Code Online (Sandbox Code Playgroud)

从POD类型中读取填充字节并将它们与其他内容进行比较是不确定的行为?我无法在Standard和StackOverflow中找到明确的答案.

Bat*_*eba 9

您的程序的行为是在两个方面定义的实现:


1)在C++之前14:由于你可能会有1的补码或有符号的幅度signed类型char,你可能会因为比较+0和-0而返回一个令人惊讶的结果.

真正无懈可击的方法是使用const unsigned char*指针.这消除了对现在废除(来自C++ 14)1的补码或带符号幅度的任何担忧char.


由于(i)您自己的存储器中,(ii)您正在服用的指针x,和(iii)一个unsigned char不能包含陷阱表示,(IV) char,unsigned char以及signed char从被免除严格别名规则,关于使用行为const unsigned char*来读取未初始化的内存是完全明确的.


2)但是由于您不知道未初始化的内存中包含的内容,因此读取它的行为未指定,这意味着程序行为是实现定义的,因为char类型不能包含陷阱表示.