空指针 - segmentation fault: 栈中的临时变量

10 篇文章 0 订阅
5 篇文章 3 订阅

所谓 空指针,其实就是指内存地址0。这个地址操作系统保留,是不能够被访问的。所以,尝试访问一个空指针,程序就会崩溃。

而大家熟悉的 segmentation fault (段错误,段异常),就是因为访问一个不存在的地址 (或尝试访问操作系统的地址)。

程序就会崩溃,不是说程序运行不下去了。我们常见的Intel x86系列处理器在遇到这类错误时候回产生一个中断,而操作系统收到这个中断以后,就知道,现在运行的程序出现了错误,把他kill (杀死,结束)掉吧。

Windows系列系统在遇到这类问题,会显示如下图的一个对话框。而Linux系统则更加直白,直接在屏幕上显示segmentation fault,然后就没有然后了。

Windows中的segmentation fault

其实今天这个主题和空指针的关系不算特别特别的大,但是也是非常大的(笑)。大家可以看下面这个简短而有趣的小程序:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct student {
	char *name;
	int class;
	int grade;
};


void get_student(struct student *s);


int main(void) {
	struct student s;

	get_student(&s);

	printf("student name: %s, class %d grade %d. \n", 
		s.name, s.class, s.grade);

	return 0;
}


void get_student(struct student *s) {
	struct student students[16];
	char name[9] = "student A";
	int i;

	for (i = 0; i < 16; i++) {
		students[i].name = malloc(sizeof(char) * 9);
		strcpy(students[i].name, name);
		
		students[i].grade = 6;
		students[i].class = 2; 

		/* update student name */

		name[8]++;

		printf("new student name: %s\n", name);
	}

	s = students + i;

	return;
}

(当然,大家可能一眼就能看出错误来…) 这是笔者曾经犯过的一个错误 (在Cunix的文件系统中出现, https://gitee.com/pengruiyang-cpu/lessons/blob/master/0x00000002/code/cfs.chttps://github.com/pengruiyang-cpu/lessons/blob/master/0x00000002/code/cfs.c,read_inode函数),当时甚至费了大功夫从汇编语言分析,最终错误原因把笔者一顿气。

到底是什么错误呢?大家可以先尝试编译运行上边的代码,在笔者的想象中,这段代码应该将main函数中的s变量设为get_value函数中的变量students的最后一个元素 (大吸气) ,可是运行结果却又是一个诡异的结果。

在这里插入图片描述

结果竟然是空?嗯,原因笔者是知道的,请听我细细道来…

类似main函数中s这样在函数结束后就没有用了的变量,编译器会将他们放在栈中。栈就是一个典型的先进后出的缓冲区,他的顶端地址由一个寄存器sp保存。sp的地址便是上一个进栈的元素的顶地址 (我敢打赌你没听懂)。

在这里插入图片描述

那么,上一个进栈元素的地址就是sp - 这个元素的大小,这个这个元素的大小一般为一个固定值,32位为2字节,64位为8字节,就是他们的最大字节数(或说成"字的大小")。

因为栈这玩意儿是固定大小的,所以,我们要想一直将他用下去,就得手动的(对于编译器来讲)将他们收回。只需要将sp寄存器的值更改一下就可以了。

在这里插入图片描述

当函数执行完毕后,编译器就会自动的回收栈中使用的部分(平衡栈)。所以,所有在栈中的变量都是临时的,只要函数一结束,他们的姓命也就结束了。

我们再来看代码。在函数get_student中,students是一个临时的变量。而函数的最后将参数s赋值为它(s = students + i),这样,赋值到的只是地址,而不是他的值。在函数get_student返回后,s保存的内存地址是在栈中,这导致它获得了一个诡异的结果

错误的重现

知道了错误原因,解决办法就简单了。我们可以为get_student函数中的students申请一块不在栈中的内存,也可以将students中的内容直接复制到s里。第二种方法可以不用多申请内存,而第一种方法比较简单。具体的决定权在你手里头。

通过这次错误报告,我们知道了:

在栈中的临时变量不可以作为全局或看做是申请过的内存,其值是易失的。所以,我们必须采用申请或复制的方法来确保该值不会随着函数的返回,栈的回收而失去。

当然,更重要的是:

作为一个学C语言的,怎么可以不懂点汇编呢?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值