1.段错误的定义
段错误是计算机软件运行过程中可能出现的一种特殊错误情况。当程序试图访问不允许访问的内存位置,或试图以不允许的方式访问内存位置(例如尝试写入只读位置,或覆盖部分操作系统)时会发生段错误。分段是操作系统内存管理和保护的一种方法。在大多数情况下,它已经被分页所取代,但是分段的许多术语仍然被使用,“分段错误”就是一个例子。尽管分页被用作主内存管理策略,但有些操作系统在某些逻辑级别上仍然有分段。在类Unix操作系统上,访问无效内存的进程接收SIGSEGV信号。在Microsoft Windows上,访问无效内存的进程会收到状态“访问冲突”异常。段错误的原因(这篇文章非常好)
2.段错误的常见原因
2.1 使用未经初始化及或已经释放的指针地址
- 手动释放地址:
#include <stdio.h>
#include <stdlib.h>
int main() {
int a = 555;
int *str = &a;
*str = 666;
str = NULL;
printf("str ==%d", *str);
system("pause");
return 0;
}
- 被动释放的地址:
地址会被释放,但不一定会崩溃。
#include <stdio.h>
#include <stdlib.h>
char *getStr() {
char str[] = "hello world";
return str;
}
int main() {
char *str;
str = getStr();
printf("str == %s",str);
system("pause");
return 0;
}
- 释放无效指针
#include <stdlib.h>
int main()
{
char *buffer = malloc(20);
free(buffer+2);
return 0;
}
2.2 访问受系统保护的内存地址
#include <stdio.h>
#include <stdlib.h>
int main() {
int *str = (int *)0x20000000;
*str = 555;
system("pause");
return 0;
}
2.3 写入只读的内存地址
#include <stdio.h>
#include <stdlib.h>
int main() {
char *str = "samson";
*str = "cool";
printf("name is %d",str);
system("pause");
return 0;
}
2.3 数组越界
#include <stdio.h>
#include <stdlib.h>
int main() {
int arr[10] = { 0 };
int i = 0;
for (i = 0; i < 100; i++) {
arr[i] = 6;
}
printf("%p",arr);
system("pause");
return 0;
}
2.4 堆栈溢出
#include <stdio.h>
#include <stdlib.h>
int main() {
int i = 0;
for (; i < 10000; i++) {
int arr[999666];
}
system("pause");
return 0;
}
2.5 文件操作符超出限制
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
for (int i = 0; i < 1025; i++) {
int fpr, fpw,res;
int buf[100];
fpr = open(argv[1], O_RDONLY, 0600);
fpw = open(argv[2], O_WRONLY|O_CREAT, 0600);
assert(fpr != -1 && fpw != -1);
memset(buf, 0, sizeof(buf));
while((res = read(fpr, buf, 100)) > 0) {
write(fpw, buf, res);
}
}
return 0;
}
2.6 跨线程传递指针
例一:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
int main() {
int fd_pipe[2];
pid_t pid = fork();
if (pid < 0) {
perror("fork");
}
if (pid == 0) {
sleep(2);
char str[] = "1234455";
write(fd_pipe[1], str, strlen(str));
free(str);
_exit(0);
} else {
fcntl(fd_pipe[0], F_SETFL, 0);
while(1) {
sleep(4);
char *buf;
memset(buf, 0, sizeof(buf));
read(fd_pipe[0], buf, sizeof(buf));
printf("buf is %s", buf);
}
}
return 0;
}
例二:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
void *thread_func(void *buff)
{
sleep(1);
char *buffer = (char *)buff;
buffer[22] = 'a';
return NULL;
}
int main()
{
char *buffer = malloc(20);
pthread_t th;
pthread_create(&th, NULL, thread_func, buffer);
pthread_join(th, NULL);
return 0;
}
2.7 某些有特殊要求的系统调用
例如epool_wait,正常情况下使用close关闭一个套接字后,epool会不再返回这个socket上的事件,但是如果你使用dup或dup2操作,将导致epool无法进行移除操作,此时再进行读写操作肯定是段错误的。