最可靠方式是调用getifaddrs()遍历接口,精确匹配接口名(如"eth0")并筛选af_inet地址,用inet_ntop转换,最后调用freeifaddrs释放内存;ioctl(siocgifaddr)因兼容性差、易出错且不支持别名接口而不推荐。

用 getifaddrs() 获取指定接口的 IPv4 地址
Linux 下最可靠、可移植的方式是调用 getifaddrs(),它能遍历所有网络接口及其地址信息,且无需 root 权限。关键是要过滤出目标接口名(如 "eth0")和 AF_INET 类型的地址。
常见错误是忽略地址族判断,把 IPv6 或链路层地址当 IP 用;或者没检查 ifa_addr 是否为 nullptr(某些接口可能无配置 IP)。
- 必须用
ifa->ifa_addr->sa_family == AF_INET确保是 IPv4 - 用
strcmp(ifa->ifa_name, "eth0") == 0精确匹配接口名(注意:不是前缀匹配) - 用
inet_ntop(AF_INET, &sin->sin_addr, buf, sizeof(buf))转换二进制地址为字符串 - 调用后别忘了
freeifaddrs(),否则内存泄漏
为什么不用 ioctl(SIOCGIFADDR)
虽然 ioctl 可以直接查单个接口,但它要求 socket 已打开、接口已 up、且返回结构体字段易被误读(比如把广播地址或 netmask 当成 IP)。更麻烦的是,SIOCGIFADDR 在某些内核版本或容器环境中会失败并返回 EINVAL 或 ENODEV,而 getifaddrs() 更健壮。
典型错误现象:ioctl(fd, SIOCGIFADDR, &ifr) 成功返回但 ifr.ifr_addr.sa_data 里全是乱码——这是因为没正确 cast 到 struct sockaddr_in* 并提取 sin_addr 字段。
Linux 性能分析与调优专家,覆盖 CPU、内存、磁盘 I/O、网络、内核参数、编译优化、容器/K8s。适用场景:系统卡顿/高负载、内存不足/OOM/Swap 高、CPU 异常/iowait 高。
-
SIOCGIFADDR只返回主 IP,不支持别名接口(如eth0:0) - 需要先
socket(AF_INET, SOCK_DGRAM, 0),fd 泄漏风险更高 - 在 network namespace 或 pod 内可能因权限/虚拟设备限制而失败
处理 IPv6 和多地址场景
一个接口常有多个 IPv6 地址(link-local、global、temporary),getifaddrs() 全部返回,需额外过滤。如果只要第一个可用 IPv4,循环中找到即 break;如果要全部,得收集到 vector 中再筛选。
容易忽略的是:IPv6 地址可能处于 tentative 状态(DAD 检测中),此时虽已分配但尚未生效,应用不应立即使用。Linux 不暴露该状态给用户态,所以稳妥做法是跳过 scope ID 为 IPV6_ADDR_LINKLOCAL 的地址,除非明确需要 link-local 通信。
- IPv6 地址用
AF_INET6+inet_ntop(AF_INET6, &sin6->sin6_addr, buf, sizeof(buf)) - 检查
IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)排除非全局地址 - 同一接口多个 IPv4?一般不会,除非配置了 secondary 地址(如
ip addr add 192.168.1.100/24 dev eth0 label eth0:1),这时getifaddrs()仍能枚举出来
跨平台兼容性提醒
Windows 没有 getifaddrs(),glibc 提供,musl libc 也支持,但旧版 Android NDK(getifaddrs(),Windows 用 GetAdaptersAddresses()。
另一个坑是:某些嵌入式系统或精简发行版(如 Alpine + busybox)可能关闭 glibc 的网络数据库支持,导致 getifaddrs() 返回空列表——此时应 fallback 到读取 /sys/class/net/eth0/device/address(MAC)或 /proc/sys/net/ipv4/conf/eth0/forwarding(非 IP),但无法替代 IP 查询。
- 编译时加
-D_GNU_SOURCE(部分旧系统需要) - 链接时不需要额外库,
libc自带 - 不要依赖
ifconfig或ip addr show的 shell 输出解析——格式不稳定,且 fork 开销大
"enp0s3",有些是 "ens33",甚至 Docker 容器里可能是 "eth0" 或 "br-xxxx",硬编码接口名几乎必然出错。最好让调用方传入名称,或先用 getifaddrs() 列出所有接口供选择。C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










