结构存储器布局在C中

时间:2012-01-16 15:45:56

标签: c++ memory pointers struct

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打印出来? 获取存储的值的任何命令都不在对象的视图中?

6 个答案:

答案 0 :(得分:1)

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是无意义的代码。它永远无法做任何有用的事情。

答案 1 :(得分:1)

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

A+0  pi.num
A+4  pi.denum

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

语句的((fraction*)&(pi.denum))部分将使布局看起来像这样:

A+0  pi.num
A+4  pi.denum   ((fraction*)&(pi.denum))->num
A+8             ((fraction*)&(pi.denum))->denum

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

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

答案 2 :(得分:0)

首先,如果你想要打印33,而不是你需要使用以下说明:

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

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

&amp; pi.denum现在将第二个元素地址转换为结构指针,因此它将denum地址逻辑地视为num地址,但物理上它是牛仔布地址。

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

答案 3 :(得分:0)

该行可能会将pi.denum设置为12,但这是未定义的行为。 成员“num”(在大多数情况下)在结构的开头,所以something.num就像(int)something(当然,这个something必须是一个分数),但你应该不要以为它会在每台机器上运行。

答案 4 :(得分:0)

如果此代码基本上错误,也许您正在搜索:

#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;
    }

}

答案 5 :(得分:0)

在您的问题中发布的代码中,您告诉编译器,好像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;