c++ - 内存地址的数字表示与对齐之间的关系?

标签 c++ pointers memory memory-alignment

例子:

std::ptrdiff_t dist(void* a, void* b)
{
    return static_cast<std::uint8_t*>(b) - static_cast<std::uint8_t*>(a);
}

Align8Type align8; // alignof(Align8Type) == 8
std::uintptr_t(&align8) & 3; // [1]
dist(nullptr, &align8) & 3; // [2]
Align8Type* p = reinterpret_cast<Align8Type*>(static_cast<std::uint8_t*>(nullptr) + dist(nullptr, &align8));
assert(&align8 == p); // [3]

假设 std::uint8_t支持 , [1] & [2] 的结果是否保证为 0 并且 [3] 在 c++ 标准中保证为真?
如果不是,在实践中呢?

最佳答案

该标准不保证指针的表示 [注 1]。指针的值不一定直接映射为连续的整数,也不一定指向具有不同对齐方式的类型的指针具有相同的表示。因此,以下任何一种都是可能的:

  • 段/偏移量表示,其中段号占据指针表示的低位。
  • 预对齐表示,其中从表示中删除具有已知对齐方式的对象地址的低阶 0。
  • 标记表示,其中指向某些对象类型的指针的低位用于标识类型的一个方面,并且不参与地址解析。 (一个例子是硬件辅助的垃圾收集架构,其中指向足够大的类型的指针的低位位被重新用作 GC 标志。)
  • 子字寻址表示,其中底层硬件是字寻址的(并且一个字远长于 8 位),但是硬件或软件解决方案可用于字节寻址,其中字节指针由一对字地址/子字偏移组成。在这种情况下,字节指针将大于字指针,这是标准允许的。

  • 我相信还有其他的可能性。
    对齐必须是 2 的幂,但不能保证存在多个对齐。所有类型都有对齐方式 1 是完全可能的。所以很可能在给定的架构上不可能有意义地定义 Align8Type .
    鉴于以上所有,我的解释:
  • std::uintptr_t(&align8) & 3 == 0错误的。即使 Align8Type是可定义的,不能保证Align8Type*的转换至 std::uintptr_t是一个可被 8 整除的数。例如,在 32 位字寻址机器上,底层硬件地址 mod 8 可以是 0、2、4 或 6。
  • dist(nullptr, &align8) & 3 == 0错误的。 nullptr的减法从指向对象的指针是未定义行为。 (第 5.7/5 节:“除非两个指针都指向同一数组对象的元素,或者指向数组对象最后一个元素之后的元素,否则行为未定义。”)
  • reinterpret_cast<Align8Type*>(static_cast<std::uint8_t*>(nullptr) + dist(nullptr, &align8)) == &align8错误的。首先,按照 2.,调用 dist是未定义的行为。其次,将该值添加到空指针是未定义行为。T1*的往返转换至 T2*并返回 T1*只要满足 T2 的对齐要求,就可以保证不如 T1 严格(第 5.2.10/7 节)。在这种情况下,T1Align8TypeT2uint8_t ,并且对齐限制可能成立,因此如果不是由于算术的未定义行为,这将起作用。也就是说,你可以投 &align8uint8_t*然后将其转换回 Align8Type .您甚至可以添加整数 0到中间 uint8_t*指针,但没有其他整数。

  • 这些身份在实践中是否有效?他们可能在 8 位字节寻址的 2 的补码机器上使用 C++ 实现,这很常见(比上面提到的理论野兽更常见,从统计上讲,它们与 unicorn 一样常见)。但从技术上讲,它们使您的代码不可移植。我不知道对第 2 点和第 3 点中提到的 UB 进行哪些积极的优化,所以我不建议在生产代码中冒险。

    笔记:
  • §3.9.2/3:

    The value representation of pointer types is implementation-defined.


    第 5.2.10/4 节:

    A pointer can be explicitly converted to any integral type large enough to hold it. The mapping function is implementation-defined. [ Note: It is intended to be unsurprising to those who know the addressing structure of the underlying machine. —end note ]


    我复制了这个注释,因为它很有趣:为了理解地址作为整数的表示,你必须理解底层机器的寻址结构(这意味着它可能不像连续的整数序列那么简单)。
  • 关于c++ - 内存地址的数字表示与对齐之间的关系?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34737737/

    相关文章:

    c++ - 为什么 std::packaged_task<void()> 无效?

    c - 如何为结构中指针指向的数组元素插入值

    c++ - 如何将某个类的指针方法转换为指针函数?

    c++ - 调试 lambda 内存损坏 ||自动监视 GDB 中的对象指针

    arrays - 使用数组实现图形

    c++ - 深度优先搜索的替代算法

    c++ - constexpr log10 整数函数

    c++ - 在 C++ 中进行复制初始化时实际发生了什么?

    c - 将子进程中创建的有效指针地址通过管道传输到父进程

    c++ - 有没有办法确保数组变量(unsigned int*)在内存中?