段错误(Segmentation Fault)

关于段错误的产生,涉及到计算机体系结构、虚拟内存、TLB等等方面的知识。如果你

  • 刚接触编程,并且在此之前对计算机构造一无所知,我会尝试用最简单的语言把发生段错误的过程描述出来。

  • 是一名OIer\极客\硬件爱好者,我建议直接阅读参考文档《段错误——关于计算机内存布局》或者书籍《深入理解计算机系统》,这会让你对计算机系统构造有基本的认识。(这本书是某专业大二整年需要学习的)遇到不懂的问题请STFW(Search The Fucking Web),或者向助教提问。(提问前请先阅读提问的智慧这篇文章)

一、程序怎么运行在计算机中?

我们写C++程序时会有代码逻辑,并且每一行代码都是有顺序的(如果调换两行代码的顺序可能会导致程序出错)。这样,我们可以简单的认为程序在计算机中运行时是一行一行往下执行的(当然实际会有各种跳转指令,比如while循环时,执行到最后一行时会返回代码块中的第一行)。经过编译后的代码(机器码),自然也是逐行执行。

好,现在我们知道程序在计算机中一行一行的顺序执行。既然是顺序执行,那只需要把代码放到一个计算机能够识别的地方,让计算机逐行执行就可以了。这个地方就叫做内存(不是存储单元,是内存)。计算机会根据指示器状态逐条读取内存中相应位置的指令并执行。

二、内存访问与段错误

为了保护系统,自然的计算机会限制对内存的访问。如果内存中一块空间没有被代码定义,或者程序没有权限访问这块内存空间,计算机就会出现错误。段错误其实就是类似于这样的一种内存访问错误。

之所以称为段错误,是因为在以前我们将程序加载到内存中时,是分段加载的。以下面这个顺序查找的程序为例。

int main(){
  int  k, x;
  int array[] = {2, 3, 1, 7, 5, 8, 9, 0, 4, 6};
  cout << "请输入要查找的元素值:"; 
  cin >> x;
  for (k = 0; k < 10; ++k){
      if(x == array[k]) {
          cout << k;
          break;
      }
      if(k == 10) 
          cout << "not found";
  }
  return 0;
}

整个程序段int main(){... return 0;}刻画了整个代码的运行逻辑,放入内存中时会加载在代码段中。但我们也知道这段代码需要额外的存储空间,因为

int  k, x;
int array[] = {2, 3, 1, 7, 5, 8, 9, 0, 4, 6};

这些涉及到变量定义的语句,都需要内存中的额外空间来存放数据。这些空间显然不能与代码段放在一起(他们不能被计算机执行)。因此计算机重新命名了一个段可以叫变量段(实际是堆栈段,stack、heap)专门用来存放代码中使用变量的空间。这样的分段方法可以让计算机清楚的了解内存中各个段的作用,因此被一直使用。也因此当数组越界时,可能会因为访问超出本段内存范围而产生段错误。


现代计算机的内存访问方式一般使用分页机制,分段机制可能会更多的用在嵌入式设备中。(分段可能已经是历史包袱了?)

当然介绍段错误是想提醒同学们,写代码时一定要注意数组的使用范围。现在很多编译器中数组越界调用不会有警告和报错,这可能会导致你发现不了代码中出现的越界问题,因此使用数组时请不断检查数组的使用是否在自己掌控中。