2012-08-31 5 views
2

Je possède ce code:ne peut pas comprendre pourquoi ce comportement


    char *name[] = { "a1", "b2", "c3", "d4" }; 
    printf("%s\n", *name); //the critical line 

liés à critical line:

Sous cette forme, la sortie est simple: a1. Si je remplace le critical line avec:

printf("%s\n", ++*name);

alors la sortie est 1. Je pense que jusqu'à présent tout est bon.

La prise en compte du fait que name est un pointeur sur la première chaîne de caractères, respectivement "a1", je remplace le critical line avec:

printf("%s\n", ++name);

dans l'espoir que je vais "b2" résultat en sortie. Mais j'obtiens cette erreur:

../src/test.c:32: error: lvalue required as increment operand.

Question: Je ne comprends pas pourquoi ++*name est légal - name est un pointeur sur la première chaîne de caractères - et ++name est pas. À mon avis, le ++name devrait déplacer le name à la prochaine chaîne de caractères. Quelqu'un peut-il m'expliquer où est le manque de compréhension?

Répondre

3

Lorsque vous écrivez ++name, le tableau name est converti en un pointeur au premier élément du tableau. Le résultat de cette conversion n'est pas une lvalue, et il ne peut pas être modifié avec ++ ou autrement. Vous pourriez plutôt écrire name+1, ce qui imprimerait la bonne chose. Lorsque name est un tableau, il n'y a aucun moyen de le modifier pour faire référence à autre chose que ce tableau [*].

Il faut aussi considérer:

char **p = name; // OK, `name' converts to pointer 
++p;    // OK, `p' is an lvalue 
++(p+1);   // not OK, `p+1' is not an lvalue 
++((char**)p);  // not OK, `(char**)p' is not an lvalue 

++*name;   // OK, `*name' is an lvalue 

En gros, un "lvalue" est une expression qui fait référence à un objet, alors qu'un "non lvalue" est une expression qui a une valeur. La différence entre un objet et une valeur, c'est qu'un objet est un endroit pour stocker des valeurs (enfin, une valeur à la fois). Les valeurs ne peuvent jamais être modifiées, les objets peuvent parfois le faire.

Chaque fois que vous avez une sous-expression qui est une lvalue mais dont la valeur actuelle est nécessaire, l'objet est lu/chargé/ce que vous voulez appeler.En C++, cela s'appelle une "conversion de lvalue en rvalue", je ne me souviens pas si cela s'appelle quelque chose dans C autre que "l'évaluation de la sous-expression".

[*] vous pouvez l'ombrer avec une autre variable name dans une portée interne, qui fait référence à autre chose. Mais cela ne modifie toujours pas le name extérieur, juste temporairement le cacher.

+0

Voir mon commentaire à la réponse de @ Kerrek. – artaxerxe

2

name est un tableau, donc, sauf si vous l'utilisez comme opérandes des opérateurs sizeof ou &, il est évalué comme un pointeur sur l'élément initial du tableau et objet est pas une lvalue.

En conséquence, vous ne pouvez pas modifier name directement, avec un opérateur tel que ++ (rappelez-vous que l'opérateur d'incrément de suffixe a besoin d'une lvalue modifiable en tant qu'opérande). Sinon, vous pouvez utiliser un pointeur temporaire (p dans l'exemple suivant).

#include <stdio.h> 

const char *name[] = { "a1", "b2", "c3", "d4" }; 
const char **p = name; 
printf("%s\n", *p); /* Output: a1 */ 
*++p; /* or *p++ */ 
printf("%s\n", *p); /* Output: b2 */ 
+0

Oui, je suis entièrement d'accord. Je voulais juste souligner que «j'ai besoin d'être une lvalue modifiable pour effectuer cette opération. NB: Je l'ai enlevé. – md5

1

Tout d'abord, assurez-vous que vous êtes totalement satisfait du fait suivant: Un tableau n'est pas un pointeur.

Deuxièmement, que se passe-t-il dans un name? Le tableau se désintègre en un pointeur vers le premier élément. Après la désintégration, l'expression name a le type char ** (pointeur vers le premier élément d'un tableau de char*.) Cependant, l'expression décomposée est un rvalue Vous ne pouvez pas le modifier! Et bien sûr, cela n'a aucun sens de modifier un pointeur qui est un pointeur vers un tableau fixe.

par conséquent, vous ne pouvez pas augmenter directement le pointeur qui est le résultat de la décomposition name, pas plus que vous pouvez dire ++5 ou ++foo() (où foo retourne en valeur un type primitif) [ whoops, c'était une concession destinée à C++]

Ce que vous pouvez dire est ceci:

char ** np = name; 
++np; 
printf("%s", *np); 

Cela a le même effet que l'impression name[1], mais maintenant vous aussi une variable qui est titulaire d'un pointeur sur le deuxième élément de tableau.

+0

"l'expression décroissante est un rvalue" - ou en terminologie standard C, c'est le "résultat d'une expression". Stupide C terminologie standard. De même, en C, un appel de fonction 'foo()' ne peut retourner une lvalue. –

+0

Encore plus bizarre, après avoir exécuté votre code, j'obtiens une chaîne binaire, comme ceci: $ ' * @ au lieu de '" b2 "'. Quelle est l'explication? – artaxerxe

+0

@artaxerxe: Je ne peux pas reproduire cela: http://ideone.com/S63K7.Peut-être qu'il y a quelque chose d'autre qui ne va pas avec le code que vous avez utilisé. –

1

Alors que le nom indique l'adresse du premier élément, le nom n'est pas type char *, mais char *[4]. Par conséquent,

Incrémenter un pointeur signifie toujours ajouter la taille des données vers lesquelles il pointe. Donc, après l'incrémentation, il pointe derrière l'ensemble du tableau. Tout comme si vous avez

int i, a; 
    int *p = &i; 
    p++; 

p va maintenant pointer derrière i. Il pointera vers un, si le compilateur a décidé de mettre un derrière je.

Notez également que votre tableau contient seulement 4 pointeurs, pas 4 chaînes. Comme ci-dessus, c'est le choix des compilateurs où ces chaînes sont réellement. Ainsi, la fin de la première chaîne n'est pas nécessairement à côté du début de la deuxième chaîne. Surtout si vous affectez d'autres valeurs (adresses de chaînes) à nommer [1] plus tard. Par conséquent, vous pouvez attribuer un nom à char **, mais ne devez pas convertir le numéro en char *. L'incrémentation de la première pointe sur le second élément (second char * pointeur).

Questions connexes