段错误(Segmentation Fault)总结
1. 空指针解引用
代码示例:
#include <stdio.h>
int main() {int *ptr = NULL;*ptr = 10; // 触发段错误return 0;
}
原因:
空指针(NULL
)指向内存地址0x0
,该地址通常属于操作系统保护区域,用户程序无权访问。尝试写入此地址会触发段错误。
调试方法:
2. 数组越界
代码示例:
#include <stdio.h>
int main() {int arr[3];for (int i = 0; i <= 3; i++) { // 越界访问i=3arr[i] = i;}return 0;
}
原因:
数组arr
的有效索引为0-2
,访问arr[3]
时超出栈分配的数组边界,可能覆盖其他数据(如函数返回地址),触发段错误。
调试工具:
Valgrind会报告Invalid write of size 4
并指出越界位置。
3. 使用已释放的内存
代码示例:
#include <stdlib.h>
int main() {int *ptr = malloc(sizeof(int));free(ptr);*ptr = 42; // 触发段错误return 0;
}
原因:
free(ptr)
后,ptr
成为“野指针”,指向的内存可能已被系统回收或重新分配,此时写入会访问非法内存区域。
解决方法:
释放后立即置空指针:free(ptr); ptr = NULL;
。
4. 修改字符串常量
代码示例:
#include <stdio.h>
int main() {char *str = "hello"; // 字符串常量存储在只读段str[0] = 'H'; // 触发段错误return 0;
}
原因:
字符串字面值"hello"
存储在代码段的只读内存区域(.rodata
),尝试修改会触发段错误。
正确写法:
使用字符数组替代:char str[] = "hello";
(存储在可写的栈区)。
5. 栈溢出
代码示例:
void recursive_call() {recursive_call(); // 无限递归导致栈溢出
}
int main() {recursive_call();return 0;
}
原因:
无限递归或局部变量过大(如int arr[1000000]
)会耗尽栈空间(默认约8MB),触发段错误或栈溢出信号SIGSEGV
。
调试工具:
GDB的bt
命令可查看调用栈深度;
AddressSanitizer会提示stack-buffer-overflow
。
AddressSanitizer:DEADLYSIGNAL
=================================================================
==17980==ERROR: AddressSanitizer: stack-overflow on address 0x7ffe54cb8ff8 (pc 0x5d9964aa0171 bp 0x7ffe54cb9000 sp 0x7ffe54cb9000 T0)
6. 重复释放内存
代码示例:
#include <stdlib.h>
int main() {int *ptr = malloc(sizeof(int));free(ptr);free(ptr); // 触发段错误return 0;
}
原因:
多次释放同一块内存会导致堆管理器状态混乱,可能触发段错误或未定义行为。
解决方法:
确保每个malloc
对应唯一一次free
,释放后指针置空。
7. 多线程资源竞争
代码示例:
#include <pthread.h>
void *thread_func(void *arg) {// 线程被取消后,内存可能被回收
}
int main() {pthread_t tid;pthread_create(&tid, NULL, thread_func, NULL);pthread_cancel(tid);sleep(1); // 线程资源可能已释放pthread_join(tid, NULL); // 可能触发段错误return 0;
}
原因:
线程取消后,若未正确处理资源回收,尝试访问已释放的线程栈或内存会触发段错误。
解决方法:
使用pthread_tryjoin_np
替代pthread_join
,避免等待已销毁的线程。
调试工具总结
- GDB:通过
gcc -g
编译后,使用run
和backtrace
定位崩溃点。 - Valgrind:检测内存泄漏和非法访问,如
valgrind --tool=memcheck ./a.out
。 - AddressSanitizer:编译时添加
-fsanitize=address
,实时检测内存错误。