C语言中struct sockaddr_in*指针使用触发段错误的原因
两种写法运行结果差异的根本原因
核心问题是崩溃版本传入函数的是未初始化的野指针,没有指向合法可访问的struct sockaddr_in结构体内存。
崩溃代码的问题
触发段错误的代码逻辑如下:
int main(){ struct sockaddr_in* s; f(s); }
这里的s只是一个指针类型的变量,本身仅占用栈上很小的空间(32位系统通常为4字节,64位系统通常为8字节),作用是存储一块内存的地址。但代码中从来没有给s赋过合法的地址值,它存储的是栈空间残留的随机垃圾值,也就是常说的野指针。
当把这个野指针传入f函数后,函数内部执行in->sin_addr.s_addr = htonl(INADDR_ANY)这类赋值操作时,本质是向那个随机垃圾地址对应的内存位置写入数据。这块内存要么不属于当前进程的合法地址空间,要么是受系统保护的只读区域,会直接触发操作系统的内存保护机制,抛出段错误终止进程。
正常运行代码的逻辑
可正常运行的两种写法本质完全一致,都是给函数传入了指向合法结构体实例的有效指针:
- 第一种写法中
struct sockaddr_in s;直接在栈上分配了一整块大小匹配struct sockaddr_in的内存,s就是真实存在的结构体实例,通过&s取到的是这块合法内存的起始地址,传入函数后,所有对in->xxx的赋值操作都是在读写当前进程有权限访问的栈内存,不会触发访问错误。 - 第二种写法中的指针
p最终被赋值为&s,也就是让p存储了栈上合法结构体实例的地址,此时p不再是野指针,传入函数后自然可以正常工作。
补充说明
不要混淆“定义结构体指针”和“创建结构体实例”两个概念:指针本身只是存储内存地址的容器,必须让它指向一块真实存在、可读写、大小足够容纳目标结构体的内存后,才能通过指针访问结构体成员。除了栈上定义结构体取地址的方式,也可以通过堆内存分配实现同样的效果,示例如下:
int main(){ struct sockaddr_in* s = malloc(sizeof(struct sockaddr_in)); if (s == NULL) { // 处理内存分配失败的逻辑 return 1; } f(s); // 堆内存用完后需要手动释放 free(s); return 0; }
内容的提问来源于stack exchange,提问作者FueledByPizza
相关产品推荐
相关产品推荐

