尝试在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);
未显示任何警告。我做错了什么?
UPD。修复了printf()中的a
3条答案
按热度按时间luaexgnf1#
Attie已经给出了完整的答案,但这给出了存储套接字地址的替代方案:
socket(7)的手册页说明如下:
sockets API提供数据类型struct sockaddr_storage。
这种类型适合容纳所有
支持特定于域的套接字地址结构;它很大
足够,并正确对齐。
sockaddr_storage结构在必须
以通用方式处理套接字地址(例如,必须处理IPv4和IPv6套接字地址的程序)。
因此,与此相反:
你应该使用这个:
用于为大多数不同的地址类型获得足够的空间。
ecfdbz9o2#
不幸的是,这些结构不是这样工作的...你会发现
struct sockaddr
是一个“base”定义,它实际上只包括socket家族字段和一些最小的空间保留-然后通过类型转换,你可以访问另一个结构的字段。当你使用IPv6时,这意味着你将有一个更长的地址,并且需要相应地分配存储。struct sockaddr
struct sockaddr_in
struct sockaddr_in6
由于各种寻址方案使用不同的信息(例如:地址长度),您需要确保为您尝试使用的内容分配足够的存储空间。
在您的情况下,以下方法将起作用:
当您随后使用它来连接/绑定时,您可以将其类型转换到那里。当把结构传递给内核时,你几乎可以把它看作是传递一个无类型的指针。。内核知道你使用的是IPv6(因为你在调用
socket()
时和在地址族字段中这样说),因此知道要使用哪种类型。这里的类型转换只是让编译器高兴-您仍然共享完整的IPv6信息,以及该结构的大小。把结构看作是一个你可以放置在内存上的模板--它只是一个如何读取内存中保存的数据的指示器……通过将内存区域转换为另一种类型,根本不会导致底层的“thing”改变形状。
如果在堆(而不是堆栈)上分配地址,那么当调用
*alloc()
时,需要为正在使用的地址给予正确的大小。z18hc3ub3#
关于这部分的问题:
如果我这样做:
未显示任何警告。我做错了什么?
这段代码有几个问题。
首先,这个演员表
是完全错误的
a
不是struct sockaddr_in6
,而是struct sockaddr
。有了这个强制转换,你基本上对编译器撒谎了。将该内存视为它不是的东西是strict aliasing violation,这会调用未定义的行为。(例外情况是,它始终允许访问任何作为[[un]signed] char
数据的对象。然后,通过访问不是
struct sockaddr_in6
的某个对象的sin6_addr.s6_addr
,您可能会访问实际struct sockaddr a
对象之外的内存和如果struct sockaddr_in6
比struct sockaddr
有更严格的对齐要求,那么可能会出现任何对齐问题。违反其中任何一项都将也会调用未定义的行为。并且注意,仅仅创建一个未对齐的指针就足以调用未定义的行为--取消引用这样的指针对于调用未定义的行为是不必要的。既然现在知道
struct sockaddr_in6
比struct sockaddr
大,那么就知道访问了struct sockaddr a
对象之外的内存。这是未定义的行为。你还在阅读未初始化的数据-和that invokes undefined behavior too。
该代码以多达四种不同的方式调用未定义的行为:
1.严格混叠违规
1.在对象边界之外进行访问
1.对齐违例
1.读取未初始化数据
任何类型的未定义行为的结果之一是“看起来像预期的那样工作”。
根据您使用的编译器,增加警告级别和/或其他选项可能会生成一些警告和/或错误。GCC的
-Wall -pedantic
* 可能 * 在这里有所帮助。是的,POSIX在使用严格的别名时会有一点松散,当您必须将
struct sockaddr_in6
转换为struct sockaddr
才能实际使用该对象时。但是POSIX标准does impose requirements要求符合POSIX标准的实现必须允许这样做(C标准未定义的行为的一个特征是允许实现实际定义行为):当指向sockaddr_storage结构的指针被转换为指向sockaddr结构的指针时,sockaddr_storage结构的ss_family字段将Map到sockaddr结构的sa_family字段。当指向sockaddr_storage结构的指针被转换为指向协议特定地址结构的指针时,ss_family字段应Map到该结构的类型为sa_family_t并且标识协议的地址族的字段上。
(In我的意见是,POSIX的要求并不足以涵盖所有预期工作的
sockaddr_*
用法/转换/强制转换...)