J'avais écrit des choses comme
char *x=NULL;
en supposant que
char *x=2;
créerait un char
pointeur vers l'adresse 2.
Mais, dans le didacticiel de programmation GNU C, il est dit que int *my_int_ptr = 2;
stocke la valeur entière 2
dans n'importe quelle adresse aléatoire my_int_ptr
lorsqu'elle est allouée.
Cela semblerait impliquer que le mien char *x=NULL
assigne quelle que soit la valeur de NULL
cast à a char
est à une adresse aléatoire en mémoire.
Tandis que
#include <stdlib.h>
#include <stdio.h>
int main()
{
char *x=NULL;
if (x==NULL)
printf("is NULL\n");
return EXIT_SUCCESS;
}
imprime en fait
est NULL
lorsque je le compile et l'exécute, je crains de me fier à un comportement non défini, ou du moins à un comportement sous-spécifié, et que je devrais écrire
char *x;
x=NULL;
au lieu.
c
pointers
initialization
fagricipni
la source
la source
int *x = whatever;
fait et ce quiint *x; *x = whatever;
fait.int *x = whatever;
se comporte réellement commeint *x; x = whatever;
, non*x = whatever;
.Réponses:
TL; DR Oui, beaucoup.
La réclamation réelle faite sur le guide se lit comme suit
Eh bien, ils ont tort, vous avez raison.
Pour l'instruction, (en ignorant, pour l'instant, le fait que la conversion du pointeur vers un entier est un comportement défini par l'implémentation )
my_int_ptr
est une variable (de type pointeur versint
), elle a une adresse qui lui est propre (type: adresse du pointeur vers un entier), vous stockez une valeur de2
dans cette adresse.Maintenant,
my_int_ptr
étant un type pointeur, nous pouvons dire, il pointe vers la valeur de "type" à l'emplacement mémoire pointé par la valeur contenue dansmy_int_ptr
. Ainsi, vous affectez essentiellement la valeur de la variable pointeur, et non la valeur de l'emplacement mémoire pointé par le pointeur.Donc, pour conclure
initialise la variable du pointeur
x
àNULL
, pas la valeur à l'adresse mémoire pointée par le pointeur .C'est la même chose que
Expansion:
Maintenant, étant strictement conforme, une déclaration comme
est illégale, car elle implique une violation de contrainte. Pour être clair,
my_int_ptr
est une variable de pointeur, tapezint *
2
a un typeint
, par définition.et ce ne sont pas des types "compatibles", donc cette initialisation est invalide car elle enfreint les règles d'assignation simple, mentionnées au chapitre §6.5.16.1 / P1, décrites dans la réponse de Lundin .
Au cas où quelqu'un serait intéressé par la manière dont l'initialisation est liée à de simples contraintes d'affectation, citant
C11
, chapitre §6.7.9, P11la source
2
c'est unint
, l'affectation est un problème. Mais c'est plus que ça.NULL
peut également être unint
, unint 0
. C'est juste que celachar *x = 0;
est bien défini etchar *x = 2;
ne l'est pas. 6.3.2.3 Pointeurs 3 (BTW: C ne définit pas un littéral entier , seulement un littéral de chaîne et un littéral composé .0
Est une constante entière )char *x = (void *)0;
, d'être conforme? ou est-ce seulement avec d'autres expressions qui donne la valeur0
?0
sont spéciales: elles se convertissent implicitement en pointeurs nulles séparément des règles habituelles pour convertir explicitement des expressions entières générales en types pointeurs.int *p = somePtrExpression
est à mon humble avis plutôt horrible car elle semble définir la valeur de,*p
mais elle définit en fait la valeur dep
.Le tutoriel est faux. En ISO C,
int *my_int_ptr = 2;
c'est une erreur. Dans GNU C, cela signifie la même chose queint *my_int_ptr = (int *)2;
. Cela convertit l'entier2
en une adresse mémoire, d'une manière déterminée par le compilateur.Il n'essaye pas de stocker quoi que ce soit à l'emplacement adressé par cette adresse (le cas échéant). Si vous continuiez à écrire
*my_int_ptr = 5;
, alors il essaierait de stocker le numéro5
à l'emplacement adressé par cette adresse.la source
Pour clarifier pourquoi le didacticiel est erroné,
int *my_int_ptr = 2;
est une "violation de contrainte", c'est du code qui n'est pas autorisé à compiler et le compilateur doit vous donner un diagnostic lorsqu'il le rencontre.Selon 6.5.16.1 Affectation simple:
Dans ce cas, l'opérande gauche est un pointeur non qualifié. Nulle part il ne mentionne que l'opérande de droite est autorisé à être un entier (type arithmétique). Donc, le code enfreint la norme C.
GCC est connu pour se comporter mal à moins que vous ne lui disiez explicitement qu'il s'agit d'un compilateur C standard. Si vous compilez le code en tant que
-std=c11 -pedantic-errors
, il donnera correctement un diagnostic comme il se doit.la source
void *
, est appelée une constante pointeur nul.» Notez l'avant-dernier point dans votre devis. C'est doncint* p = 0;
une manière légale d'écrireint* p = NULL;
. Bien que ce dernier soit plus clair et plus conventionnel.int m = 1, n = 2 * 2, * p = 1 - 1, q = 2 - 1;
légale.intptr_t
explicitement en l'un des types autorisés sur le côté droit. Autrement dit,void* a = (void*)(intptr_t)b;
est légal par le point 4, mais(intptr_t)b
n'est ni un type de pointeur compatible, ni avoid*
, ni une constante de pointeur nul, etvoid* a
n'est ni un type arithmétique ni_Bool
. La norme dit que la conversion est légale, mais pas qu'elle est implicite.int *my_int_ptr = 2
C'est complètement faux. Si cela est réellement écrit, veuillez obtenir un meilleur livre ou un tutoriel.
int *my_int_ptr = 2
définit un pointeur entier qui pointe vers l'adresse 2. Vous obtiendrez probablement un plantage si vous essayez d'accéder à l'adresse2
.*my_int_ptr = 2
, c'est-à-dire sans leint
dans la ligne, stocke la valeur deux à n'importe quelle adresse aléatoiremy_int_ptr
pointée. Cela dit, vous pouvez attribuerNULL
à un pointeur lorsqu'il est défini.char *x=NULL;
est parfaitement valide C.Edit: En écrivant ceci, je ne savais pas que la conversion d'entier en pointeur était un comportement défini par l'implémentation. Veuillez consulter les bonnes réponses de @MM et @SouravGhosh pour plus de détails.
la source
Une grande confusion sur les pointeurs C vient d'un très mauvais choix qui a été fait à l'origine concernant le style de codage, corroboré par un très mauvais petit choix dans la syntaxe du langage.
int *x = NULL;
est correct C, mais il est très trompeur, je dirais même absurde, et cela a gêné la compréhension de la langue pour de nombreux novices. Cela fait penser que plus tard, nous pourrions faire*x = NULL;
ce qui est bien sûr impossible. Vous voyez, le type de la variable n'est pasint
, et le nom de la variable ne l'est pas*x
, et le*
dans la déclaration ne joue aucun rôle fonctionnel en collaboration avec le=
. C'est purement déclaratif. Donc, ce qui a beaucoup plus de sens, c'est ceci:int* x = NULL;
ce qui est également correct C, bien qu'il n'adhère pas au style de codage K&R d'origine. Cela rend parfaitement clair que le type estint*
et que la variable de pointeur estx
, de sorte qu'il devient clairement évident, même pour les non-initiés, que la valeurNULL
est stockée dansx
, qui est un pointeur versint
.De plus, cela facilite la dérivation d'une règle: lorsque l'étoile est éloignée du nom de la variable, il s'agit d'une déclaration, tandis que l'étoile attachée au nom est un déréférencement de pointeur.
Donc, maintenant, il devient beaucoup plus compréhensible que plus bas, nous pouvons le faire
x = NULL;
ou*x = 2;
en d'autres termes, il est plus facile pour un novice de voir commentvariable = expression
mène àpointer-type variable = pointer-expression
etdereferenced-pointer-variable = expression
. (Pour les initiés, par «expression», je veux dire «rvalue».)Le choix malheureux dans la syntaxe du langage est que lors de la déclaration de variables locales, vous pouvez dire
int i, *p;
qui déclare un entier et un pointeur vers un entier, cela laisse donc penser que le*
est une partie utile du nom. Mais ce n'est pas le cas, et cette syntaxe n'est qu'un cas spécial excentrique, ajouté par commodité, et à mon avis, elle n'aurait jamais dû exister, car elle invalide la règle que j'ai proposée ci-dessus. Autant que je sache, nulle part ailleurs dans le langage cette syntaxe n'a de sens, mais même si c'est le cas, elle indique une divergence dans la façon dont les types de pointeurs sont définis en C. Partout ailleurs, dans les déclarations à variable unique, dans les listes de paramètres, dans les membres de structure, etc., vous pouvez déclarer vos pointeurs commetype* pointer-variable
au lieu detype *pointer-variable
; c'est parfaitement légal et a plus de sens.la source
int *x = NULL; is correct C, but it is very misleading, I would even say nonsensical,
... Je dois accepter de ne pas être d'accord.It makes one think
.... arrêtez de penser, lisez d'abord un livre C, sans offense.int* somePtr, someotherPtr
déclarer deux pointeurs, en fait, j'avais l'habitude d'écrireint* somePtr
mais cela conduit au bogue que vous décrivez.create
place decreat
. :) Le fait est que c'est comme ça et que nous devons nous façonner pour nous adapter à cela. Tout se résume à un choix personnel à la fin de la journée, d'accord.Je voudrais ajouter quelque chose d'orthogonal aux nombreuses excellentes réponses. En fait, l'initialisation vers
NULL
est loin d'être une mauvaise pratique et peut être pratique si ce pointeur peut ou non être utilisé pour stocker un bloc de mémoire alloué dynamiquement.Étant donné que selon la norme ISO-IEC 9899
free
est un nop lorsque l'argument estNULL
, le code ci-dessus (ou quelque chose de plus significatif dans le même sens) est légitime.la source
void*
est converti au besoin. Mais avoir du code qui fonctionne avec un compilateur C et C ++ pourrait avoir des avantages.const
pointeurs déclarés dans medias res , mais même lorsqu'un pointeur doit être mutable (comme celui utilisé dans une boucle ou parrealloc()
), le paramétrer pourNULL
attraper les bogues là où il était utilisé auparavant il a sa valeur réelle. Sur la plupart des systèmes, le déréférencementNULL
provoque une erreur de segmentation au point de défaillance (bien qu'il y ait des exceptions), alors qu'un pointeur non initialisé contient des déchets et l'écriture corrompt la mémoire arbitraire.NULL
, mais il peut être très difficile de distinguer un pointeur de garbage d'un valide. Il est donc utile de s'assurer que tous les pointeurs sont toujours valides ouNULL
, à partir du moment de la déclaration.c'est un pointeur nul
la source
C'est correct.
Cette fonction est correcte pour ce qu'elle fait. Il attribue l'adresse 0 au pointeur char x. Autrement dit, il pointe le pointeur x vers l'adresse mémoire 0.
Alternative:
Ma conjecture quant à ce que vous vouliez est:
la source
char* x = 0; if (x == 0)
sera vrai. Les pointeurs ne sont pas nécessairement des entiers.