将指向较大结构的指针转换为指向较小结构的指针
这篇文章将以此为基础,但将更加通用。
通常我会看到指向大型结构的指针被转换为指向较小结构的指针,我认为这是有效的,因为指针只是结构中第一个字节的地址。 我还有一个问题是,当大多数方法没有指向他们期望的类型时,如何处理指针。
我将使用Socket API作为示例:
sockaddr_storage
比sockaddr
但是指向sockaddr
指针会被转换为指向sockaddr
指针,然后才会传递给sockaddr
这样的函数
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
因此,当指针指向比预期更大的结构时,如何处理函数。 他们说
“哦,传给我的大小( socklen_t addrlen
)比我想象的要大。最好把这个指针扔回sockaddr_storage
!”
然后像这样访问其成员? 或者他们做了更复杂的事情?
sockaddr
是C的穷人多态性成语的完美例子。 在OO语言中, sockaddr
将是一个基类,从中可以“派生”多种协议地址类型( sockaddr_in
, sockaddr_in6
, sockaddr_ll
等)。
基本上, sockaddr
被定义为具有一个类型( sa_family
),它指定它实际拥有的内容,然后是一些数据。 指向sockaddr
的指针通常指向派生结构( sockaddr_*
),该结构指定数据的特定解释。
sockaddr_storage
类似于plain sockaddr
,因为它不包含特定的协议地址,只保存存储空间。 区别在于sockaddr_storage
指定了比sockaddr
更多的空间,使其成为存储特定于协议的sockaddr
的合适类型。
查看普通sockaddr
函数首先查看sa_family
,例如AF_INET
(用于IPv4)或AF_INET6
(用于IPv6)或其他内容,然后将指定的指针强制转换为适当的sockaddr
子类型(例如,用于IPv4的sockaddr_in
)。 然后它可以检查addrlen
以确保铸造指针指向足够的空间来保存子类型。
@nneonneo对套接字示例中发生的事情给出了很好的答案。
但总的来说:
"What I still have a question about is how to most methods handle pointers when they aren't pointing at the type they expect."
重要的是它们接收指针的结构布局是否与函数计划对其进行匹配。 C为布局提供了一些保证,使其成为可能。 在这些情况下,函数会获得指向它们所期望的类型的指针。 只是在内存中有一些额外的东西,可以忽略。
通常,被调用函数中不会发生任何downcast
。 它只是在较大结构的base class
部分上运行
我的猜测是函数只是不关心,如果结构指向大于预期读取它至少不会给出访问违规。 当然,较大结构的第一部分必须具有与较小结构相同(类型)的成员,因此可以正确读取值。
可能不建议在您自己的代码中执行此操作,编译器会抱怨不兼容的类型。