将内存分配给void*时的gcc-calloc行为

gcc calloc behavior when allocating memory to void*

本文关键字:时的 gcc-calloc 行为 void 内存 分配      更新时间:2023-10-16

我知道这不是使用void指针的好方法,但我对这种行为很好奇。

int main()
{
    void* ptr;
    void* next;
    ptr = ::operator new(8);
    next = (void*)((char*)ptr + 1) ;
    *(int*)next = 90;
    *(int*)ptr =100;
    cout << "Ptr = " << ptr<<endl;
    cout << "Ptr-> " << *(int*)ptr<<endl;
    cout << "Next = " << next<<endl;
    cout << "Next-> " << *(int*)next<<endl;
}
---------------
Result :
Ptr = 0x234e010
Ptr-> 100
Next = 0x234e011
Next-> 0
---------------

在64位(x86_64)和32位(x86)平台(Linux和windows)中,当向ptr添加超过4个字节时,90将保留在next指向的位置,但当添加小于4时,

*当*ptr=100将被执行时,next突然变为0,与calloc和malloc 的结果相同

为什么更改ptr点的内容必须更改ptr+1点的内容?当我们超过4字节时,为什么不发生ganna?如果是关于内存对齐,为什么在32位和64位平台上都有相同的行为?

在delphi(windows 32位/Enbarcadero)中也有相同的结果

谢谢你,很抱歉我的英语不好

当您使用X86体系结构时,对齐不会成为问题:处理器可以访问未对齐的内存,即使速度较慢。

英特尔处理器以最低有效字节FIRST存储数据。谷歌上关于endianness的内容可以得到更深入的解释。

vvvvvvvvvvvvvvvv  this is Ptr
+===+===+===+===+===+===+===+===+
|   |100| 0 | 0 | 0 |   |   |   |
+===+===+===+===+===+===+===+===+
    ^^^^^^^^^^^^^^^^ this is next after you say '*next=100'

现在,当您将90存储到*Ptr时,这将更改为

vvvvvvvvvvvvvvvv  this is Ptr
+===+===+===+===+===+===+===+===+
| 90| 0 | 0 | 0 | 0 |   |   |   |
+===+===+===+===+===+===+===+===+
    ^^^^^^^^^^^^^^^^ this is next

所以next变为0,因为它的前3个字节被0覆盖,而最后一个字节无论如何都是0。

int(通常)为4个字节。您只向ptr中添加了一个字节作为next,而不是4。这意味着ptr和next重叠,因此您的100赋值会破坏90的最后一个字节(恰好0)。

您需要:

next = (void*)((char*)ptr + sizeof(int));

实际上,你只需要使用正确类型的指针。

出现的情况是,在每次操作后,八个字节的内存具有以下内容:

*(int*)next = 90; // 90  == 0x00 00 00 5A
--> 0x?? 5A 00 00 00 ?? ?? ??
*(int*)ptr = 100; // 100 == 0x00 00 00 64 
--> 0x64 00 00 00 00 ?? ?? ??

因此,*(int*)next现在等于零。请注意,您不能指望这种行为,因为它取决于底层硬件。

这与分配或void*无关,而是与您对值如何存储在内存中做出假设有关。

您向地址101写入了一个int(系统中占用4个字节的单位),然后又向地址100写入了另一个int,覆盖了第一个值的3。

不幸的是,您使用的系统没有使用假设的尾数/字节排序,因此第一个值的lsb将占用地址104。结果是,第二次写入的四个零字节中的一个使第一个值为零。

这是因为您调用了未定义的行为,使用字符偏移量来位旋转int值。

当int变成8字节时,任何使用自己的指针操作的人都会遇到同样的问题。

顺便说一句,您应该能够从中了解系统数字表示的语序。