将指向较大结构的指针转换为指向较小结构的指针

这篇文章将以此为基础,但将更加通用。

通常我会看到指向大型结构的指针被转换为指向较小结构的指针,我认为这是有效的,因为指针只是结构中第一个字节的地址。 我还有一个问题是,当大多数方法没有指向他们期望的类型时,如何处理指针。

我将使用Socket API作为示例:

sockaddr_storagesockaddr但是指向sockaddr指针会被转换为指向sockaddr指针,然后才会传递给sockaddr这样的函数

 int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); 

因此,当指针指向比预期更大的结构时,如何处理函数。 他们说

“哦,传给我的大小( socklen_t addrlen )比我想象的要大。最好把这个指针扔回sockaddr_storage !”

然后像这样访问其成员? 或者他们做了更复杂的事情?

sockaddr是C的穷人多态性成语的完美例子。 在OO语言中, sockaddr将是一个基类,从中可以“派生”多种协议地址类型( sockaddr_insockaddr_in6sockaddr_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部分上运行

我的猜测是函数只是不关心,如果结构指向大于预期读取它至少不会给出访问违规。 当然,较大结构的第一部分必须具有与较小结构相同(类型)的成员,因此可以正确读取值。

可能不建议在您自己的代码中执行此操作,编译器会抱怨不兼容的类型。