Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

The `sockaddr` and `sockaddr_in` (and other similar) structs behave and are used as if they were a union. Why are they not part of an explicit union?

Tags:

c

unix

sockets

Some of the sockets/unix networking struct objects behave and are intended to be used as if they were a union type. In other words, many functions in the sockets API take a struct sockaddr*, and it is expected that the client will call these functions with a range of struct types by casting pointers to different types.

For example, a sockaddr_in (ipv4) and sockaddr_in6 (ipv6) struct may be cast to a struct sockaddr* by taking the address of the object and performing a pointer type cast.

I am curious to know why there is no explicit union type involved here, even though these objects are intended to be used and behave as if they were a union type.

I hope the question makes sense and is clear enough? This is a somewhat abstract or difficult to describe question.

union behave somewhat polymorphically. Perhaps this comment helps to explain what I am thinking about here.

like image 562
FreelanceConsultant Avatar asked Sep 07 '25 03:09

FreelanceConsultant


2 Answers

The sockets API is very old, dating back to the 1980s. The ANSI C standard was ratified only in 1989. Programmers using C were not concerned with academic undefined behavior issues, like that certain type punning is formally well-defined if done through a union, but not otherwise.

C unions are cumbersome to use, due to introducing extra members. I think ISO C still does not have transparent unions.

If you make a union out of all the possible structures, the resulting object will have a size large enough to contain all of them. That may be undesirable.

Only fairly recently (in terms of the long history of the API) did POSIX add the type struct sockaddr_storage which has this property of being large enough to define storage for any sockaddr type, but that's normally only used for that specific purpose.

Code not working with any type might not want the overhead of the extra storage not needed by its type.

The API designers opted for the simple "OOP" technique of just casting pointers from a "derived" class like struct sockaddr_in * to a "base" type like struct sockaddr, ensuring that any common members were put in front, so that for instance the address family could be accessed through any type to tell that the struct sockaddr is really struct sockaddr_in, due to its tag being AF_INET.

This is not unheard of C programs, not just the socket API of POSIX.

like image 73
Kaz Avatar answered Sep 09 '25 19:09

Kaz


In addition to what the other answers have said, many new protocols have appeared since the early days when sockaddr was introduced. Those protocols use their own unique sockaddr_... struct types (ie, sockaddr_un for UNIX domains, sockaddr_bth for Bluetooth, etc), sometimes in separate libraries/headers, but they are still "compatible" with socket APIs that take basic sockaddr* pointers. It would be much more difficult to develop new protocols if all of the structs had to be declared in a single union in one place.

like image 43
Remy Lebeau Avatar answered Sep 09 '25 19:09

Remy Lebeau