为何gethostbyname_r返回小端序IP?小端机器下的技术疑问
问题描述
我尝试用gethostbyname_r()获取本机IP地址,按文档说明这个函数应该返回**大端序(网络字节序)**的IP,但实际得到的却是小端序格式。比如本机IP是10.80.0.200时,大端整数应该是173015240(计算方式:(10 * 256 * 256 * 256) + (80 * 256 * 256) + 0 + 200),小端整数是3355463690(计算方式:(200 * 256 * 256 * 256) + 0 + (80 * 256) + 10),但我拿到的始终是小端序的3355463690。
我的C++代码如下:
rc = gethostbyname_r(hostname_.c_str(), &h, buf, sizeof(buf), &result, &local_errno); if (rc) { std::cout << "Failed to get the ip" << std::endl; ipaddr_ = 0; } ipaddr_ = *reinterpret_cast<uint32_t *>(h.h_addr_list[0]);
另外,我的机器是小端序,验证代码:
import sys sys.byteorder # 输出 'little'
我想知道:为什么gethostbyname_r()会返回小端序IP?还是reinterpret_cast转换改变了字节序?我记得reinterpret_cast不会改字节序。
分析与解决
问题不在gethostbyname_r(),也不是reinterpret_cast改了字节序,问题出在直接把网络字节序的字节数组强转成主机字节序的uint32_t。
gethostbyname_r()返回的h.h_addr_list[0]确实是网络字节序(大端)的IP,它指向的4字节数组顺序是[10, 80, 0, 200](对应10.80.0.200)。但你的机器是小端序,CPU读取uint32_t类型时,会默认按照“低位字节在前、高位字节在后”的规则解析内存内容。当你用reinterpret_cast把这段4字节内存直接当成uint32_t读时,就会把最后一个字节(200)作为整数的最高位,第一个字节(10)作为最低位,最终得到小端序的3355463690。
reinterpret_cast只是改变了编译器对这段内存的类型解读,完全不会修改内存里的字节顺序,所以本质是主机字节序和网络字节序的匹配问题。
解决方法很简单,用标准库的字节序转换函数把网络字节序转成主机字节序:
ipaddr_ = ntohl(*reinterpret_cast<uint32_t *>(h.h_addr_list[0]));
ntohl()的作用就是把32位的网络字节序数据转换为主机字节序数据,刚好适配你的场景。
如果担心内存对齐问题(比如某些平台不允许非对齐的指针转换),也可以手动拼接字节:
uint8_t* ip_bytes = reinterpret_cast<uint8_t*>(h.h_addr_list[0]); ipaddr_ = (static_cast<uint32_t>(ip_bytes[0]) << 24) | (static_cast<uint32_t>(ip_bytes[1]) << 16) | (static_cast<uint32_t>(ip_bytes[2]) << 8) | static_cast<uint32_t>(ip_bytes[3]);
这种方式更稳妥,完全规避了对齐风险,结果也和ntohl()一致。
内容的提问来源于stack exchange,提问作者kadina

