89 votes

Est-il possible d'initialiser un pointeur C sur NULL?

J'avais écrit des choses comme

char *x=NULL;

sur l'hypothèse que

 char *x=2;

serait de créer un char pointeur d'adresse 2.

Mais, dans Le GNU C Programmation Tutoriel il dit qu' int *my_int_ptr = 2; stocke la valeur de l'entier 2 à tout hasard une adresse en my_int_ptr lorsqu'il est alloué.

Cela veut dire que mon propre char *x=NULL est l'assignation quelle que soit la valeur de NULL converti en char pour une adresse au hasard dans la mémoire.

Alors que

#include <stdlib.h>
#include <stdio.h>

int main()
{
    char *x=NULL;

    if (x==NULL)
        printf("is NULL\n");

    return EXIT_SUCCESS;
}

en effet, l'impression

est NULL

quand j'ai compiler et l'exécuter, je suis inquiet que je me fonde sur un comportement indéfini, ou au moins sous-spécifiée comportement, et que je devrais écrire

char *x;
x=NULL;

au lieu de cela.

111voto

Sourav Ghosh Points 54713

Est-il possible d'initialiser un C pointeur à NULL?

TL;DR - Oui, très bien.


La demande faite sur le guide se lit comme

D'autre part, si vous utilisez seulement le seul affectation initiale, int *my_int_ptr = 2;, le programme va essayer de remplir le contenu de la zone mémoire pointée par my_int_ptr de la valeur 2. Depuis my_int_ptr est rempli avec des déchets, il peut être n'importe quelle adresse. [...]

Eh bien, ils sont mauvais, vous avez raison.

Pour la déclaration,ignorant, pour l'instant, le fait que le pointeur d'entier de conversion est une mise en œuvre définies par le comportement)

int * my_int_ptr = 2;

my_int_ptr est une variable (de type pointeur vers int), elle a une adresse qui lui est propre (type: adresse du pointeur d'entier), vous êtes le stockage d'une valeur de 2 dans cette adresse.

Maintenant, my_int_ptr, étant un type de pointeur, on peut dire, c' points à la valeur de "type" à l'emplacement mémoire pointé par la valeur en my_int_ptr. Donc, vous êtes essentiellement de l'affectation de la valeur de la variable du pointeur, pas la valeur de l'emplacement mémoire pointé par le pointeur.

Donc, pour conclure

 char *x=NULL;

initialise le pointeur de la variable x de NULL, et non pas la valeur de l'adresse mémoire pointée par le pointeur.

C'est le même que

 char *x;
 x = NULL;    

L'Expansion:

Aujourd'hui, en se conformant strictement, un énoncé comme

 int * my_int_ptr = 2;

est illégale, car elle implique la violation de contrainte. Pour être clair,

  • my_int_ptr est un pointeur de variable, tapez int *
  • une constante entière, 2 type int, par définition.

et ils ne sont pas "compatibles" types, de sorte que cette initialisation n'est pas valide car elle viole les règles de la simple assignation, mentionné dans le chapitre §6.5.16.1/P1, décrit dans Lundin de réponse.

Dans le cas où ça intéresse quelqu'un comment initialisation est liée à une simple affectation des contraintes, citant C11, au chapitre §6.7.9, P11

L'initialiseur, pour un scalaire est une expression unique, éventuellement entre accolades. L' valeur initiale de l'objet est celui de l'expression (après conversion); le même type de les contraintes et les conversions que pour les simples d'attribution s'appliquent, compte le type de scalaires pour être les moins qualifiés version de son type déclaré.

53voto

Matt McNabb Points 14273

Le tutoriel est faux. En ISO C, int *my_int_ptr = 2; est une erreur. Dans GNU C, cela signifie la même chose que int *my_int_ptr = (int *)2; . Ceci convertit l'entier 2 en une adresse mémoire, d'une manière déterminée par le compilateur.

Il ne tente pas de stocker quoi que ce soit à l'emplacement indiqué par cette adresse (le cas échéant). Si vous écrivez *my_int_ptr = 5; , il essaiera de stocker le nombre 5 à l'emplacement indiqué par cette adresse.

17voto

Lundin Points 21616

Afin de comprendre pourquoi le tutoriel est faux, int *my_int_ptr = 2; est une "violation de contrainte", c'est le code qui n'est pas autorisé à compiler et le compilateur doit vous donner un diagnostic lors de la rencontre.

Comme par 6.5.16.1 Simple affectation:

Les contraintes

L'une des opérations suivantes doivent contenir:

  • l'opérande de gauche est atomique, qualifiés ou non qualifiés de type arithmétique, et le droit a l'arithmétique de type;
  • l'opérande de gauche est atomique, qualifiés ou non qualifiés version d'une structure ou d'une union de type compatible avec le type de la droite;
  • l'opérande de gauche est atomique, qualifiés ou non qualifiés de type pointeur, et (compte tenu du type de l'opérande de gauche après la lvalue de conversion) les deux opérandes sont des pointeurs vers qualifié ou non qualifié les versions de types compatibles, et le type pointé par la gauche, a tous les qualificatifs du type pointé par le droit;
  • l'opérande de gauche est atomique, qualifiés ou non qualifiés de type pointeur, et (compte tenu du type de l'opérande de gauche après la lvalue de conversion) l'un des opérandes est un pointeur vers un type d'objet, et de l'autre est un pointeur vers un qualifié ou non qualifié version de vide, et la type pointé par la gauche a tous les qualificatifs du type pointé par le droit;
  • l'opérande de gauche est atomique, qualifiés ou non qualifiés pointeur, et le droit est un pointeur null constante; ou
  • l'opérande de gauche est de type atomique, qualifiés ou non qualifiés _Bool, et le droit est un pointeur.

