ifaddrs 是什么?
ifaddrs 结构体定义在 <ifaddrs.h> 头文件中,它的主要作用是提供一个链表,链表中的每个节点都代表一个网络接口(eth0, wlan0, lo 等),并包含了该接口的详细地址信息(如 IP 地址、子网掩码、广播地址等)。

(图片来源网络,侵删)
当你需要编写一个程序来获取本机所有网络接口及其配置时,ifaddrs 是最标准、最常用的方法。
核心结构体定义
ifaddrs 结构体本身很简单,它包含两个主要部分:
- 一个指向下一个
ifaddrs结构体的指针,用于构建链表。 - 一个名为
ifa_addr的指针,它指向一个更通用的地址结构体(通常是sockaddr或其子类型,如sockaddr_infor IPv4,sockaddr_in6for IPv6)。
以下是 <ifaddrs.h> 中的典型定义:
#include <sys/socket.h> // 包含 sockaddr 的定义
#include <ifaddrs.h>
struct ifaddrs {
struct ifaddrs *ifa_next; // 指向链表中的下一个接口
char *ifa_name; // 接口名称,"eth0", "lo"
unsigned int ifa_flags; // 接口标志位 (IFF_UP, IFF_LOOPBACK 等)
struct sockaddr *ifa_addr; // 接口的网络地址 (IPv4/IPv6)
struct sockaddr *ifa_netmask; // 接口的子网掩码
union {
struct sockaddr *ifu_broadaddr; // 广播地址 (IPv4)
struct sockaddr *ifu_dstaddr; // 点对点目标地址 (IPv4)
} ifa_ifu;
#define ifa_broadaddr ifa_ifu.ifu_broadaddr
#define ifa_dstaddr ifa_ifu.ifu_dstaddr
void *ifa_data; // 接口特定的额外数据 (不常用)
};
关键成员解析:

