6
votes

La désarférence de pointeur nulle apparente est-elle en réalité l'arithmétique du pointeur?

J'ai ce morceau de code. Il apparaît à une désarférence un pointeur nul ici, mais alors le résultat du bitwise-et du résultat avec non signé INT . Je ne comprends vraiment pas la partie entière. Qu'est-ce que c'est destiné à faire? Est-ce une forme de pointeur arithmétique? XXX

La sortie que je reçois est 44. Mais comment ça marche?


2 commentaires

Les réponses décrivent comment cela parvient à travailler, mais ne faites pas cela: ça fait mal de le regarder.


Voir aussi Stackoverflow.com/questions / 713963 / ...


5 Réponses :


7
votes

Cela vous donne le décalage en octets du champ B à l'intérieur du hi struct

((struct hi *) 0) est un pointeur sur un hi struct, à partir de l'adresse 0 . .

(((struct hi *) 0) -> b) est le champ B de la structure ci-dessus

& ((((struct hi *) 0) -> b) est l'adresse du champ ci-dessus. Parce que le hi struct est situé à l'adresse 0 , il s'agit du décalage de B dans la structure.

(non signé int) & (((struct hi *) 0) -> b) est une conversion de celle du type d'adresse à non signé INT , de sorte que Il peut être utilisé comme un nombre.

Vous n'êtes pas réellement déréférencier A null pointeur. Vous faites juste du pointeur arithmétique.


Accéder à (((struct hi *) 0) -> b) vous donnera une erreur de segmentation car vous essayez d'accéder à un emplacement de mémoire interdit.

en utilisant & (((((struct hi *) 0) -> b) ne vous donne pas de défaut de segmentation car vous ne prenez que l'adresse de cette mémoire interdite emplacement, mais vous n'essayez pas de accès dit emplacement.


3 commentaires

Y a-t-il des raisons d'utiliser cette méthode sur offset de ?


offsetof est probablement mis en œuvre de cette façon dans certains fichiers d'en-tête. Il est donc préférable d'utiliser officeTof (parce que si cette façon ne fonctionne pas, les personnes qui ont écrit l'en-tête utilisée probablement une autre que cela fait) mais c'est essentiellement la même chose.


et ((struct salut *) 0) -> b; me donne une faute de segmentation.Mais lorsque j'ajoute l'opérateur "&" Ça marche ?? comment c'est?



8
votes

Ce n'est pas vraiment la proportion du pointeur NULL. Vous devriez regarder sur tout le code. Ce que le code dit est: prenez le numéro 0 , le traiter comme struct hi * , sélectionnez élément B dans la structure il pointe et prenez adresse de cet élément. Le résultat de cette opération serait le décalage de l'élément B du début de la structure. Lorsque vous l'ajoutez au pointeur, vous obtenez l'élément B qui est égal à 4 .


1 commentaires

@James c'est ce que je voulais dire :)



3
votes

Vous devez utiliser un compilation 32 bits (ou une compilation de 64 bits sous Windows).

La première expression - pour num - est une implémentation courante du offset de macro de ; Ce n'est pas portable, mais cela fonctionne souvent.

La deuxième expression ajoute que sur 0 (le pointeur NULL) et vous donne la même réponse - 4. La deuxième expression ajoute 4 à l'adresse de base de l'objet que PTR pointe vers, et c'est la valeur 4 dans la structure.

Votre sortie n'inclut pas une nouvelle ligne - elle devrait probablement (le comportement n'est pas totalement portable car il est défini comme si vous n'incluez pas la nouvelle ligne: C99 §7.19.2: «Si la dernière ligne nécessite une nouvelle ligne. Le caractère -line est défini par la mise en œuvre. "). Sur une boîte Unix, il est désordonné car l'invite suivante apparaîtra immédiatement après 44.


2 commentaires

La deuxième expression l'ajoute en réalité à PTR - qui pointe à la structure, donc compte tenu du décalage de B , car c'est ce que B est égal à.


Comportement de Qu'est-ce que est indéfini si vous n'incluez pas de nouvelle ligne?



3
votes

Ce n'est pas un "and", cela prend l'adresse de l'argument de droite.
Ceci est un hack standard pour obtenir le décalage d'un membre de la structure au moment de l'exécution. Vous moulez 0 à un pointeur sur Struct HI, puis référencez le membre «B» et obtenez son adresse. Ensuite, vous ajoutez ce décalage au pointeur "PTR" et obtenez une adresse réelle du champ "B" de la structure pointée par PTR, qui est ob. Ensuite, vous jetez que le pointeur retourne au pointeur d'int (parce que B est INT) et la sortie. Ceci est la 2ème impression. La première sortie d'impression num, qui n'est pas non plus parce que la valeur de B est de 4, mais parce que 4 est le décalage du champ B dans la structure HI. Qui est la taille de (int), parce que b suit a, et a int ... J'espère que cela a du sens :)


4 commentaires

Alors, c'est quelque chose comme ça - je prends l'adresse 0, puis trouvez le décalage de ma structure variable de 0, puis ajoutez-le à l'adresse de l'originaire, c'est-à-dire PTR ??


Oui. Google à compter de la compensation mentionnée dans d'autres réponses, vous trouverez quelques exemples de la raison pour laquelle cela est utile.


et ((struct salut *) 0) -> b; me donne une faute de segmentation.Mais lorsque j'ajoute l'opérateur "&" Ça marche ?? comment c'est??


Eh bien, quand vous demandez ((struct hik *) 0) -> B Vous demandez la valeur stockée à l'adresse 4, que vous n'avez pas accès à et lorsque vous demandez et que vous demandez cela adresse, qui est 4, et est un calcul valide. Vous pouvez parler de la question, mais vous ne pouvez pas le demander.



0
votes

Juste pour clarifier que vous devez comprendre la différence entre la déréférence NULL-POINTER et quand elle n'est pas considérée comme une référence. La spécification dicte réellement que la référence ne se produit pas et est en fait optimisée lorsque vous avez l'opérateur & (adresse de l'adresse de) dans l'expression.

donc le & (((struct t *) 0) -> b) optimise réellement le -> et saute simplement ce nombre d'octets de compensation 0 et suppose que c'est une structure T *. Cela obscurcit vraiment les choses pour les nouveaux débutants. Cependant, il est largement utilisé dans le noyau Linux - et fournit un sens réel de List_Entry, List_head's et divers Pointer Arithmétic Magic que les débutants ne peuvent pas comprendre.

En tout état de cause, il s'agit d'une manière programmatique de trouver le décalage de 'B' dans l'objet struct T. Il est utilisé dans une compensation de fonctionnalités List_head, telles que List_Entry.

Pour plus d'informations - vous pouvez en lire à propos de ce livre de Robert Love intitulé "Développement du noyau Linux".


1 commentaires

Tous les calculs d'adresse ne sont-ils pas nécessaires pour fonctionner et produire des adresses d'objets adressables, sinon les adresses suivantes suivant des objets adressables? Une implémentation ne serait-elle pas autorisée à déclencher des démons nasaux à tout moment, un calcul de l'adresse fonctionne avec ou produit autre chose?