40 votes

Est-ce que C vérifier si un pointeur est lié sans le pointeur déréférencé?

J'ai eu cette idée avec certaines personnes en disant que C hors limites, les pointeurs de provoquer un comportement non défini, même s'ils ne sont pas être déréférencé. exemple:

int a;
int *p = &a;
p = p - 1;

la troisième ligne ici va provoquer un comportement non défini même si p n'est jamais déréférencé (*p n'est jamais utilisé).

À mon avis, il semble illogique que C serait de vérifier si un pointeur est lié sans le pointeur utilisé (c'est comme quelqu'un serait d'inspecter les gens dans la rue pour voir si ils sont munis de fusils dans le cas où ils entrer dans sa maison. Là où l'idéal chose à faire est d'inspecter les gens quand ils sont sur le point d'entrer dans la maison). Je pense que si C vérifie que beaucoup de gestion d'exécution se produit.

De Plus, si C vraiment vérifier pour OOB pointeurs alors pourquoi cela ne cause pas de UB:

int *p; // uninitialized thus pointing to a random adress

dans ce cas, pourquoi rien ne se passe, même si le risque de p pointant vers un OOB adresse est élevé.

AJOUTER:

int a;
int *p = &a;
p = p - 1;

dire &a est de 1000. La valeur de p après l'évaluation de la troisième ligne:

  • 996 mais encore un comportement indéfini, car p pourrait être déréférencés quelque part et d'autre cause que le véritable problème.
  • valeur indéfinie et c'est un comportement indéfini.

parce que je pense que "le troisième ligne a été appelé à être de comportement indéfini" en premier lieu en raison de la possibilité d'utiliser à l'avenir que des OOB (pointeur déréférencé) et les gens, au fil du temps, pris comme un comportement indéfini dans son propre. Maintenant, c'est la valeur de p 100% 996 et qui restent encore à définir le comportement ou sa valeur ne sera pas défini?

65voto

chqrlie Points 17105

C ne permet pas de vérifier si un pointeur est en dehors des limites. Mais le matériel sous-jacent peuvent se comporter de façon étrange lorsqu'une adresse est calculée qui tombe à l'extérieur de l'objet, en pointant juste après la fin d'un objet d'être une exception. La Norme décrit de manière explicite ce que causer un comportement indéfini.

Pour la plupart des environnement actuel, le code ci-dessus ne pose pas de problème, mais des situations semblables pourraient causer des défauts de segmentation en x86 16 bits en mode protégé, il y a 25 ans.

Dans le langage de la Norme, une valeur de ce type pourrait être un piège valeur, quelque chose qui ne peut pas être manipulé sans avoir recours à un comportement indéfini.

La section pertinente de la Norme C11 est:

6.5.6 Additif opérateurs

  1. Quand une expression de type entier est ajoutée ou déduite à partir d'un pointeur, le résultat est du type du pointeur de l'opérande. Si le pointeur de l'opérande points à un élément d'un objet de tableau, et le tableau est assez grand, le résultat des points à un élément de décalage de l'élément d'origine, tels que la différence des indices résultant de l'original et les éléments du tableau est égale à l'expression entière. [...] Si le pointeur de l'opérande et le résultat de point d'éléments d'un même objet de tableau, ou un passé le dernier élément de l'objet tableau, l'évaluation ne doit pas produire un dépassement de capacité; sinon, le comportement est indéfini. Si le résultat des points l'un après le dernier élément de l'objet tableau, il ne doit pas être utilisé comme opérande de unaire * de l'opérateur qui est évaluée.

Un exemple similaire de comportement indéfini est ceci:

char *p;
char *q = p;

Simplement charger la valeur de pointeur non initialisé p invoque un comportement indéfini, même si elle n'est jamais déréférencé.

EDIT: c'est un point discutable en essayant d'argumenter à ce sujet. Que dit la Norme de calcul d'une telle adresse invoque un comportement indéfini, de sorte qu'il n'. Le fait que certaines implémentations peuvent juste de calcul de la valeur et de la stocker ou non n'est pas pertinent. Ne comptez pas sur des hypothèses concernant le comportement indéfini: le compilateur peut profiter de ses imprévisibles par nature, la nature afin de procéder à des optimisations que vous ne pouvez pas imaginer.

Par exemple cette boucle:

for (int i = 1; i != 0; i++) {
    ...
}

peut compiler à une boucle infinie sans aucun test à tous: i++ invoque un comportement indéfini si i est INT_MAX, de sorte que le compilateur analyse est ceci:

  • valeur initiale de l' i est > 0.
  • pour toute valeur positive de l' i < INT_MAX, i++ encore > 0
  • pour i = INT_MAX, i++ invoque un comportement indéfini, donc on peut supposer i > 0 parce que nous pouvons supposer quoi que ce soit nous s'il vous plaît.

Par conséquent, i toujours > 0 et le code de test peut être retiré.

21voto

Kerrek SB Points 194696

