跨平台获取网络接口ip应优先使用getifaddrs(linux/macos)或getadaptersaddresses(windows),需条件编译;过滤时检查ifa_addr非空、sa_family为af_inet/af_inet6、iff_up标志,并调用freeifaddrs或正确处理缓冲区。

getifaddrs 是最直接可靠的方案
Linux 和 macOS 上,getifaddrs 是获取所有网络接口地址的标准 POSIX 方法,它能同时返回 IPv4、IPv6 地址、掩码、广播地址和接口状态(如是否 up)。Windows 不支持该函数,但可通过 GetAdaptersAddresses 替代——所以跨平台代码必须做条件编译。
常见错误是忽略 AF_INET 和 AF_INET6 的过滤逻辑,导致拿到一堆链路层地址(如 AF_PACKET)或未配置的接口;另一个坑是忘记检查 ifa_addr 是否为 nullptr(某些接口可能无 IP 地址,比如纯桥接或 down 状态的 lo)。
- 调用前确保链接
-lnet(部分旧系统需要) - 遍历结果时用
if (addr->ifa_addr && addr->ifa_addr->sa_family == AF_INET)判断 IPv4 - 用
getnameinfo()将sockaddr转为可读字符串,避免手动解析in_addr
Windows 下必须用 GetAdaptersAddresses 而非 gethostbyname
gethostbyname 只返回本机 hostname 对应的首个 IPv4 地址,完全不可靠:它不枚举所有接口,不支持 IPv6,且受 hosts 文件或 DNS 配置干扰。真实场景中,你得调用 GetAdaptersAddresses 并传入 AF_UNSPEC + GAA_FLAG_INCLUDE_PREFIX 标志。
容易踩的坑包括:忘记初始化 ULONG buffer_size = 0 并先调一次函数获取所需缓冲区大小;忽略 ERROR_BUFFER_OVERFLOW 返回值直接访问空指针;以及没检查 Adapter->OperStatus == IfOperStatusUp 导致拿到禁用网卡的地址。
- 分配内存前务必先调用一次
GetAdaptersAddresses获取buffer_size - IPv4 地址在
FirstUnicastAddress->Address.lpSockAddr中,需判断lpSockAddr->sa_family == AF_INET - 不要依赖
Adapter->FirstIpAddress字段——它已被弃用,只返回第一个 IPv4,且常为空
std::string 转 inet_ntop 需要 const_cast?不,用 reinterpret_cast 更安全
从 sockaddr_in* 提取 IP 字符串时,inet_ntop 要求 const void* 参数。有人写 const_cast<const void>(...)</const> 是错的——sockaddr_in 本身不是 const,强制 const_cast 可能掩盖类型问题。正确做法是 reinterpret_cast<const void>(&addr->sin_addr)</const>。
另一个常见疏漏是目标缓冲区长度:IPv6 地址最长为 45 字符(含终止符),IPv4 为 16 字符。用栈上固定数组(如 char buf[INET6_ADDRSTRLEN])比动态分配更简单可靠。
- IPv4 用
INET_ADDRSTRLEN(16),IPv6 用INET6_ADDRSTRLEN(46) - 调用
inet_ntop(AF_INET, ..., buf, sizeof(buf))后必须检查返回值是否为nullptr - 不要用
inet_ntoa()—— 它返回静态缓冲区,多线程下会覆盖
跨平台封装建议:别硬写宏,用小函数隔离差异
把 Linux/macOS 和 Windows 的逻辑混在同一个函数里,很快就会变成难以维护的 #ifdef 嵌套地狱。更可行的做法是定义统一接口,例如:
struct InterfaceInfo {
std::string name;
std::string ip;
int family; // AF_INET or AF_INET6
};
然后分别实现 get_interfaces_linux() 和 get_interfaces_windows(),主逻辑只调用抽象函数。这样测试、替换、加日志都方便。
真正容易被忽略的是:有些虚拟网卡(如 Docker 的 br-*、WSL2 的 vEthernet)会带大量私有地址,业务逻辑往往需要过滤掉 127.0.0.1、::1、169.254.x.x(link-local)和 fd00::/8(ULA)——这些得在封装层后手动筛,而不是指望系统 API 过滤。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











