【问题标题】:Memory content behind C struct pointersC 结构指针后面的内存内容
【发布时间】:2013-09-08 21:16:02
【问题描述】:

浏览一些旧的 C 代码,我遇到了这个 sn-p。我对幕后发生的事情感到非常困惑。

我对struct指针的用法和可操作性没有完全了解,也不太明白下面的代码是如何存储和访问内存的。

struct x{
int i1;
int i2;
char ch1[8];
char ch2[8];
};

struct y{
long long int f1;
char f2[18];
};

int main(void)
{
struct x * myX;
struct y * myY;
myX = malloc(sizeof(struct x));
myX->i1 = 4096;
myX->i2 = 4096;
strcpy(myX->ch1,"Stephen ");
strcpy(myX->ch2,"Goddard");
myY = (struct y *) myX;
printf("myY->f1 = %d\n", myY->f1);
printf("myY->f2 = %s\n", myY->f2);
}

这个输出

 myY->f1 = 4096
 myY->f2 = Stephen Goddard

转换后,i1 存储到 myY->f1 中,ch1 和 ch2 都存储在 myY->f2 中。我的问题是如何?。演员表后的记忆内容是什么样的?

我知道这与结构的大小和指针指向的位置有关(显然),但是在查看这段代码之后,它确实让我意识到我缺乏对指针的理解。

谢谢

【问题讨论】:

  • 第一个strcpy调用也调用了未定义的行为,因为它覆盖了一个不存在的数组元素,即ch1[8]

标签: c pointers struct


【解决方案1】:

记忆不受演员表影响。强制转换只是将一个对象视为另一个不同类型的对象。如果您愿意,可以将代码想象为具有union { struct x a; struct y b; },然后写入a,但读取b

这是未定义的行为,但碰巧出现的是各种对象相互重叠,因此您可以看到观察到的结果。

第一个strcpy(myX->ch1,"Stephen "); 也是未定义的行为,因为缓冲区对于字符串来说太小了。

【讨论】:

  • 我想我真正的问题是,在演员表之后,myY->f2 现在如何包含 myX->ch1 和 ch2?
  • @ElliotM:y 结构的f2 子对象与x 结构的ch1 子对象位于相同的内存位置;第一个空终止符被第二个strcpy 覆盖(注意存在缓冲区溢出)。
【解决方案2】:

我在这里假设一个 32 位的 intstruct x后面的内存有24个字节,布局如下:

Bit   0000 0000 00111111 11112222
      0123 4567 89012345 67890123
      ----|----|--------|--------
Field  i1   i2     ch1      ch2

这是第一个 strcpy 之后发生的情况:

      ----|----|--------|--------
Field  i1   i2     ch1      ch2
      ----|----|--------|--------
                Stephen \0
                         ^
                         |
                  Zero terminator

这是在第二个strcpy 之后发生的事情:

      ----|----|--------|--------
Field  i1   i2     ch1      ch2
      ----|----|--------|--------
                Stephen  Goddard\0
                                 ^
                                 |
                           Zero terminator

注意第一个 strcpy 的终止零是如何被覆盖以完成字符串的。

这是struct y的布局:

Bit   00000000 001111111111222222
      01234567 890123456789012345
      --------|------------------
Field     f1            f2

当您将struct x 的指针转换为struct y 时,您不会复制任何内容。 struct y的布局与struct x的布局千篇一律,然后通过f2“窗口”获取x.ch1x.ch2组合的内容:

        --------|------------------
Y Field    f1            f2
X Field  i1 | i2     ch1 |    ch2
        --------|------------------
                 Stephen  Goddard\0

就打印4096 而言,这是未定义的行为:您将f1(类型为long long int)传递给printf,位于int 参数的位置,因此printf"将数据重新解释为int,切断上半部分(未定义的行为)。如果您使用适当的格式说明符,您得到的数字是17592186048512

【讨论】:

  • 为什么f1只存储4096而不存储40964096?
  • @ElliotM 实际上,它存储了17592186048512。但是,您的程序告诉printf 这是一个int,所以%d 会删除其他四个字节。
【解决方案3】:

至于内存,它仍然只保存来自struct x 的值,最初是malloced 加上两个指针myXmyY,都指向struct x

然而,ch1f2 从相同的相对内存索引 8 开始(很明显,i1f1 也是如此,索引 0)和 f2 也与 @ 重叠987654331@,所以你得到:

                      i1    i2    ch1               ch2
from x's perspective: [int] [int] [char] [char] ... [char] ...
from y's perspective: [long long] [char] [char] ... [char] ...
                      f1          f2
                      0     4     8      9          16

这是可行的,因为一个long long 与两个ints 一样长,即8 个字节。

【讨论】:

    猜你喜欢
    • 2014-09-25
    • 2021-01-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-02
    • 1970-01-01
    • 1970-01-01
    • 2018-01-29
    相关资源
    最近更新 更多