c++ - 将对象地址转换为字符指针,然后对它们使用指针数学

标签 c++ pointers portability

根据 Effective C++,“将对象地址转换为 char* 指针,然后对它们使用指针运算几乎总是会产生未定义的行为。”

对于普通旧数据是否如此?例如在我很久以前写的这个模板函数中打印一个对象的位。它在 x86 上运行出色,但是...它可移植吗?

#include <iostream>

template< typename TYPE >
void PrintBits( TYPE data ) {
    unsigned char *c = reinterpret_cast<unsigned char *>(&data);

    std::size_t i = sizeof(data);
    std::size_t b;

    while ( i>0 ) {
        i--;
        b=8;
        while ( b > 0 ) {
            b--;
            std::cout << ( ( c[i] & (1<<b) ) ? '1' : '0' );
        }
    }
    std::cout << "\n";
}

int main ( void ) {
    unsigned int f = 0xf0f0f0f0;
    PrintBits<unsigned int>( f );
    return 0;
}

最佳答案

它肯定是不可移植的。即使您坚持使用基本类型,也有字节顺序和 sizeof,因此您的函数将在大端机器上或 sizeof(int) 为 16 或 64 的机器上打印不同的结果。另一个问题是并非所有 POD 都是基本类型,结构也可能是 POD。

POD 结构成员可以根据实现定义的对齐规则进行内部填充。因此,如果您传递此 POD 结构:

struct PaddedPOD
{
char c;
int i;
}

您的代码也会打印填充的内容。即使在具有不同编译指示和选项的同一编译器上,该填充也会有所不同。

另一方面,也许这正是您想要的。

因此,它不可移植,但也不是 UB。有一些标准保证:您可以将 POD 复制到 char 或 unsigned char 数组或从中复制,并且通过 char 缓冲区复制的结果将保留原始值。这意味着您可以安全地遍历该数组,因此您的函数是安全的。但是没有人保证这个具有相同类型和值的对象的数组(或对象表示)在不同的计算机上是相同的。

顺便说一句,我在 Effective C++ 中找不到那段话。你会引用它吗?我可以说,如果您的代码的一部分已经包含很多 #ifdef thiscompilerversion,有时完全非标准并使用一些导致未定义行为的 hack 是有意义的,但按预期工作带有此编译指示和选项的编译器版本。从这个意义上说,是的,转换为 char * 通常会导致 UB。

关于c++ - 将对象地址转换为字符指针,然后对它们使用指针数学,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16073839/

相关文章:

javascript - WebAssembly 包括 OpenCV

c++ - 我创建了自己的智能指针类,我用这个智能指针指向一个类实例,我怎样才能正确返回智能指针

c - 为什么我不能使用 "fgets"将字符串读取到我的结构元素中?

c - 如何在不使用临时节点的情况下在单链表中插入新节点?

c++ - 在 Xcode 中使用带有相对路径的 SDL_LoadBMP 有困难

windows - 将非 bat 扩展文件作为批处理文件运行

c++ - 另一个系统上的应用程序在启动时崩溃,没有 sudo 的错误消息,非 sudo 的段错误

c++ - 如何比较等效类型的元组而不考虑类型顺序?

c - C 中的箭头和点运算符

c++ - 可移植 C++ 原子交换(Windows - GNU/Linux - MacOSX)