首页
学习
活动
专区
圈层
工具
发布
44 篇文章
1
[PyUserInput]模拟鼠标和键盘模拟
2
银行排队模拟(离散事件模拟)
3
Linux网络模拟
4
Linux TC(Traffic Control)作为损伤仪的基础配置和使用
5
深入学习Docker网络(看这篇就完全够了)
6
【鸿蒙 HarmonyOS】鸿蒙手机模拟器 ( 鸿蒙远程模拟器 | 鸿蒙远程模拟器运行手机应用 )
7
探索嵌入式应用框架(EAF)
8
多 OS 混合部署框架
9
嵌入式系统架构浅谈:编程设计模式 (一)---访问硬件的设计模式
10
事件驱动和消息驱动
11
原来 8 张图,就能学废 Reactor 和 Proactor
12
Linux df -h 命令hang住没有反应
13
kafka消费组信息采集异常(hang住)排查
14
ext4 io hung模拟脚本
15
解决 umount 命令卸载磁盘时busy/卡死的问题
16
程序卡死在void HardFault_Handler的解决办法
17
执行sed命令卡死CPU消耗100%一例分析
18
记一次因Redis使用不当导致应用卡死过程
19
字节对齐不慎引发的挂死问题
20
解引用NULL为什么会导致程序挂死?
21
记64位地址截断引发的挂死问题
22
websocket 在线工具_websocket添加请求头
23
【嵌入式Linux应用开发】SquareLine Studio与LVGL模拟器
24
详解Handler机制中消息队列的出队逻辑
25
Android UpdateEngine模块流程(含序列图)
26
物联网时代的嵌入式开发平台
27
400+条实用C/C++框架、库、工具整理 ,你能想到的都在这里了
28
ESP32芯片IO解读
29
M5Stack在ubuntu上进行开发编译
30
【抽象那些事】不完整的抽象&多方面抽象&未用的抽象&重复的抽象
31
H264,你不知道的小技巧
32
linux 创建虚拟块设备,制作文件系统并挂载,用于测试lustre
33
基于linux开发uvc摄像头_uvc协议扩展
34
清晰讲解LSB、MSB和大小端模式及网络字节序
35
在树莓派中使用 MicroPython 接入 MQTT
36
MicroPython 玩转硬件系列1:环境搭建
37
嵌入式系统架构浅谈:编程设计模式 (二)---嵌入并发和资源管理的设计模式
38
嵌入式软件架构设计之分层设计
39
IC之路(一)Proteus-Arduino仿真环境搭建
40
图像处理基础(六)-libjpeg常用算法
41
OpenCV双目标定
42
L-K光流推导及OpenCV代码实现
43
NDI Webcam Input工具,那些你不知道的知识!
44
使用QEMU chroot进行固件本地调试
清单首页其它文章详情

解引用NULL为什么会导致程序挂死?

来源:公众号【编程珠玑】

作者:守望先生

ID:shouwangxiansheng

解引用NULL指针为什么会出错,导致程序挂死?或者说访问内存地址为0的位置为什么会视为非法?

先了解NULL

参考《NULL,0,'\0'有何区别?》。

解引用NULL

解释问题之前,先描述问题。请看下面的代码:

代码语言:javascript
复制
#include<stdio.h>
int main(void)
{
    char *p = NULL;
    char c = *p;
    return 0;
}

运行:

代码语言:javascript
复制
Segmentation fault

为什么会出现这样的错误呢?解释这个问题之前,先简单了解以下程序地址空间布局。

程序地址空间布局

本文不深入介绍,而仅简单介绍进程的虚拟地址空间(注意下面提到的都不是实际的物理地址),以便帮助我们理解标题的问题。

程序运行起来后,会映射到一个虚拟地址空间。对于32位程序,它是一个4G的大小(一个32位程序,能用到的内存也不过4G)。

其布局如下:

程序空间地址

  • 内核空间:大小与操作系统有关,对于Linux系统,32位程序的内核空间默认为1G(可调整)。
  • 栈:Linux下默认为8M,可调整。具有自动存储期的变量存储在栈中。
  • 堆:不会超过3G,所以有人问你,一直malloc,最多能申请多少,应该有数了吧?
  • DATA :数据区
  • TEXT:代码区

关于不同类型的变量,其存储区域可以参考《全局变量,静态全局变量,局部变量,静态局部变量》。

当然,这些都不是本文的重点,本文的重点在于0的位置。可以看到,地址为0的位置,既不是在堆中,也不是在栈中,或者说不是一个能正常访问的位置。

问题所在

对于程序来说,它只能访问一些特定的位置,例如堆栈,而诸如内核空间,0等位置是受保护的,不允许程序进行访问,因此一旦程序中尝试访问了这样的地址,就会触发保护机制,最终可能直接让程序退出。

下面的例子也是类似的:

代码语言:javascript
复制
//来源:公众号【编程珠玑】
#include <stdio.h>
int main(void)
{
    char *p = "hello";
    p[0] = 'H';
    return 0;
}

字符串hello存储在了只读数据区,因此尝试修改它就会导致程序崩溃。

代码语言:javascript
复制
$ 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以上地址为内核空间。

总结

不该读的地方别读,不该写的地方不要写,解引用记得做空检查。

今日推荐《我要吹爆这本书!

关注公众号【编程珠玑】,获取更多Linux/C/C++/数据结构与算法/计算机基础/工具等原创技术文章。后台免费获取经典电子书和视频资源

下一篇
举报
领券