【问题标题】:Why does this not give a segmentation violation fault?为什么这不会产生分段违规错误?
【发布时间】:2014-04-10 17:06:39
【问题描述】:
#include <stdio.h>
#include <stdlib.h>

int noOfIntegers = 2;

struct stack {
  int *s;
  int top;
} st;

void push(int item) {
  st.top++;
  st.s[top] = item;
}

int main() {
  st.s = malloc (2 * (sizeof(int)));
  st.top = -1;
  push(1);
  push(2);
  push(3);
  return 0;
}

我不明白为什么这没有给出分段错误,因为我有 2 个 int 的 malloc 空间,并且正在将 3 插入到指针指向的数组中。

【问题讨论】:

  • 这是未定义行为的美妙之处(在您的情况下访问越界元素)。如果你不走运,它可能会按预期工作;如果你幸运的话,它会大声爆炸。
  • 破坏堆比创建 SEGFALT 的可能性更大。如果您在小型内存实现上尝试过它,例如 8051,其中每个字节都经过精心管理,它可能会这样做。但在大多数 32 位以上的地址空间中,分配单元通常以 16 字节(或更大)的单元进行管理,因此超出您要求的几个字节不会造成任何问题。
  • 您认为是什么导致了分段错误?由于您的信念导致您错误地得出您的程序必须产生这样的错误的结论,因此您的信念不正确的可能性很大。因此,一个更好的问题可能是“导致分段错误的原因是什么?”我注意到这个问题可以通过阅读相关的维基百科页面来回答。

标签: c arrays malloc


【解决方案1】:

更新:这个问题是my ATBG column in May 2014 的灵感来源。谢谢你的好问题!


想象一个巨大的雷区,一直延伸到地平线。如果你开车经过矿井,BOOM,你的车就会爆炸。

在那个雷区的中间是未地雷的停车场,每个都有四千个编号的车位。每个停车场都有一个号码。每个空格都有一个数字。有些地段是相邻的,有些则不是。

你打电话给停车场经理并申请八个车位。有人告诉您,您的空间位于地块 100,空间 1234 到 1241。

你开车去停车场,在 1243 空间找到一辆车,把车停在那个地方,然后开着别人的车离开。

为什么你的车没有爆炸?因为你不在雷区。你没有打破规则吗?当然。你偷了别人的车位。不要那样做。他们可能会因为找不到他们的车而生气。但他们不会炸毁你。

然后您将那辆车开到 4003 点,但该地段只有 4000 个点,所以您开车到 4000 点,然后从停车场的右侧开到点右侧三个点的地方4000。原来是 101 号停车场的 3 号停车位。你把偷来的车停在那里,然后偷了之前在那个停车位的车。

为什么你的车没有爆炸?因为你不在雷区。

然后你开车到地段 100 中的 8003 点,那里只有 4000 个点,一路穿过地块 100,一路穿过地块 101,在它的右端,呃,哦,这次你进入雷区。

你的车为什么会爆炸?因为你在雷区。

现在是否清楚为什么写入您不拥有的内存并不能保证分段错误?分段错误意味着“您在一个甚至不是有效页面的内存页面中”。如果您使用了您不应该使用的有效页面的一部分,则操作系统不会知道这一点。

【讨论】:

    【解决方案2】:

    malloc从堆内存中分配空间;你在堆上分配了 2 个ints,写了 3 个ints

    您的程序的内存通常分配在中等大小的块中,以提高内存管理器的效率。

    请求 1 个字节?您可能有 8 个字节。
    请求 7 个字节?您可能有 8 个字节。
    请求 14 个字节?您可能有 16 个字节。
    (或者至少 16 个字节...)

    您只能保证访问您实际请求的内存,但有时您可以在此之外写入几个字节而不会造成问题。只是没有保证过度写入是安全的。您可能会出现段错误,但可能不会!

    【讨论】:

      【解决方案3】:

      如果你幸运的话,它会给你一个段错误或者什么都没有。

      您在一个可以包含其他变量使用的数据的地方写了一些东西。

      编辑: 这不会产生错误,因为您在为程序保留的内存空间中写入,而您的操作系统什么也没说,但如果您继续可能 10 000 次,您将擦除其他程序的数据,操作系统将关闭您的程序以保护其他程序

      【讨论】:

      • 对不起,这就是我的意思。为什么不会出现segfault
      • 它不会发生,因为你又在你的保留内存空间中
      • 只有当您尝试读取/写入未映射到进程虚拟内存空间的地址时,才会发生段错误。继续推送项目,您将到达触发段错误的地址。
      • @user43968 实际上,操作系统从虚拟机为进程分配块。此进程不能擦除另一个进程的数据。因此,这句话:“如果您继续操作 10 000 次,您将擦除其他程序的数据,而操作系统将关闭您的程序以保护其他程序”是不正确的。
      • @chrk:一般不会。仍然有一些应用程序没有虚拟内存,而微控制器甚至没有 MMU。
      猜你喜欢
      • 2011-01-27
      • 2019-02-16
      • 2013-05-01
      • 2013-05-14
      • 1970-01-01
      • 2016-02-08
      • 1970-01-01
      相关资源
      最近更新 更多