(图片来源网络,侵删)
ifa_next: 链表指针,遍历时使用。ifa_name: 字符串,表示网络接口的名称。ifa_flags: 整数,表示接口的状态,最常用的标志是:IFF_UP(1): 接口已开启。IFF_RUNNING(2): 接口已运行(网线已连接)。IFF_LOOPBACK(8): 接口是回环接口 (lo)。IFF_BROADCAST(16): 接口支持广播。IFF_POINTOPOINT(32): 接口是点对点连接。
ifa_addr: 最重要的成员,它是一个指向struct sockaddr的通用指针,你不能直接使用它,需要根据地址族(ifa_addr->sa_family)将其转换为具体的类型:ifa_addr->sa_family == AF_INET,则转换为struct sockaddr_in *,用于处理 IPv4 地址。ifa_addr->sa_family == AF_INET6,则转换为struct sockaddr_in6 *,用于处理 IPv6 地址。
ifa_netmask: 子网掩码,同样是一个通用sockaddr指针,需要根据地址族进行转换。ifa_broadaddr/ifa_dstaddr: 分别对应广播地址和点对点目标地址,只在特定类型的接口上存在。ifa_data: 一个预留的指针,用于存储接口特定的额外数据,现代程序中很少使用。
如何使用 ifaddrs:标准流程
使用 ifaddrs 的标准流程分为三步:
- 获取链表: 调用
getifaddrs()函数,它会返回一个指向链表头部的指针。 - 遍历链表: 使用一个循环,顺着
ifa_next指针遍历整个链表。 - 释放内存: 调用
freeifaddrs()函数,释放getifaddrs()分配的内存。这一步至关重要,否则会导致内存泄漏。
相关函数:
int getifaddrs(struct ifaddrs **ifap);- 功能:获取所有网络接口的信息,并将结果存储在一个链表中。
- 参数
ifap是一个二级指针,函数会在这里返回链表头部的地址。 - 成功时返回 0,失败时返回 -1 并设置
errno。
void freeifaddrs(struct ifaddrs *ifa);- 功能:释放
getifaddrs()分配的整个链表的内存。 - 参数
ifa是getifaddrs()返回的链表头指针。
- 功能:释放
完整代码示例
下面的示例代码会打印出所有已开启的网络接口的名称、IPv4 地址和子网掩码。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netdb.h>
#include <ifaddrs.h>
#include <arpa/inet.h> // 用于 inet_ntoa
// 辅助函数:将 sockaddr 转换为可读的字符串 (IPv4)
void print_ipv4_info(struct sockaddr_in *addr) {
char str[INET_ADDRSTRLEN];
// inet_ntoa 是较旧的函数,inet_ntop 更现代,但这里为了简单使用 inet_ntoa
// 注意: inet_ntoa 会覆盖内部静态缓冲区,在循环中多次调用可能有问题
// 更好的做法是使用 inet_ntop
if (inet_ntop(AF_INET, &(addr->sin_addr), str, INET_ADDRSTRLEN) != NULL) {
printf(" Address: %s\n", str);
} else {
perror("inet_ntop");
}
}
int main() {
struct ifaddrs *ifaddrs_ptr = NULL;
struct ifaddrs *ifa = NULL;
// 1. 获取接口地址信息链表
if (getifaddrs(&ifaddrs_ptr) == -1) {
perror("getifaddrs");
return -1;
}
// 2. 遍历链表
for (ifa = ifaddrs_ptr; ifa != NULL; ifa = ifa->ifa_next) {
// 我们只关心已开启的接口
if (!(ifa->ifa_flags & IFF_UP)) {
continue;
}
printf("Interface: %s\n", ifa->ifa_name);
printf(" Flags: %u\n", ifa->ifa_flags);
// 处理 IPv4 地址
if (ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET) {
struct sockaddr_in *addr_in = (struct sockaddr_in *)ifa->ifa_addr;
printf(" Family: AF_INET (IPv4)\n");
print_ipv4_info(addr_in);
// 处理子网掩码
if (ifa->ifa_netmask) {
struct sockaddr_in *netmask_in = (struct sockaddr_in *)ifa->ifa_netmask;
printf(" Netmask: %s\n", inet_ntoa(netmask_in->sin_addr));
}
}
// 处理 IPv6 地址 (示例中省略,但原理相同)
else if (ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET6) {
printf(" Family: AF_INET6 (IPv6)\n");
// 可以添加打印 IPv6 地址的代码
}
printf("-------------------------------------\n");
}
// 3. 释放内存
freeifaddrs(ifaddrs_ptr);
return 0;
}
编译和运行
将上述代码保存为 list_interfaces.c,然后使用 GCC 编译:
gcc list_interfaces.c -o list_interfaces
运行程序:

(图片来源网络,侵删)
./list_interfaces
可能的输出示例:
Interface: lo
Flags: 8195
Family: AF_INET (IPv4)
Address: 127.0.0.1
Netmask: 255.0.0.0
-------------------------------------
Interface: eth0
Flags: 65535
Family: AF_INET (IPv4)
Address: 192.168.1.101
Netmask: 255.255.255.0
-------------------------------------
Interface: docker0
Flags: 4099
Family: AF_INET (IPv4)
Address: 172.17.0.1
Netmask: 255.255.0.0
-------------------------------------
| 特性 | 描述 |
|---|---|
| 用途 | 获取本地系统中所有网络接口的详细信息。 |
| 头文件 | <ifaddrs.h> |
| 核心结构 | struct ifaddrs,通过 ifa_next 组成链表。 |
| 关键函数 | getifaddrs() (获取链表), freeifaddrs() (释放内存)。 |
| 地址处理 | ifa_addr 是通用指针,需根据 sa_family (如 AF_INET, AF_INET6) 转换为具体类型 (sockaddr_in, sockaddr_in6) 后才能使用。 |
| 优点 | 标准、强大,能同时获取 IPv4 和 IPv6 信息,是网络编程的基础工具。 |
| 注意事项 | 必须调用 freeifaddrs() 释放内存,否则会导致内存泄漏。 |
ifaddrs 是 C 语言进行底层网络编程时不可或缺的工具,理解它的使用方法对于编写网络诊断工具、服务器程序等非常有帮助。
