C语言向函数传入NULL指针解引用赋值触发段错误原因问询
NULL指针传参触发段错误的原因
核心逻辑基于两个C语言的基础规则:
- C语言所有函数参数均为值传递,传入指针时,函数内部拿到的是指针值的拷贝,而非外部原指针变量本身;修改形参指针的指向,不会影响外部原指针的值。
- NULL是不指向任何合法可访问用户态内存的特殊地址值,对NULL指针做解引用读写属于未定义行为,在通用操作系统上会直接触发内存访问异常,抛出段错误。
可正常运行代码的执行逻辑
- 第一份正常代码:main函数中的指针p直接指向栈上分配的字符变量v,对应的地址是合法可写的栈内存地址。p的值(即v的栈地址)被传入f函数后,f内部的形参p持有该地址的拷贝,解引用该地址写入字符'a',本质是直接修改栈上v的值,全程没有非法内存访问,因此可以正常输出
a。
void f(char *p) { *p = 'a'; } int main() { char v; char *p = &v; f(p); printf("%c", *p); // Output : a return 0; }
- 第二份正常代码:main函数中p初始值为NULL,但f函数内部没有直接解引用传入的NULL地址,而是先通过
calloc申请了1字节合法的可写堆内存,将这块堆内存的地址赋值给f内部的形参p,再向该合法地址写入'a',最后把堆内存地址返回给main中的p。整个流程从未对NULL地址做解引用操作,所有写入都落在合法申请的内存上,因此可以正常输出a。
char *f(char *p) { p = (char*)calloc(1, sizeof(char)); *p = 'a'; return p; } int main() { char *p = NULL; p = f(p); printf("%c", *p); // Output : a return 0; }
问题代码触发段错误的具体原因
两份问题代码的崩溃核心完全一致:传入f函数的指针值为NULL,函数内部没有为指针分配合法可访问的内存,就直接解引用指针做写入操作。
- 问题代码1:main中将p初始化为NULL,将NULL值作为参数传入f函数,f内部直接执行
*p = 'a',相当于向NULL对应的内存地址写入数据。通用操作系统中NULL通常对应地址0,这块区域是内核保留的受保护内存,用户态程序没有读写权限,CPU会直接触发内存访问异常,操作系统向进程发送SIGSEGV信号,最终抛出段错误。
void f(char *p) { *p = 'a'; } int main() { char *p = NULL; f(p); printf("%c", *p); // Output : Segmentation fault (core dumped) return 0; }
- 问题代码2:崩溃逻辑和问题代码1完全一致,仅多了一步将形参p(值仍为NULL)返回给main的操作。崩溃实际发生在f内部执行
*p = 'a'的环节,代码根本不会执行到返回和后续printf步骤。就算跳过写入环节的崩溃,返回给main的p还是NULL,后续printf中解引用*p同样会触发段错误。
char *f(char *p) { *p = 'a'; return p; } int main() { char *p = NULL; p = f(p); printf("%c", *p); // Output : Segmentation fault (core dumped) return 0; }
扩展:如果需要在函数内部为外部传入的NULL指针分配内存,不能只传一级指针,需要传入指针的地址(即二级指针),让函数可以直接修改外部原指针的值,参考写法:
void f(char **p) { *p = (char*)calloc(1, sizeof(char)); **p = 'a'; } int main() { char *p = NULL; f(&p); printf("%c", *p); free(p); // 堆内存使用后需要手动释放避免泄漏 return 0; }
内容的提问来源于stack exchange,提问作者anbocode
相关产品推荐
相关产品推荐

