C语言send/recv使用出现空指针及Bad Address错误问题求解
问题根因与修复方案
1. recv返回Bad Address的直接原因
客户端代码里的tmp是未初始化的野指针:recv的第二个参数需要指向一块合法、可写、大小足够的内存空间,如果tmp没有提前申请内存(要么用malloc分配对应结构体大小的堆空间,要么直接声明栈结构体实例传地址),内核无法向非法地址写入数据,就会抛出该错误。
2. 服务端if分支的两个致命错误
- 发送长度计算错误:
send(client,usr,sizeof(usr),0)中的usr是指针类型,sizeof(usr)取的是指针本身的长度(32位系统4字节、64位系统8字节),不是你要发送的结构体的实际大小,导致只会发部分结构体数据,内容不全。正确长度应该取sizeof(*usr),即指针指向的结构体的大小。 - 跨进程传递指针的认知误区:服务端的
usr是服务端进程虚拟地址空间里的地址,仅在服务端进程内有效,发到客户端没有任何意义。你需要传递的是usr指向的结构体数据内容,客户端拿到数据后写入自己进程的结构体变量中,而不是用指针直接承接。
修复代码示例
服务端修复
usr = uslist_return(&users,i); if(usr != NULL) { // 发送usr指向的完整结构体数据,长度用结构体本身的大小 send(client, usr, sizeof(*usr), 0); //Some extra code } else { utmp.userid = -1; send(client,&utmp,sizeof(utmp),0); }
客户端修复
// 方法1:直接用栈上的结构体实例,避免野指针问题 struct 你的结构体类型 tmp_obj; // 必须判断recv返回值,不要直接使用接收的数据 ssize_t recv_len = recv(sock, &tmp_obj, sizeof(tmp_obj), 0); if(recv_len < 0) { perror("recv failed"); // 错误处理逻辑 } else if(recv_len != sizeof(tmp_obj)) { // 处理TCP粘包、分片场景,单次接收数据不完整,需要循环接收 } else { if(tmp_obj.userid > 0) { //Some Code } else { //Some other code } } // 方法2:如果必须用指针,提前分配内存 struct 你的结构体类型 *tmp = malloc(sizeof(struct 你的结构体类型)); if(tmp == NULL) { // 内存分配失败处理 } ssize_t recv_len = recv(sock, tmp, sizeof(*tmp), 0); // 后续判断逻辑同上,使用完后记得free(tmp)避免内存泄漏
额外注意事项
- TCP是流协议,存在粘包、分片的可能,单次
recv不一定能收到完整的一个结构体大小的数据,需要实现循环接收逻辑,直到拿到期望长度的数据再解析。 - 跨网络传输结构体要注意字节序、结构体对齐的问题,避免不同架构、不同编译选项的两端解析数据不一致,复杂场景建议先做序列化再传输。
内容的提问来源于stack exchange,提问作者ExhaustedCProgrammer
相关产品推荐
相关产品推荐

