2

This post will be building off of this one but will be a little more generic.

Often I see pointers to large structs being cast to pointers to smaller structs and I figure that works because the pointers are just the address of the first byte in the structure. What I still have a question about is how to most methods handle pointers when they aren't pointing at the type they expect.

I will use the Socket API as an example:

sockaddr_storage is larger than sockaddr but pointers to sockaddr_storage get cast to pointers to sockaddr before they are passed to functions like

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

So how to functions tend to handle things when the pointer is pointing to a larger structure than expected. Do they say

"Oh the size (socklen_t addrlen) passed to me is larger than I thought it would be. Better cast this pointer back to a sockaddr_storage!"

And then access its members like that? Or do they do something more complex?

Community
  • 1
  • 1
Matt Vaughan
  • 1,135
  • 2
  • 13
  • 19

3 Answers3

3

sockaddr is a perfect example of C's poor-man polymorphism idiom. In an OO language, sockaddr would be a base class, from which the multitudes of protocol address types (sockaddr_in, sockaddr_in6, sockaddr_ll, etc.) are 'derived'.

Basically, a sockaddr is defined as having a type (sa_family) that specifies what it really holds, followed by some data. A pointer to a sockaddr usually points to a derived struct (sockaddr_*) which specifies a specific interpretation of the data.

sockaddr_storage is similar to plain sockaddr, in that it doesn't hold a specific protocol address, only storage space. The difference is that sockaddr_storage specifies more space than sockaddr, making it a suitable type to stash the protocol-specific sockaddrs in.

A function looking at a plain sockaddr first looks at sa_family, which is e.g. AF_INET (for IPv4) or AF_INET6 (for IPv6) or something else, and then casts the pointer it was given to the appropriate sockaddr subtype (e.g. sockaddr_in for IPv4). It may then check addrlen to make sure the casted pointer points to enough space to hold the subtype.

nneonneo
  • 171,345
  • 36
  • 312
  • 383
  • Thank you very helpful! When you say that "sockaddr and sockaddr_storage don't hold addresses, only storage space", what do you mean? Isn't the storage space for addresses? – Matt Vaughan Apr 16 '13 at 00:58
  • 1
    It was a bit unclear. I meant that `sockaddr` and `sockaddr_storage` are not defined to hold any *specific* protocol address, but are just defined to hold some data. That data is interpreted as addresses by the appropriate `sockaddr_*` type. – nneonneo Apr 16 '13 at 01:02
  • Thank you again! Very straight forward and thorough answer. – Matt Vaughan Apr 16 '13 at 01:09
1

@nneonneo has given an excellent answer on what happens in the socket example.

In general however:

"What I still have a question about is how to most methods handle pointers when they aren't pointing at the type they expect."

What matters is whether the layout of the structure they receive a pointer to matches what the function plans to do with it. C provides some guarantees on layout which make this possible. In those circumstances, the functions do get a pointer to the type they expect. It's just that in memory there's some additional stuff following that which can be ignored.

Commonly, there is no downcast happening in the called function. It just operates on the base class part of the larger structure

Keith
  • 6,756
  • 19
  • 23
0

My guess is the functions just don't care, if the structure pointed to is larger than expected reading it at least won't give access-violations. The first part of the larger structure must of course have the same (types of) members as the smaller one so the values are read correctly.

It's probably not recommended to do this in your own code and the compiler will complain about incompatible types.

Kninnug
  • 7,992
  • 1
  • 30
  • 42