Ne, není to jen konvence.
sockaddr
je obecný deskriptor pro jakýkoli druh operace soketu, zatímco sockaddr_in
je struktura specifická pro komunikaci založenou na IP (IIRC, "in" znamená "InterNet"). Pokud vím, jedná se o určitý druh "polymorfismu":bind()
funkce předstírá, že bere struct sockaddr *
, ale ve skutečnosti bude předpokládat, že je předán příslušný typ struktury; i. E. ten, který odpovídá typu zásuvky, kterou zadáte jako první argument.
Nevím, jestli je to pro tuto otázku příliš relevantní, ale rád bych poskytl nějaké další informace, díky nimž bude typová kasta srozumitelnější pro mnoho lidí, kteří s C
nestrávili mnoho času být zmatený, když vidíte takovou typickou kastu.
Používám macOS
, takže beru příklady založené na hlavičkových souborech z mého systému.
struct sockaddr
je definován následovně:
struct sockaddr {
__uint8_t sa_len; /* total length */
sa_family_t sa_family; /* [XSI] address family */
char sa_data[14]; /* [XSI] addr value (actually larger) */
};
struct sockaddr_in
je definován následovně:
struct sockaddr_in {
__uint8_t sin_len;
sa_family_t sin_family;
in_port_t sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
Počínaje úplnými základy, ukazatel obsahuje pouze adresu. Takže struct sockaddr *
a struct sockaddr_in *
jsou v podstatě stejné. Oba si jen ukládají adresu. Jediný relevantní rozdíl je v tom, jak kompilátor zachází s jejich objekty.
Když tedy řeknete (struct sockaddr *) &name
, jen podvádíte kompilátor a říkáte mu, že tato adresa ukazuje na struct sockaddr
typ.
Řekněme tedy, že ukazatel ukazuje na umístění 1000
. Pokud je struct sockaddr *
uloží tuto adresu, bude uvažovat paměť od 1000
na sizeof(struct sockaddr)
mající členy podle definice struktury. Pokud struct sockaddr_in *
ukládá stejnou adresu, kterou bude považovat za paměť od 1000
na sizeof(struct sockaddr_in)
.
Když napíšete tento ukazatel, bude uvažovat stejnou sekvenci bajtů až do sizeof(struct sockaddr)
.
struct sockaddr *a = &name; // consider &name = 1000
Nyní, když přistupuji k a->sa_len
, kompilátor přistupuje z umístění 1000
na sizeof(__uint8_t)
což je stejná velikost bajtů jako v případě sockaddr_in
. Takže by to mělo přistupovat ke stejné sekvenci bajtů.
Stejný vzor je pro sa_family
.
Poté je v struct sockaddr
pole 14 bajtů znaků který ukládá data z in_port_t sin_port
(typedef
'd 16bitové celé číslo bez znaménka =2 bajty ), struct in_addr sin_addr
(prostě 32bitová adresa ipv4 =4 bajty) a char sin_zero[8]
(8 bajtů). Tyto 3 dohromady tvoří 14 bajtů.
Nyní jsou tyto tři uloženy v tomto 14bajtovém poli znaků a my můžeme přistupovat ke kterémukoli z těchto tří tím, že zpřístupníme příslušné indexy a znovu je přetypujeme.
Odpověď uživatele 529758 již vysvětluje důvod, proč to udělat.
Je to proto, že bind může vázat jiné typy soketů než IP sokety, například unixové doménové sokety, které mají jako typ sockaddr_un. Adresa pro soket AF_INET má jako adresu hostitele a port, zatímco soket AF_UNIX má cestu k souborovému systému.