试图在
struct sin6_addr
中操纵 IPv6 地址,例如清洁它:
struct sockaddr a;
memset(&(((struct sockaddr_in6 *)&a)->sin6_addr.s6_addr), 0, 16);
printf("SIZEOF: %lu\n", sizeof((((struct sockaddr_in6 *)&a)->sin6_addr.s6_addr)));
面对警告:
1.c:34:2: warning: 'memset' will always overflow; destination buffer has size 8, but size argument is 16 [-Wfortify-source]
memset(&(((struct sockaddr_in6 *)&a)->sin6_addr.s6_addr), 0, 16);
同时,
printf("SIZEOF...")
返回16个字节,所以空间应该够了吧?
此外,如果我这样做:
struct sockaddr a;
struct in6_addr aa;
aa = ((struct sockaddr_in6 *)&a)->sin6_addr.s6_addr;
memset(&aa, 0, 16);
没有显示警告。我做错了什么?
更新。修正了 printf()
Attie 已经给出了完整的答案,但这提供了存储套接字地址的替代方法:
struct sockaddr_storage
socket(7) 的手册页说明如下:
套接字 API 提供数据类型 struct sockaddr_storage。
这种类型适合容纳所有
支持特定域的套接字地址结构;它很大
足够并且正确对齐。
sockaddr_storage 结构在程序中很有用
以通用方式处理套接字地址(例如,程序必须 处理 IPv4 和 IPv6 套接字地址)。
所以不是这个:
struct sockaddr a;
你应该使用这个:
struct sockaddr_storage a;
为大多数不同的地址类型获得足够的空间。
不幸的是,这些结构不是那样工作的......你会发现
struct sockaddr
是一个“base”定义,它实际上只包括套接字系列字段和一些最小空间预留 - 然后通过类型转换,您可以访问另一个结构的字段。当您使用 IPv6 时,这意味着您将拥有 much 更长的地址,并且需要相应地分配存储空间。
由于各种寻址方案使用不同的信息(例如:地址长度),您需要确保为您尝试使用的内容分配足够的存储空间。
在您的情况下,以下方法可行:
struct sockaddr_in6 a;
memset(&(a->sin6_addr.s6_addr), 0, sizeof(a->sin6_addr.s6_addr));
printf("SIZEOF: %lu\n", sizeof(a->sin6_addr.s6_addr));
/* don't be afraid to clear the whole structure before filling it in too! */
memset(&a, 0, sizeof(a));
当您随后使用它来连接/绑定时,您将在那里进行类型转换。将结构传递给内核时,你几乎可以认为它是传递一个无类型指针......内核知道你正在使用IPv6(因为你在调用
socket()
时这么说了,并且在地址族字段中) ,因此要使用哪种类型。这里的类型转换只是让编译器开心——你仍然在共享完整的 IPv6 信息,以及该结构的大小。
int fd, ret;
fd = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
ret = connect(fd, (struct sockaddr *)&a, sizeof(a));
将结构视为可以覆盖内存的模板——它只是指示如何读取内存中保存的数据……通过将内存区域转换为另一种类型不会导致底层的“thing “完全改变形状。
如果你在堆上分配地址(不是堆栈),那么当你调用
*alloc()
时,你需要为你正在使用的寻址提供正确的大小。
struct sockaddr_in6 *a;
a = malloc(sizeof *a);
a = malloc(sizeof (struct sockaddr_in6)); /* this works too... */
a = malloc(sizeof (struct sockaddr)); /* this is BROKEN */