在sockaddr和sockaddr_in之间进行转换

时间:2015-10-24 14:24:22

标签: c++ network-programming

我遇到了一个套接字编程教程,其中引用了

  

“指向struct sockaddr_in的指针可以转换为指向struct sockaddr 的指针,反之亦然

我不明白sockaddr_in如何被投射到sockaddr。将 Big 类型的指针强制转换为 Small 类型应该会产生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级只有两个整数而B级有4个整数。如果我有一个类型B的指针,我将其转换为A类型,那么我确定我可以获取前两个元素。但是,如果类A首先声明了2个字符并且稍后声明了2个字符,则指针将无法正确获取值,因为在这种情况下对象布局将是不同的。

修改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;
}

假设我通过调整变量类型来更改示例以使两个类具有相同的大小...即使大小保持不变,对象布局仍然可能不同。

3 个答案:

答案 0 :(得分:4)

我将添加到@gsamaras的回答说,未定义的行为并不总是意味着坏事即将发生。未定义的行为实际上表示&#34;我们*不提供任何关于XYZ发生时应该发生什么的规范&#34;。

(* C ++标准)。

这是操作系统发生的地方并说“#34;它由我们定义&#34;。

虽然通过标准将未关联的结构(sockaddr_insockaddr)置于未定义的行为,但OS API指定它对其API有效。

答案 1 :(得分:1)

它们大小相同,所以不,你没有得到任何 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("%zu\n", sizeof(unsigned short) + sizeof(char) * 14);
  printf("%zu\n", 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

您可能还想看一下这个问题:Why isn't sizeof for a struct equal to the sum of sizeof of each member?

请检查此问题:Is it possible to cast struct to another?

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

  

这被称为Type Punning。这里,两个结构都具有相同的大小,因此不存在结构大小的问题。虽然你几乎可以将任何东西投射到任何东西上,但使用结构进行渲染很容易出错。

理查德·J·罗斯三世也是如此:

  

这是C的“继承”形式(注意引号)。这是有效的,因为C不关心地址中的基础数据,只是你所代表的数据。

     

该函数通过使用sa_family字段确定它实际上是什么结构,并将其转换为函数内正确的sockaddr_in。

答案 2 :(得分:1)

不同尺寸并不重要。就像您可以将不同长度的字符串传递给各种字符串处理函数一样,您可以将不同长度的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。因此,它总是在同一个地方。