En effet, le comportement d'un programme C est pas défini si elle tente de calculer une valeur grâce à l'arithmétique des pointeurs que n'aboutit pas à un pointeur sur un élément, ou un passé la fin, le même élément de tableau. De C11 6.5.6/8:

Si le pointeur de la l'opérande et le résultat de point d'éléments d'un même objet de tableau, ou un passé la dernière élément de l'objet tableau, l'évaluation ne doit pas produire un dépassement de capacité; sinon, l' le comportement est indéfini.

(Pour les fins de la présente description, l'adresse d'un objet de type T peut-être considérée comme l'adresse du premier élément d'un tableau T[1].)

15voto

harmic Points 7450

Pour préciser, "un Comportement Indéfini" signifie que le résultat de la code en question n'est pas définie dans les normes relatives à la langue. Le résultat final dépend de la façon dont le compilateur est mis en œuvre, et peut aller de rien à un crash complet et tout entre les deux.

Les normes ne précise pas que tout contrôle de la portée des pointeurs devraient se produire. Mais par rapport à votre exemple, c'est ce qu'ils disent:

Quand une expression de type entier est ajouté ou soustrait à partir d'un pointeur ... Si le pointeur de l'opérande et le résultat du point de éléments d'un même objet de tableau, ou un passé le dernier élément de la objet de tableau, l'évaluation ne doit pas produire un dépassement de capacité; sinon, le comportement est indéfini. Si le résultat des points un passé la dernière élément de l'objet tableau, il ne doit pas être utilisé comme opérande d'un unaire * l'opérateur qui est évaluée.

La citation ci-dessus est de C99 §6.5.6 Par 8 (la dernière version que j'ai sous la main).

Remarque que ci-dessus s'applique également aux non-tableau de pointeurs, car dans le précédent paragraphe, il est dit:

Pour l'application de ces opérateurs, un pointeur vers un objet qui est pas un élément d'un tableau se comporte comme un pointeur vers le premier élément d'un tableau de longueur avec le type de l'objet comme sa type d'élément.

Donc, si vous effectuez l'arithmétique des pointeurs, et le résultat est soit à l'intérieur de limites ou points à un passé la fin de l'objet, alors vous obtiendrez un résultat valide, sinon vous obtiendrez un comportement indéfini. Que behaivour peut-être que vous vous retrouvez avec un errant pointeur, mais il pourrait être quelque chose d'autre.

7voto

porneL Points 42805

Oui, c'est un comportement indéfini, même si le pointeur n'est pas déréférencé.

C permet uniquement des pointeurs vers point seulement un élément du passé les limites du tableau.

4voto

Ollie Jones Points 20488

Lorsque les spécifications de dire que quelque chose est undefined,ce qui peut être assez déroutant.

Il signifie, en cette circonstance, la mise en œuvre de la spécification est libre de faire ce qu'il veut. Dans certains cas, il va faire quelque chose qui apparaît, de manière intuitive, correct. Dans d'autres cas, il ne sera pas.

Pour l'adresse de limite de specs, je sais que mon intuition vient de mes hypothèses sur un plat uniforme de la mémoire de modèle. Mais il existe d'autres modèles de mémoire.

Le mot "undefined" n'apparaît jamais dans un formulaire de spec involontairement. Les normes de comités décident généralement à utiliser le mot quand ils connaissent les différentes implémentations de la norme doivent faire des choses différentes. Dans de nombreux cas, la raison pour laquelle les différentes choses est la performance. Donc: l'apparition de la parole dans la spec est un drapeau rouge d'avertissement à ce sujet pour nous, simples mortels, les utilisateurs de la spec, que notre intuition peut-être tort.

Ce genre de "ce qu'il veut" spécification de la célèbre ennuyé rms quelques années en arrière. Donc, il a fait quelques versions de son gcc (Gnu compiler Collection) essayez de jouer à un jeu d'ordinateur quand il a rencontré quelque chose d'indéfini.

IBM a utilisé le mot imprévisible dans son cahier des charges en arrière dans le 360 / 370 jours. C'est un meilleur mot. Il rend le résultat sonore plus aléatoire et plus dangereux. Dans le cadre de "imprévisible" comportement mensonge aussi problématiques que les résultats "arrêter et de prendre feu."

Voici la chose, cependant. "Aléatoire" est un mauvais moyen pour décrire ce genre de comportement imprévisible, parce que "aléatoire" implique que le système peut faire quelque chose de différent chaque fois qu'il rencontre le problème. Si elle fait quelque chose de différent à chaque fois, vous avez une chance d'attraper le problème dans le test. Dans le monde de la "undefined" / "imprévisible" le comportement que le système fait la même chose à chaque fois, jusqu'à ce qu'il n'a pas. Et, vous savez le temps qu'il n'a pas l' aura des années après que vous pensez que vous avez terminé de tester votre stuff.

Ainsi, lorsque la spec dit que quelque chose est pas défini, ne pas faire cette chose. Sauf si vous êtes un ami de Murphy. OK?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X