如何保存hostent结果 避免重复调用gethostbyname时被覆盖
解决gethostbyname结果被覆盖的问题
问题背景
调用两次gethostbyname时,第二次的结果会覆盖第一次的,导致两次输出相同:
struct hostent* d = gethostbyname("www.google.com"); struct hostent* b = gethostbyname("www.youtube.com"); printf("%s\n",d->h_name); printf("%s\n",b->h_name);
输出:
youtube-ui.l.google.com youtube-ui.l.google.com
直接拷贝hostent结构体的方式无效,因为结构体中的指针仍指向共享的静态缓冲区:
struct hostent d = *(gethostbyname("www.google.com")); struct hostent b = *(gethostbyname("www.youtube.com")); printf("%s\n",d.h_name); // 无输出 printf("%s\n",b.h_name); // 输出youtube-ui.l.google.com
原因分析
gethostbyname的返回值指向静态分配的内存缓冲区,所有调用共享这块区域。每次调用都会覆盖缓冲区里的内容,而hostent结构体中的h_name、h_aliases、h_addr_list都是指向该缓冲区内部的指针。直接拷贝结构体只是复制了这些指针,并没有复制指针指向的实际数据,所以第二次调用后,第一次拷贝的结构体里的指针指向的内容已经被覆盖,甚至可能变成无效地址。
解决方案:深拷贝hostent结构
需要手动实现hostent的深拷贝,把结构体本身以及它指向的所有字符串、地址列表都复制到独立的内存区域,这样每次的结果就不会互相覆盖。
实现深拷贝函数
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <netdb.h> // 深拷贝hostent结构体 struct hostent* copy_hostent(const struct hostent* src) { if (src == NULL) return NULL; // 分配hostent结构体内存 struct hostent* dest = malloc(sizeof(struct hostent)); if (dest == NULL) return NULL; memcpy(dest, src, sizeof(struct hostent)); // 拷贝h_name dest->h_name = strdup(src->h_name); if (dest->h_name == NULL) { free(dest); return NULL; } // 拷贝h_aliases字符串数组 int alias_count = 0; while (src->h_aliases[alias_count] != NULL) alias_count++; dest->h_aliases = malloc((alias_count + 1) * sizeof(char*)); if (dest->h_aliases == NULL) { free(dest->h_name); free(dest); return NULL; } for (int i = 0; i < alias_count; i++) { dest->h_aliases[i] = strdup(src->h_aliases[i]); if (dest->h_aliases[i] == NULL) { // 释放已分配的别名 for (int j = 0; j < i; j++) free(dest->h_aliases[j]); free(dest->h_aliases); free(dest->h_name); free(dest); return NULL; } } dest->h_aliases[alias_count] = NULL; // 拷贝h_addr_list地址列表 int addr_count = 0; while (src->h_addr_list[addr_count] != NULL) addr_count++; dest->h_addr_list = malloc((addr_count + 1) * sizeof(char*)); if (dest->h_addr_list == NULL) { // 释放已分配的别名和name for (int i = 0; dest->h_aliases[i] != NULL; i++) free(dest->h_aliases[i]); free(dest->h_aliases); free(dest->h_name); free(dest); return NULL; } for (int i = 0; i < addr_count; i++) { dest->h_addr_list[i] = malloc(src->h_length); if (dest->h_addr_list[i] == NULL) { // 释放已分配的地址 for (int j = 0; j < i; j++) free(dest->h_addr_list[j]); free(dest->h_addr_list); // 释放别名和name for (int i = 0; dest->h_aliases[i] != NULL; i++) free(dest->h_aliases[i]); free(dest->h_aliases); free(dest->h_name); free(dest); return NULL; } memcpy(dest->h_addr_list[i], src->h_addr_list[i], src->h_length); } dest->h_addr_list[addr_count] = NULL; return dest; } // 释放拷贝后的hostent结构体 void free_hostent(struct hostent* hp) { if (hp == NULL) return; free(hp->h_name); for (int i = 0; hp->h_aliases[i] != NULL; i++) free(hp->h_aliases[i]); free(hp->h_aliases); for (int i = 0; hp->h_addr_list[i] != NULL; i++) free(hp->h_addr_list[i]); free(hp->h_addr_list); free(hp); }
使用示例
int main() { struct hostent* orig_d = gethostbyname("www.google.com"); struct hostent* d = copy_hostent(orig_d); struct hostent* orig_b = gethostbyname("www.youtube.com"); struct hostent* b = copy_hostent(orig_b); if (d != NULL) printf("%s\n", d->h_name); if (b != NULL) printf("%s\n", b->h_name); // 记得释放内存 free_hostent(d); free_hostent(b); return 0; }
替代方案:使用线程安全的gethostbyname_r
如果系统支持,可以使用gethostbyname_r函数,它允许你传入自己的缓冲区来保存结果,避免静态缓冲区的共享问题。示例代码:
#include <stdio.h> #include <netdb.h> #include <string.h> int main() { struct hostent host_d, host_b; char buf_d[1024], buf_b[1024]; struct hostent *orig_d, *orig_b; int herrno_d, herrno_b; gethostbyname_r("www.google.com", &host_d, buf_d, sizeof(buf_d), &orig_d, &herrno_d); gethostbyname_r("www.youtube.com", &host_b, buf_b, sizeof(buf_b), &orig_b, &herrno_b); printf("%s\n", host_d.h_name); printf("%s\n", host_b.h_name); return 0; }
注意:gethostbyname_r的参数在不同系统可能有差异,需要参考对应平台的手册。
内容的提问来源于stack exchange,提问作者user2912091902102109
相关产品推荐
相关产品推荐