Dans ce cas, l'opérande de gauche est un pointeur non qualifiés. Nulle part n'est-il mentionner que l'opérande de droite est autorisé à être un nombre entier (arithmétique). Donc le code viole le C standard.

GCC est connu pour se comporter mal, sauf si vous explicitement dire qu'il soit un compilateur C standard. Si vous compilez le code -std=c11 -pedantic-errors, il sera bien de donner un diagnostic comme elle doit le faire.

15voto

taskinoor Points 24438

int *my_int_ptr = 2

stocke la valeur de l'entier de 2 à quelle adresse aléatoire est dans my_int_ptr quand il est alloué.

Ce qui est totalement faux. Si ce qui est écrit alors s'il vous plaît obtenir un meilleur livre ou un tutoriel.

int *my_int_ptr = 2 définit un entier pointeur qui pointe vers l'adresse 2. Vous obtiendrez probablement un plantage si vous essayez d'accéder à l'adresse 2.

*my_int_ptr = 2, c'est à dire sans l' int dans la ligne, les magasins de la valeur de deux à quelle adresse aléatoire my_int_ptr pointe. Ayant dit cela, vous pouvez affecter NULL d'un pointeur lorsqu'il est défini. char *x=NULL; est parfaitement valable C.

Edit: en écrivant cela, je ne savais pas que entier pointeur de conversion est définie par l'implémentation de comportement. Veuillez voir les bonnes réponses par @M. M et @SouravGhosh pour plus de détails.

14voto

Mike Nakis Points 7259

Beaucoup de confusion au sujet de C les pointeurs vient d'un très mauvais choix qui a été à l'origine concernant le style de codage, corroborée par une très mauvaise peu de choix dans la syntaxe de la langue.

int *x = NULL; est C correct, mais il est très trompeur, je dirais même absurde, et il a gêné la compréhension de la langue pour beaucoup de novices. Cela nous permet de penser que, plus tard, nous avons pu faire *x = NULL; ce qui est évidemment impossible. Vous voyez, le type de la variable n'est pas int, et le nom de la variable n'est pas *x, ni ne l' * dans la déclaration de jouer un rôle fonctionnel dans la collaboration avec l' =. C'est purement déclaratif. Donc, ce qui fait beaucoup plus de sens est ceci:

int* x = NULL; qui est également correcte C, mais il n'adhère pas à l'origine de K&R style de codage. Il rend parfaitement clair que le type est - int*, et le pointeur est une variable x, de sorte qu'il devient clairement évident, même pour les non-initiés que la valeur NULL est stocké dans x, ce qui est un pointeur vers int.

En outre, il est plus facile de tirer une règle: quand la star est loin de la variable nom, alors c'est une déclaration, tandis que l'étoile est attaché le nom est un déréférencement du pointeur.

Alors, maintenant, il devient beaucoup plus compréhensible que plus bas, nous pouvons soit nous n' x = NULL; ou *x = 2; , en d'autres termes, il rend plus facile pour un novice de voir comment variable = expression entraîne pointer-type variable = pointer-expression et dereferenced-pointer-variable = expression. (Pour les initiés, par "l'expression", je veux dire "rvalue'.)

Le mauvais choix dans la syntaxe de la langue, c'est que lors de la déclaration de variables locales que vous pouvez dire int i, *p; qui déclare un entier et un pointeur vers un entier, cela conduit à penser que l' * est une partie utile de ce nom. Mais il n'est pas, et cette syntaxe est juste une étrange cas particulier, ajouté à des fins de commodité, et à mon avis il n'aurait jamais existé, parce qu'il invalide la règle que j'ai proposé ci-dessus. Autant que je sache, nulle part ailleurs dans la langue est cette syntaxe de sens, mais même si elle l'est, c'points de divergence dans la façon dont les types de pointeur sont définies dans le C. Partout ailleurs, dans une seule des déclarations de variable, dans la liste des paramètres, les membres de la structure, etc. vous pouvez déclarer votre pointeur en tant que type* pointer-variable au lieu de type *pointer-variable; il est parfaitement légal et fait plus de sens.

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