结构内存布局 在 C 语言中

Structure memory layout In C

本文关键字:语言 内存 布局 结构      更新时间:2023-10-16
struct fraction {
    int num;
    int denum;
} pi;
pi.num=22;
pi.denum=7;
((fraction*)&(pi.denum))->num=12;
cout << endl;
cout << pi.denum <<endl;
cout << pi.num   <<endl;

到目前为止,我可以理解内存图我感到困惑的是以下代码

((fraction*)&pi.denum)->denum=33;

有没有合法的方法可以打印出那 33 个?任何命令来获取存储在对象视图中的值?

struct fraction将在内存中作为两个连续ints进行布局。 您的代码查看表达式 &pi.denum ,它是第二个整数的地址:

 -----------------------   
|  int num  | int denum |
 -----------------------
 ^           ^
 |           |
 &pi         &(pi.denum)

但是您将&pi.denum投射到fraction *并尝试访问((fraction*)&(pi.denum))->num. 因为numstruct fraction的第一个成员,C标准保证它的地址与结构本身相同。 所以

&(((fraction*)&(pi.denum))->num) == (fraction*)&(pi.denum) == &pi.denum.

这是一个有效的内存位置 - 但只是运气好。 如果您尝试访问((fraction*)&(pi.denum))->denom,则会得到未定义的行为 - 可能会损坏内存或导致分段错误。

最重要的是,((fraction*)&(pi.denum))->num = 12是无意义的代码。 它永远做不了任何有用的事情。

也许一个简单的图像会有所帮助:

A+0 pi.numA+4 pi.denum

A 是存储在内存中的pi的基址。 num存储在地址 A+0denum存储在地址 A+4(如果int为 32 位,即 4 字节(。

语句的((fraction*)&(pi.denum))部分将使布局如下所示:

A+0 pi.numA+4 pi.denum ((fraction*(&(pi.denum((->numA+8 ((分数*(&(pi.denum((->德努姆

如您所见,内存中存在重叠。这意味着语句((fraction*)&(pi.denum))->num=12;实际上会将pi.num设置为 12。

此外,如您所见,如果您尝试将((fraction*)&(pi.denum))->denum设置为一个值,您现在正在分配给pi结构的内存之外写入,并且可能会覆盖其他变量或其他内容。

首先,如果要打印出 33,则需要使用以下说明:

((fraction*)&pi.denum)->num=33;

现在了解了 C 内存访问机制,结构部分有两个 int 成员。结构的基址指向第一个元素地址。

&pi.denum 现在给出第二个元素地址,将其强制转换为结构指针,以便它在逻辑上将 denum 地址视为 num 地址,但在物理上将其视为 denim 地址。

因此,如果要更改牛仔布值,则必须使用指向牛仔布地址的第一个元素地址。

该行可能会将 pi.denum 设置为 12,但它是未定义的行为。成员 "num" (在大多数情况下(位于结构的开头,因此something.num就像 (int)something 一样(当然,这个something必须是分数(,但您不应该假设它可以在每台机器上工作。

如果这段代码基本上是错误的,也许你正在搜索这个:

#include <iostream>
int main () {
    struct fraction {
        int num;
        int denum;
    } pi;
    pi.num=22;
    pi.denum=7;
    ((fraction*)&(pi.denum))->num=12;
    std::cout << std::endl;
    std::cout << pi.denum <<std::endl;
    std::cout << pi.num   <<std::endl;
    ((fraction*)&pi.denum)->denum=33;
    int * items;
    items = (int *)&pi;
    for (int i = 0; i < 3; i ++) {
        std::cout << i << ": " << items[i] << std::endl;
    }
}

在您的问题中发布的代码中,您已经告诉编译器表现得好像pi.denum的地址实际上是fraction struct的地址。

它不是,因此您靠自己获得正确的内存寻址。 C(或 C++(将按照您指定的顺序在内存中布置您的结构,仅受对齐填充的限制。

因此,在具有 32 位

整数和 32 位整数的典型 32 位对齐方式的计算机上,您的行((fraction*)&(pi.denum))->num=12;pi.denum设置为 12,而不是pi.num->num是指针的偏移量(偏移量为零(,指针指向pi.denum,因此设置的是pi.denum

这不会崩溃,因为您只是使用了一个特殊的表达式来寻址堆栈上正确分配的内存。

下面的代码((fraction*)&pi.denum)->denum=33;将写入分配pi之外的内存。 这是崩溃还是简单地覆盖另一个变量取决于您在堆栈上分配的其他内容,也可能取决于您的编译器和编译器设置。

如果你真的想在结构后面的内存上涂鸦,然后读回它,同样的寻址表达式会为你做,例如

int myTest = ((fraction*)&pi.denum)->denum;