Casting des pointeurs vers des structures plus grandes des pointeurs vers des structures plus petites

Ce billet s’appuiera sur celui-ci mais sera un peu plus générique.

Je vois souvent des pointeurs vers de grandes structures en cours de conversion en pointeurs vers des structures plus petites et je pense que cela fonctionne car les pointeurs ne sont que l’adresse du premier octet de la structure. Ce que j’ai toujours à savoir, c’est comment la plupart des méthodes gèrent les pointeurs lorsqu’elles ne pointent pas vers le type qu’elles attendent.

Je vais utiliser l’API Socket comme exemple:

sockaddr_storage est plus grand que sockaddr mais les pointeurs sur sockaddr_storage sont sockaddr_storage en pointeurs sur sockaddr avant d’être passés à des fonctions comme

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

Alors, comment les fonctions ont tendance à gérer les choses lorsque le pointeur pointe vers une structure plus grande que prévue. Dit-on

“Oh, la taille ( socklen_t addrlen ) qui m’a été transmise est plus grande que je ne le pensais. Mieux vaut renvoyer ce pointeur à un sockaddr_storage !”

Et puis accéder à ses membres comme ça? Ou font-ils quelque chose de plus complexe?

sockaddr est un exemple parfait de l’idiome de polymorphism des pauvres de C. Dans un langage OO, sockaddr serait une classe de base, à partir de laquelle les multitudes d’adresses de protocole ( sockaddr_in , sockaddr_in6 , sockaddr_ll , etc.) sont «dérivées».

Fondamentalement, un sockaddr est défini comme ayant un type ( sa_family ) qui spécifie ce qu’il contient réellement, suivi de quelques données. Un pointeur sur un sockaddr pointe généralement sur une structure dérivée ( sockaddr_* ) qui spécifie une interprétation spécifique des données.

sockaddr_storage est similaire à plain sockaddr , en ce sens qu’il ne contient pas d’adresse de protocole spécifique, mais uniquement d’espace de stockage. La différence est que sockaddr_storage spécifie plus d’espace que sockaddr , ce qui en fait un type approprié pour stocker les sockaddr spécifiques au protocole.

Une fonction qui regarde un sockaddr simple examine d’abord sa_family , par exemple AF_INET (pour IPv4) ou AF_INET6 (pour IPv6) ou autre, puis renvoie le pointeur atsortingbué au sous-type sockaddr approprié (par exemple, sockaddr_in pour IPv4). Il peut ensuite vérifier addrlen pour s’assurer que le pointeur addrlen pointe sur un espace suffisant pour contenir le sous-type.

@nneonneo a donné une excellente réponse sur ce qui se passe dans l’exemple de socket.

En général cependant:

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

Ce qui compte, c’est si la structure de la structure reçoit un pointeur qui correspond à ce que la fonction prévoit de faire avec. C fournit des garanties sur la mise en page qui rendent cela possible. Dans ces circonstances, les fonctions obtiennent un pointeur sur le type attendu. C’est juste qu’en mémoire, il y a des choses supplémentaires qui peuvent être ignorées.

Généralement, il n’ya pas de downcast dans la fonction appelée. Il fonctionne uniquement sur la partie base class de la plus grande structure

Je suppose que les fonctions ne s’intéressent tout simplement pas, si la structure indiquée est plus grande que prévu, sa lecture au moins ne donnera pas de violation d’access. La première partie de la structure la plus grande doit évidemment avoir les mêmes (types de) membres que la plus petite pour que les valeurs soient lues correctement.

Cela n’est probablement pas recommandé dans votre propre code et le compilateur se plaindra des types incompatibles.