【问题标题】:casting object addresses to char ptrs then using pointer math on them将对象地址转换为 char ptrs 然后在它们上使用指针数学
【发布时间】:2013-04-18 03:10:11
【问题描述】:

根据 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;
}

【问题讨论】:

  • 我没有花时间完全了解这里的代码,但我知道这可能导致未定义行为(或崩溃)的一个原因是,如果您尝试使用指针算法来处理随机地址为 int,不一定与“字边界”(0、4、8、12、16 等字节)对齐。
  • 好的,现在我确实理解了代码,并且正如我所怀疑的那样,这不会是一个问题,因为你不会再转换回 int 了。我不知道这样做是否还有其他问题。

标签: c++ pointers portability


【解决方案1】:

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

根据实现定义的对齐规则,POD 结构成员可能具有内部填充。所以如果你传递这个 POD 结构:

struct PaddedPOD
{
char c;
int i;
}

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

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

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

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

【讨论】:

  • 这段话可以在 Effective C++ 第 3 版第 119 页的第 27 条“最小化大小写”下找到。
【解决方案2】:

是的,POD 类型总是可以被视为一个字符数组,大小为sizeof (TYPE)。 POD 类型就像对应的 C 类型(这就是它们“普通、旧”的原因)。由于 C 没有函数重载,因此编写“通用”函数来执行诸如将它们写入文件或网络流之类的操作取决于将它们作为 char 数组访问的能力。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-05-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-31
    • 2012-01-24
    • 1970-01-01
    相关资源
    最近更新 更多