在sockaddr指针和sockaddr_in指针之间进行强制转换

casting between sockaddr pointer and sockaddr_in pointer

本文关键字:指针 sockaddr 转换 in 之间      更新时间:2023-10-16

我偶然看到一个套接字编程教程,里面引用了

"指向结构体sockaddr_in的指针可以被强制转换为指向结构体sockaddr的指针,反之亦然"

我不明白sockaddr_in如何被强制转换为sockaddr。将类型的指针强制转换为类型会导致UD行为。

struct sockaddr {
unsigned short sa_family; // address family, AF_xxx
char sa_data[14]; // 14 bytes of protocol address
};

struct sockaddr_in {
short int sin_family; // Address family, AF_INET
unsigned short int sin_port; // Port number
struct in_addr sin_addr; // Internet address
unsigned char sin_zero[8]; // Same size as struct sockaddr
};

强制类型转换如何不被定义?把这些相互关联不是很不安全吗?

如果a类只有两个int型,B类有4个int型。如果我有一个B类型的指针,并将其转换为a类型,那么我肯定可以获取前两个元素。但是,如果类A首先声明了2个字符,然后声明了2个int型,那么指针将无法正确获取值,因为在这种情况下对象布局将是不同的。

编辑1:

class Anu
{
public:
    char a;
    int b;
    Anu()
    {
        a='a';
    }
};
class Anurag
{
public:
    Anurag() { a=4;}
    int a;
    int b;
    int c;
    int d;
};
int main()
{
        Anu objanu;
        Anurag objanurag;
        Anurag *ptrAnurag= &objanurag;
        ptrAnurag= (Anurag*)&objanu;
        cout<<ptrAnurag->a; //Some weird value here
        return 0;
}

假设我改变了这个例子,这样两个类通过调整变量类型有相同的大小…对象的布局可能会有所不同,即使大小保持不变。

我要补充@gsamaras的回答,未定义行为并不总是意味着不好的事情即将发生。未定义行为实际上是说"如果XYZ发生,我们*不提供任何关于接下来应该发生什么的规范"。

(* c++标准).

这是操作系统发生的地方,并说"它是由我们定义的"。

虽然转换不相关的结构(sockaddr_in, sockaddr)可能是标准未定义的行为,但操作系统API指定它与他们的API有效。

大小不同不重要。就像你可以将不同长度的字符串传递给不同的字符串处理函数一样,你也可以将不同长度的struct sockaddr传递给不同的套接字处理函数。

struct sockaddr的大小由被调用函数根据结构体sa_family成员的内容来解释。还要注意,所有接受struct sockaddr *地址的函数也接受socklen_t参数,该参数保存传递的结构体的大小。

例如,struct sockaddr_un结构是110字节:

   struct sockaddr_un {
       sa_family_t sun_family;               /* AF_UNIX */
       char        sun_path[108];            /* pathname */
   };

被调用的函数如bind()getpeername()的声明类似于

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

,因为不同套接字结构的大小不同。

注意struct sockaddr_???的第一个成员是sa_family。因此它总是在同一个地方

它们的大小相等,所以不,你没有得到任何UB!

证明:

#include <stdio.h>
struct sockaddr {
  unsigned short sa_family; // address family, AF_xxx
  char sa_data[14]; // 14 bytes of protocol address
};
// src: http://www.gta.ufrj.br/ensino/eel878/sockets/sockaddr_inman.html
struct in_addr {
    unsigned long s_addr;  // load with inet_aton()
};
struct sockaddr_in {
  short int sin_family; // Address family, AF_INET
  unsigned short int sin_port; // Port number
  struct in_addr sin_addr; // Internet address
  unsigned char sin_zero[8]; // Same size as struct sockaddr
};
int main (void) {
  printf("%zun", sizeof(unsigned short) + sizeof(char) * 14);
  printf("%zun", sizeof(short int) + sizeof(unsigned short int) + sizeof(struct in_addr) + sizeof(unsigned char) * 8);
  return 0;
}
输出:

16
16

好评论:sockaddr: 2+14=16, sockaddr_in:2+2+4+8=16 - Amer Agovic

您可能还想看一下这个问题:为什么结构体的n't sizeof等于每个成员的sizeof之和?


请检查这个问题:是否有可能将结构强制转换为另一个?

我在这里也复制了Benoit的答案:

这被称为类型双关语。这里,两个结构具有相同的大小,因此不存在结构大小的问题。虽然你几乎可以将任何东西强制转换成任何东西,但是用结构体来做是很容易出错的。

和Richard J. Ross III的这首

这是C语言的"继承"形式。(注意引号)。这是有效的,因为C并不关心地址中的底层数据,只关心您表示它的方式。

函数通过使用sa_family字段来确定它实际是什么结构,并将其强制转换为函数内部适当的sockaddr_in。