81 votes

"int * nums = {5, 2, 1, 4}" provoque une erreur de segmentation

int *nums = {5, 2, 1, 4};
printf("%d\n", nums[0]);

provoque une erreur de segmentation, tandis que

int nums[] = {5, 2, 1, 4};
printf("%d\n", nums[0]);

ne fait pas. Maintenant:

int *nums = {5, 2, 1, 4};
printf("%d\n", nums);

imprime 5.

Sur cette base, je pense que l'initialisation de tableau de notation, {}, aveuglément les charges de ces données en ce que la variable est sur la gauche. Quand il est de type int[], le tableau est rempli, comme souhaité. Quand il est de type int*, le pointeur est rempli par 5, et les emplacements de mémoire après l'endroit où le pointeur est stocké sont remplis par 2, 1 et 4. Donc nums[0] tente de deref 5, provoquant une erreur de segmentation.

Si je me trompe, corrigez-moi. Et si je suis correct, veuillez donner des précisions, parce que je ne comprends pas pourquoi les initialiseurs de tableau de travail de la façon dont ils le font.

113voto

Lundin Points 21616

Il y a un (stupide) de la règle en C en disant que toute la plaine de la variable peut être initialisée avec un corset-joint une liste d'initialiseur, juste comme si c'était un tableau.

Par exemple, vous pouvez écrire int x = {0};, ce qui est complètement équivalent à int x = 0;.

Donc, lorsque vous écrivez int *nums = {5, 2, 1, 4}; vous sont effectivement donner un initialiseur de liste à un seul pointeur de variable. Cependant, il n'est qu'une seule variable de sorte qu'il ne affectées à la première valeur de 5, le reste de la liste est ignoré (en fait je ne pense pas que ce code avec un excès d'initialiseurs de même à compiler avec une stricte compilateur) - il ne pas s'écrit à la mémoire à tous. Le code est équivalent à int *nums = 5;. Ce qui signifie, numsdoit pointer vers l'adresse 5.

À ce stade, vous devriez avoir déjà reçu deux avertissements du compilateur/erreurs:

  • L'attribution entier pointeur sans un plâtre.
  • L'excès d'éléments dans la liste des initialiseurs.

Et puis, bien sûr, le code de crash et brûler depuis 5 est probablement pas une adresse valide, vous êtes autorisé à déréférencer avec nums[0].

Comme une note de côté, vous devriez printf pointeur d'adresses avec l' %p rédacteur de devis ou sinon, vous êtes en invoquant un comportement indéfini.


Je ne suis pas tout à fait sûr de ce que vous essayez de faire ici, mais si vous souhaitez définir un pointeur sur un tableau, il faut faire:

int nums[] = {5, 2, 1, 4};
int* ptr = nums;

// or equivalent:
int* ptr = (int[]){5, 2, 1, 4};

Ou si vous voulez créer un tableau de pointeurs:

int* ptr[] = { /* whatever makes sense here */ };

MODIFIER

Après quelques recherches, je peux dire que "l'excédent des éléments de la liste des initialiseurs" n'est en effet pas valide C - c'est un GCC extension.

