解引用NULL指针为什么会出错,导致程序挂死?或者说访问内存地址为0的位置为什么会视为非法?
先了解NULL
参考《NULL,0,'\0'有何区别?》。
解引用NULL
解释问题之前,先描述问题。请看下面的代码:
#include
int main(void){
char *p = NULL;
char c = *p;
return 0;
}
运行:
Segmentation fault
为什么会出现这样的错误呢?解释这个问题之前,先简单了解以下程序地址空间布局。
程序地址空间布局
本文不深入介绍,而仅简单介绍进程的虚拟地址空间(注意下面提到的都不是实际的物理地址),以便帮助我们理解标题的问题。
程序运行起来后,会映射到一个虚拟地址空间。对于32位程序,它是一个4G的大小(一个32位程序,能用到的内存也不过4G)。
其布局如下:
程序空间地址
内核空间:大小与操作系统有关,对于Linux系统,32位程序的内核空间默认为1G(可调整)。
栈:Linux下默认为8M,可调整。具有自动存储期的变量存储在栈中。
堆:不会超过3G,所以有人问你,一直malloc,最多能申请多少,应该有数了吧?
DATA :数据区
TEXT:代码区
关于不同类型的变量,其存储区域可以参考《全局变量,静态全局变量,局部变量,静态局部变量》。
当然,这些都不是本文的重点,本文的重点在于0的位置。可以看到,地址为0的位置,既不是在堆中,也不是在栈中,或者说不是一个能正常访问的位置。
问题所在
对于程序来说,它只能访问一些特定的位置,例如堆栈,而诸如内核空间,0等位置是受保护的,不允许程序进行访问,因此一旦程序中尝试访问了这样的地址,就会触发保护机制,最终可能直接让程序退出。
下面的例子也是类似的:
//来源:公众号【编程珠玑】
#include
int main(void){
char *p = "hello";
p[0] = 'H';
return 0;
}
字符串hello存储在了只读数据区,因此尝试修改它就会导致程序崩溃。
$ gcc -o test test.c
$ ./test
Segmentation fault (core dumped)
$ readelf test -x .rodata #查看只读数据段
Hex dump of section '.rodata':
0x00400570 01000200 68656c6c 6f00 ....hello.
总结来说,就是当程序访问了不允许访问,或者使用了错误访问(只读却想写)方式的时候,程序就要受到惩罚了。
所以有时候可以通过地址值来粗略的判断其访问区域是否合法。例如打印的指针地址大于0xc0000000,那么你就知道可能可能访问了内核区域。当然对于64位程序,它不是这样的,64位程序0xffff,8000,0000,0000以上地址为内核空间。
总结
不该读的地方别读,不该写的地方不要写,解引用记得做空检查。
转载自网络