【问题标题】:Memory Referencing Bug Example内存引用错误示例
【发布时间】:2017-12-20 20:38:10
【问题描述】:
typedef struct
{
    int a[2];
    double d;
}struct_t;

double fun(int i)
{
    volatile struct_t s;
    s.d = 3.14;
    s.a[i] = 1073741824;
    return s.d;
}

我在学习有关 CSAPP 的课程时遇到了这个例子。解释如下所示。但我仍然无法弄清楚。

【问题讨论】:

  • 想想 int 的 sizeof,double 的 sizeof,以及在这个例子中保证可写的内存。
  • 您的实际问题是什么?你到底有什么不明白的?
  • double fun(int i) { if (i < 0 || i > 1) {.. handle error ..} ...} 怎么样?
  • 什么让你困惑?解释是混合字节和位。 a[i] 是一个字节,d0, d1... 是位。如果你写一个超出 a[1],你就超出了范围;您正在分配给 d 的空间中写作。如果你在 a[3] 之外写作,那么你是在一个不属于你的结构的空间中写作。
  • 它告诉您,当您超出数组边界时,您不会总是遇到访问冲突。这种情况也发生在基于堆栈的数组上。只有当您超出堆栈框架/分配的内存时,您才会得到它

标签: c++ c cpu-architecture


【解决方案1】:

让我们看看你的结构在内存中的样子。假设 int 是 4 个字节(因为这是最终行为让我们相信的),这里是:

byte 0,1,2,3: a[0]
byte 4,5,6,7: a[1]
byte 8,9,A,B,C,D,E,F: d

fun(1) 创建此结构,将 d 设置为 3.14 (0x40091EB851EB851F),然后将 a[i] 设置为 1073741824 (0x40000000)。没有运行时检查来确保a[i] 指向a[2] 内的某个位置,这可能会导致问题(如此处所示)。 0 和 1 不会改变任何东西,因为两者都将分别写入 a[0]a[1]。但是,如果您写信给a[2],您将与d 占用的空间重叠。通过写入a[2],您替换了d 的前4 个字节。

请注意,您系统上的大小可能不同,例如int 可能是 2 个字节,但问题的主旨是一样的。

最终,当您到达a[4] 时,d 会返回到 3.14。这是因为你已经完全跨过了d。超出此点的堆栈未分配,因此程序以分段错误终止。

TL;DR:您正在写入与您的替身重叠的内存。

(注意 - 这是一个不做什么的例子。这是未定义的行为,并且不能保证给定系统在这种情况下会做什么。)

【讨论】:

  • 从会计的角度来看,您并没有关闭,而是从 C 标准的角度来看,您调用 Undefined Behavior 试图访问定义范围之外的数组元素。
  • 确实如此。 CSAPP 课程正在访问一个越界数组来演示一个点,并且可能不是旨在鼓励导致未定义行为的代码(至少,我希望如此)。
  • 未定义的行为在 stackOverflow 中通常是不受欢迎的。我认为它有点失控了,这是一个完美的教育示例,只要您添加免责声明,即不同的系统可能会表现出不同的行为,并且解释显示在特定情况下发生了什么
  • 您似乎假设 int 在 OP 的示例中是 2 个字节。显然是 4B,因为 fun(4) 根本不影响 double (它写在结构的末尾)。此外,fun(3) 显着影响了该值,而不仅仅是修改尾数中的一些低位。内存布局图也证实了这一点,a[] 占用的空间与d 一样多。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-05
  • 2018-11-03
  • 2017-02-07
  • 1970-01-01
  • 1970-01-01
  • 2019-12-16
相关资源
最近更新 更多