La norme 6.7.9 Initialisation dit (l'emphase est mienne):

2 Pas de l'initialiseur doit tenter de fournir une valeur pour un objet n'est pas contenues dans l'entité en cours d'initialisation.

/--/

11 L'initialiseur, pour un scalaire est une expression unique, en option, placées entre crochets. La valeur initiale de l'objet, c'est que de l'expression (après conversion); le même type de contraintes et les conversions que pour les simples d'attribution s'appliquent, compte le type de l' scalaire à la non qualifiés version de son type déclaré.

"Scalaire de type" est un terme faisant référence à des variables individuelles qui ne sont pas de tableau, struct ou union de type (ceux-ci sont appelés "type de regroupement").

Donc en clair la norme dit: "lorsque vous initialisez une variable, n'hésitez pas à jeter dans certains supplémentaire des accolades autour de l'initialiseur d'expression, tout simplement parce que vous le pouvez."

28voto

artm Points 4966

SCÉNARIO 1

int *nums = {5, 2, 1, 4};    // <-- assign multiple values to a pointer variable
printf("%d\n", nums[0]);    // segfault

Pourquoi est-ce une erreur de segmentation?

Vous avez déclaré nums comme un pointeur vers int - c'est - nums est censé contenir l'adresse d' un entier dans la mémoire.

Vous ensuite essayé d'initialiser nums d'un tableau de plusieurs valeurs. Donc, sans creuser dans les détails, c'est sur le plan conceptuel incorrect - il n'est pas logique d'attribuer plusieurs valeurs à une variable qui est censé tenir sur une seule valeur. À cet égard, vous pouvez voir exactement le même effet si vous faites cela:

int nums = {5, 2, 1, 4};    // <-- assign multiple values to an int variable
printf("%d\n", nums);    // also print 5

Dans les deux cas (attribuer plusieurs valeurs à un pointeur ou une variable int), ce qui se passe ensuite est que la variable d'obtenir la première valeur qui est - 5, tout en restant valeurs sont ignorées. Ce code est conforme, mais vous pouvez obtenir des avertissements pour chaque valeur supplémentaire qui n'est pas censé être en mission:

warning: excess elements in scalar initializer.

Pour le cas de l'attribution de plusieurs valeurs de pointeur de variable, le programme de segmentation lorsque vous accédez nums[0], ce qui signifie que vous sont deferencing tout ce qui est stocké à l'adresse 5 littéralement. Vous n'avez pas à allouer de la mémoire pour le pointeur nums dans ce cas.

Il serais intéressant de noter qu'il n'y a pas d'erreur de segmentation pour le cas de l'attribution de plusieurs valeurs à la variable int (vous n'êtes pas référence à toute pointeur non valide ici).


SCÉNARIO 2

int nums[] = {5, 2, 1, 4};

Celui-ci n'est pas l'erreur, parce que vous êtes légalement de l'allocation d'un tableau de 4 entiers dans la pile.


SCÉNARIO 3

int *nums = {5, 2, 1, 4};
printf("%d\n", nums);   // print 5

Celui-ci n'a pas d'erreur de segmentation comme prévu, en raison de l'impression de la valeur du pointeur lui-même - non PAS ce que c'est d'être déréférencé (qui n'est pas valide d'accès à la mémoire).


D'autres

C'est presque toujours vouée à l'erreur de segmentation quand vous le coder en dur la valeur d'un pointeur comme ça (parce que c'est le système d'exploitation de la tâche de déterminer ce processus peut accéder à ce que l'emplacement de la mémoire).

int *nums = 5;    // <-- segfault

Ainsi, une règle de base est de toujours initialiser un pointeur vers l'adresse de certains alloué variable, tels que:

int a;
int *nums = &a;

ou,

int a[] = {5, 2, 1, 4};
int *nums = a; 

25voto

Matt McNabb Points 14273

int *nums = {5, 2, 1, 4}; est mal formé code. Il y a un GCC extension qui traite de ce code, le même que:

int *nums = (int *)5;

la tentative de former un pointeur vers l'adresse de mémoire 5. (Cela ne semble pas être une extension utile pour moi, mais je suppose que le développeur de la base de la veut).

Pour éviter ce comportement (ou au moins, obtenir un avertissement), vous pouvez compiler en mode standard, par exemple, -std=c11 -pedantic.

Une autre forme de code valide serait:

int *nums = (int[]){5, 2, 1, 4};

qui points à une mutable littérale de la même durée de stockage comme nums. Cependant , l' int nums[] version est généralement préférable, car il utilise moins d'espace de stockage, et vous pouvez utiliser sizeof pour détecter combien de temps le tableau est.

12voto

Gopi Points 17042
int *nums = {5, 2, 1, 4};

nums est un pointeur de type int. Donc, vous devriez faire de ce point de certains valide emplacement de la mémoire. num[0] vous essayez de déréférencement de certains emplacement de mémoire vive et d'où l'erreur de segmentation.

Oui le pointeur est tenue de valeur 5 et que vous essayez de déréférencer ce qui est un comportement indéfini sur votre système. (Ressemble 5 n'est pas valide emplacement de la mémoire sur votre système)

Alors que

int nums[] = {1,2,3,4};

est une déclaration valide où vous dites nums est un tableau de type int et la mémoire est allouée sur la base du nombre d'éléments transmis lors de l'initialisation.

10voto

Fahad Points 152

En assignant {5, 2, 1, 4}

 int *nums = {5, 2, 1, 4};
 

vous affectez 5 à nums (après une conversion de type implicite d’int pour pointeur à int). Le déréglage effectue un appel d'accès à l'emplacement de la mémoire à 0x5 . Votre programme n’a peut-être pas accès à cela.

Essayer

 printf("%p", (void *)nums);
 

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