les pointeurs ou les emp

Le
heron maladroit
/*d'après K&R 2 : "there is one difference between an array name and a
pointer that must be kept in mind. A pointer is a variable , so pa = a
and pa++ are legal. But an array name is not a variable. constructions
like a=pa and a++ are illegal. Je veux juste avoir une idée des
messages d'erreur relatifs aux pointeurs pour l'instant au niveau des
chaines de caractères. */

#include <stdio.h>
int main(void)
{
char Chaine[10] = "abc" ;
char *Chaine2[] = {"abc", "def" }; /* 2 pointeurs sur Char */

printf("%s", Chaine) ; /* A : OK */
printf("%s", *Chaine) ; /* B : warning: format ‘%s’ exp=
ects type
‘char *’, but argument 2 has type ‘int’ .=
.. Segmentation fault :
*/
printf("%s", *(Chaine+0) ) ; /* C : warning: format ‘%s’=
expects
type ‘char *’, but argument 2 has type ‘intâ€=
™ Segmentation
fault : */
printf("%c", *(Chaine+0) ) ; /* D: OK */
printf("%s", Chaine+0 ) ; /* E : OK */
printf("%s", Chaine+1 ) ; /* F : OK */
printf("%s", &Chaine ) ; /* G : warning: format ‘%s’ exp=
ects type
‘char *’, but argument 2 has type ‘char (*)[10]â=
€™ OK */
printf("%s", &Chaine[0] ) ; /* H: OK */

/*CHAINE 2 */

printf("%s", &Chaine2) ; /* I : warning: format ‘%s’ exp=
ects type
‘char *’, but argument 2 has type ‘char * (*)[2] ..=
. résultat
indéterminé*/
printf("%o", &Chaine2) ; /* J : warning: format ‘%o’ exp=
ects type
‘unsigned int’, but argument 2 has type ‘char * (*)=
[2] résultat :
27776137614 */
printf("%c", *Chaine2[1]) ; /* K : warning: format ‘%c’=
expects
type ‘int’, but argument 2 has type ‘char **â€=
™ résultat : vide */
printf("%s", Chaine2[0]) ; /* L : OK */
printf("%s", Chaine2) ; /* M : warning: format ‘%s’ expe=
cts type
‘char *’, but argument 2 has type ‘char **’=
resultat : ����E#abc */

printf("") ;
return 0 ;
}

/*
A :
Resultat : abc

Je comprends ici que Chaine est un tableau (ou une chaine constante
pour être plus précis?).
Le paragraphe suivant l'extrait du K&R précédent dis que lorsqu'u=
n nom
de tableau est envoyé à une fonction, c'est l'emplacement du
1er élément qui est envoyé et à l'intérieur de =
cette fonction, cet
argument est une variable locale

Je ne comprends pas que pour printf avec %s, * ou le contenu de Chaine
ne soit pas appelé mais ce qui me semble être l'adresse en mÃ=
©moire de
Chaine comme lorsqu'on passe char *pointeur ; pointeur = Chaine ou
pointeur = &Chaine[0]. Le point H va dans ce sens

Ce premier cas est celui qui me fait toujours réfléchir et peut m=
e
donner une erreur . Surtout que dans ce cas scanf ajoute de l'insulte
à la blessure car : scanf("%s", Chaine) au lieu de scanf("%s",
&Chaine) comme on peut s'y attendre ;

char Chaine[10] ;
printf("Entrez une chaine : ") ;
scanf("%s", &Chaine[0]) OK MAIS scanf("%s", &Chaine) affiche
warning: format ‘%s’ expects type ‘char *’,=
but argument 2 has type
‘char (*)[10]’ mais la saisie se fait quand même

B :
Resultat : Segmentation fault

Le warning dit que le 2ème argument est de type int.
Ce n'est pas la première fois que j'ai ce message. Est-ce que je dois
comprendre que dans printf, %s est le 1er argument, et *Chaine est le
second? Alors comme le pointeur *Chaine n'a pas été déclar=
é, *Chaine
est de type int par défaut ?
Ensuite, le K et R dis pour pa = a un tableau constant a[i] peut
aussi s'écrire *(a+i) pourquoi pas *a (soit le contenu de a) avec %s
affiche une chaine?

C :
Resultat : Segmentation fault

Resultat identique si *(Chaine+1), *(Chaine+2), *(Chaine+3) etc
*(Chaine+9)
L'argument %s semble ici bloquer le déroulement du programme. Ca
paraît cohérent avec %c dans printf :

D :
Resultat : a

E:
Resultat : abc

F:
Resultat : bc

G :
Resultat : abc

1ere fois que je vois ce warning, je dois avouer que je n'avais pas
penser à appeler un tableau d'une chaine constante ou d'entier avec
l'adresse &. Le '&' est à mes yeux associé aux pointeurs. A ma gr=
ande
surprise, le code fonctionne. En tant que débutant avec le langage C,
je trouve intéressant le warning: il montre bien qu'un tableau est un
pointeur vers le type de données dans lequel il a été dÃ=
©finit/déclaré
ici : char (*)[10], càd 10 pointeurs vers des données de type
caractères?

H:
Resultat : abc


I :
Resultat : des carrés s'affichent : �`� et abc

Je suppose que c'est la représentation d'emplacements mémoire (vo=
ir I)

J:
Resultat : 27776137614

Le message confirme que ce sont bien 2 pointeurs sur char
Je suppose que c'est l'adresse de *Chaine2 représentée sous la fo=
rme
d'un entier?

K :
Resultat : abc

Je ne comprends pas l'erreur "invalid initializer". Pourtant le code
fonctionne même avec : *Chaine[0], *Chaine[20]

L :
Resultat : abc

error: invalid initializer
error: expected expression before ‘]’ token
error: array subscript is not an integer
warning: format ‘%c’ expects type ‘int’, bu=
t argument 2 has type ‘char
*’

Et pourtant ça fonctionne.
Invalid initializer est dans J alors je suppose que la réponse
précédente satisfait la question présente.
expected expression : je comprends que je dois mettre un indice
entre [ ] . Dans J, les indices ne me semblent pas utiles et ici, ils
seraient utiles?
array subscript is not an integer : je ne comprends pas
warning : format %c expects type int : selon moi, %c attend un
caractère et non pas un int

M:
Resultat inédit : ����E#abc
warning: format ‘%s’ expects type ‘char *’,=
but argument 2 has type
‘char **’
Je ne suis pas sûre de comprendre.

-o-

Je vais arrêter là étant arrivé à la moitié d=
e l'alphabet et les
besoins physiologiques se font pressantsurtout que le tableau
multidimensionnel chaine2 permet encore d'autres combinaisons

*/
Vidéos High-Tech et Jeu Vidéo
Téléchargements
Vos réponses
Gagnez chaque mois un abonnement Premium avec GNT : Inscrivez-vous !
Trier par : date / pertinence
-ed-
Le #22221351
On 6 juin, 23:19, heron maladroit
        char Chaine[10] = "abc" ;
        char *Chaine2[] = {"abc", "def" }; /*  2 pointeurs su r Char ...  */



Les chaines étant par défaut, non modifiables en C, on préfère rest er
cohérent et portable comme ceci :

char const *Chaine2[] = {"abc", "def" }; /* 2 pointeurs sur
char ... */

D'autre part, il n'y a pas de type 'Char' en C standard. C'est 'char'.

        printf("n%s", Chaine) ; /* A : OK */



Le 'n' indique la fin de ligne, il se place donc ... en fin de
ligne !

printf ("%sn", Chaine); /* A : OK */

en effet, il force l'émission des caractères. Sinon, le caractères
placés après risquent de ne pas être émis ...

        printf("n%s", *Chaine) ; /* B :  warning: format ‘%s ’ expects type
‘char *’, but argument 2 has type ‘int’   ...   Segmentation fault :
*/



Ben oui, normal. *Chaine ou *(Chaine + 0) ou encore Chaine[0], désigne
un caractère (type char), qui est automatiquement converti en int. Cet
int n'a quasiment aucune chance d'être une adresse valide ("%s"
attends l'adresse d'une chaine de caractères). Tu veux peut être "%c"
pour voir le caractère ?


        printf("n%s", *(Chaine+0) ) ; /* C : warning: format ‘ %s’ expects
type ‘char *’, but argument 2 has type ‘int’ ...   Segmentation
fault : */



Même problème

        printf("n%c", *(Chaine+0) ) ; /* D: OK  */
        printf("n%s", Chaine+0 ) ; /* E : OK */
        printf("n%s", Chaine+1 ) ; /* F : OK */

        printf("n%s", &Chaine ) ; /* G : warning: format ‘%s ’ expects type
‘char *’, but argument 2 has type ‘char (*)[10]’ ... OK */



Non, pas OK. Le comportement est indéfini. On ne fait pas ça.

        printf("n%s", &Chaine[0] ) ;      /* H: OK */

        /*CHAINE 2 */

        printf("n%s", &Chaine2) ; /* I : warning: format ‘%s ’ expects type
‘char *’, but argument 2 has type ‘char * (*)[2] ... résultat
indéterminé*/



Oui, carrément n'importe quoi ...

        printf("n%o", &Chaine2) ; /* J : warning: format ‘%o ’ expects type
‘unsigned int’, but argument 2 has type ‘char * (*)[2] ... résult at :
27776137614 */



Pareil. Tu programmes au hasard ? Tu testes le compilateur ? Pourquoi
"%o" ? Tu trouves que l'octal est adapté ?

        printf("n%c", *Chaine2[1])  ; /* K : warning: format ‘%c’ expects
type ‘int’, but argument 2 has type ‘char **’... résultat : vid e */



Oui, portnawak ...

        printf("n%s", Chaine2[0]) ;  /* L : OK */

        printf("n%s", Chaine2) ; /* M : warning: format ‘%s’ expects type
‘char *’, but argument 2 has type ‘char **’ ...resultat : E#abc * /



Non, résultat indéfini, c'est tout ...

Il manque :

printf("%sn", Chaine2[1]) ; /* O : OK */



        printf("nn") ;



Pas très utile si on prends soin de mettre le 'n' à la fin de l
ligne, comme il se doit ...

        return 0 ;

}

/*
A :
Resultat : abc

Je comprends ici que Chaine est un tableau (ou une chaine constante
pour être plus précis?).



C'est un tableau de char initialisé modifiable. Sa valeur est
{'a','b','c',0,0,0,0,0,0,0}

Le paragraphe suivant l'extrait du K&R précédent dis que lorsqu'un no m
de tableau est envoyé à une fonction, c'est l'emplacement du
1er élément qui est envoyé...



Oui, c'est à dire une adresse. L'adresse du premier élément du tablea u
si c'est le nom seul (<nom> ou <nom> + 0)

et à l'intérieur de cette fonction, cet
argument est une variable locale



Oui, comme tous les arguments. Ici, c'est une variable locale de type
'adresse de char', dont un pointeur sur char (char *s, par exemple).

Je ne comprends pas que pour printf avec %s, * ou le contenu de


Chaine
ne soit pas appelé mais ce qui me semble être l'adresse en mémoire de
Chaine comme lorsqu'on passe char *pointeur ;  pointeur = Chaine ou
pointeur = &Chaine[0]. Le point H va dans ce sens



Comme expliqué précédemment, "%s" attend l'adresse d'un tableau de
char initialisé avec une chaine, donc terminée par un 0. Si on lui
passe autre chose (*Chaine, par exemple), le comportement est
indéfini.

Ce premier cas est celui qui me fait toujours réfléchir et peut me
donner une erreur .



Y'a pas trop à réfléchir. Une fois qu'on sait ce qu'attend é%s", to ut
est clair. Et c'est pareil avec scanf() (sauf que c'est la taille qui
importe et non le contenu).

Surtout que dans ce cas scanf ajoute de l'insulte
à la blessure car : scanf("%s", Chaine) au lieu de scanf("%s",
&Chaine) comme on peut s'y attendre ;



Pourquoi & ? On a expliqué en long en large et travers que quand on
passait le nom d'un tableau à une fonction, celui-ci était convertit
en adresse du premier élément. On peut donc mettre :

scanf("%s", Chaine)
ou
scanf("%s", Chaine + 0)
ou
scanf("%s", &Chaine[0])
si on aime la complication.

Attention, cette saisie est dangereuse et pas paratique, car elle
prend tout jusqu'au premier séparateur (espace, tab ou fin de ligne).


        char Chaine[10] ;
        printf("nEntrez une chaine : ") ;
        scanf("%s", &Chaine[0]) OK  MAIS scanf("%s", &Chaine) a ffiche
warning: format ‘%s’ expects type ‘char *’, but argument 2 has ty pe
‘char (*)[10]’ mais la saisie se fait quand même



A tes risques et périls. Le comportement est indéterminé. On a déj à
fort affaire avec les comportements normaux. Ne pas perdre de temps
avec les comportements non définis ... Enfin, tu as voulu faire du
C...

B :
Resultat : Segmentation fault

Le warning dit que le 2ème argument est de type int.
Ce n'est pas la première fois que j'ai ce message. Est-ce que je dois
comprendre que dans printf, %s est le 1er argument, et *Chaine est le
second?



Ben oui. C'est pas évident ?

Alors comme le pointeur *Chaine n'a pas été déclaré, *Chaine
est de type int par défaut ?



Attention. Le rôle de * change selon le contexte.

Il sert

- soit d'opérateur de multiplication quand il est placé entre 2
opérandes : a * 2, a * b etc.
- soit de déclarateur de pointeur dans une déclaration de variable :
int *p; char const *s="hello"
- soit d'opérateur de déréférencement quand il est appliqué à u n
pointeur : inc c = *s, f (*s) etc.

ici, c'est le 3 ème cas. *Chaine est *(Chaine + 0), c'est à dire
Chaine[0]. C'est donc un caractère qui n'a rien à voir avec une
adresse. Le type est int, parce que l'appel de fonction avec un
paramètre de type char est convertit en int.

Ensuite, le K et R dis pour pa = a ...un tableau constant a[i] peut
aussi s'écrire *(a+i) pourquoi pas *a (soit le contenu de a) avec %s
affiche une chaine?



Parce que dans ce cas, *a (valeur pointée par a) désigne un caractère
et non une adresse ...

C :
Resultat : Segmentation fault

Resultat identique si *(Chaine+1), *(Chaine+2), *(Chaine+3) etc...
*(Chaine+9)
L'argument %s semble ici bloquer le déroulement du programme. Ca
paraît cohérent avec %c dans printf :

D :
Resultat : a

E:
Resultat : abc

F:
Resultat : bc

G :
Resultat : abc



Bah oui, normal. "%c" attend un int qui représente la valeur d'un
caractère. Si on lui donne ce qu'il attend, tout va bien...

1ere fois que je vois ce warning, je dois avouer que je n'avais pas
penser à appeler un tableau d'une chaine constante ou d'entier avec
l'adresse &. Le '&' est à mes yeux associé aux pointeurs.



Peut importe. La réalité est que & est un opérateur qui retourne une
valeur et un type. La valeur est l'adresse d'un objet. Le type
retourné est 'pointeur sur le type de l'objet'. Appliqué à un tableau ,
ça retourne l'adresse du tableau (comme en C, le premier élément d'un
tableau se trouve en [0], la valeur est la même qu'avec le nom du
tableau), mais le type est différent. Ailiet de retourner le type
'pointeur sur un élément du tableau', on retourne 'pointeur sur le
tableau' ce qui est très différent, et peut avoir des conséquences
terribles si on utilise un offset : Chaine + 1 n'a pas du tout la même
valeur que &Chaine+1.

A ma grande
surprise, le code fonctionne.



Oui, la valeur est la même. "Ça tombe en marche" ... Mais c'est faux
pour les raisons déjà expliquées.

En tant que débutant avec le langage C,
je trouve intéressant le warning: il montre bien qu'un tableau est un
pointeur vers le type de données dans lequel il a été définit/d éclaré
ici : char (*)[10], càd 10 pointeurs vers des données de type
caractères?



Oui, les warnings essayent d'aider à comprendre les subtilités du C...
Mais pourquoi tu ne fais pas du Pascal ...

H:
Resultat : abc

I :
Resultat : des carrés s'affichent :   `  et abc

Je suppose que c'est la représentation d'emplacements mémoire (voir I )

J:
Resultat : 27776137614



Des comportements indéfinis ...

Le message confirme que ce sont bien 2 pointeurs sur char
Je suppose que c'est l'adresse de *Chaine2 représentée sous la forme
d'un entier?



Oui, en octal... Quelle idée. Le bon format pour une adresse est "%p"
et il faut forcer le paramètre 'adresse' en (void *)

K :
Resultat : abc

Je ne comprends pas l'erreur "invalid initializer". Pourtant le code
fonctionne même avec : *Chaine[0], *Chaine[20]...



certainement pas. C'est printf("n%c", *(Chaine2[1])) . Attention à la
préséance des opérateurs ...

L :
Resultat : abc

error: invalid initializer
error: expected expression before ‘]’ token
error: array subscript is not an integer
warning: format ‘%c’ expects type ‘int’, but argument 2 has type ‘char


Et pourtant ça fonctionne.
Invalid initializer est dans J alors je suppose que la réponse
précédente satisfait la question présente.
expected expression ... : je comprends que je dois mettre un indice
entre [ ] . Dans J, les indices ne me semblent pas utiles et ici, ils
seraient utiles?
array subscript is not an integer : je ne comprends pas
warning : format %c expects type int ... : selon moi, %c attend un
caractère et non pas un int ...



Nan. "%c" attend un int représentant la valeur d'un caractère.


M:
Resultat inédit : E#abc
warning: format ‘%s’ expects type ‘char *’, but argument 2 has ty pe
‘char **’
Je ne suis pas sûr de comprendre.



Il n'y a rien de prévu nativement pour représenter un tableau de
pointeurs.
Antoine Leca
Le #22221731
heron maladroit écrivit :
char Chaine[10] = "abc" ;

printf("n%s", Chaine) ; /* A : OK */
Resultat : abc

Je comprends ici que Chaine est un tableau (ou une chaine constante
pour être plus précis?).
Le paragraphe suivant l'extrait du K&R précédent dis que lorsqu'un nom
de tableau est envoyé à une fonction, c'est l'emplacement du
1er élément qui est envoyé... et à l'intérieur de cette fonction, cet
argument est une variable locale

Je ne comprends pas que pour printf avec %s, * ou le contenu de Chaine
ne soit pas appelé mais ce qui me semble être l'adresse en mémoire de
Chaine comme lorsqu'on passe char *pointeur ; pointeur = Chaine ou
pointeur = &Chaine[0].



Bin il va falloir que tu comprennes cela avant de pousser plus loin,
parce que tout en dépend...

Comme rappelé dans l'extrait du K&R, Chaine est un tableau ; il est donc
jugé trop gros pour être passé au sous-programme : pour passer un
paramètre il faut le copier dans un emplacement prédéfini avant l'appel,
puis au début de la fonction le recopier depuis la zone de passage
prédéfinie : cela s'appelle les conventions d'appel, et c'est très
semblable au circuit que font tes valises lorsque tu prends l'avion :
avant de monter dedans il te faut les enregistrer, puis elles sont mises
dans la soute, et te sont restituées à destination : comme c'est très
lourd, on préfère si possible utiliser les bagages de cabine, pardon les
paramètres passés par registre, qui évite le circuit compliqué.
Seulement les paramètres que l'on peut passer par registre sont limités
en taille ; et un tableau n'est pas acceptable: donc on préfère passer
un registre, en l'occurrence un pointeur vers la dite chaîne.

<LEÇON_SUIVANTE>
SAUF QU'en faisant cela, on ne transporte plus réellement la valise,
pardon, le tableau n'est pas réellement copié dans le sous-programme, on
travaille non pas sur une copie locale mais sur le vrai tableau, seul et
unique : cela est la cause profonde des « problèmes de pointeurs » en C:
il faut constamment veiller à ne pas écraser les tableaux que l'on nous
a passé en paramètres : d'où l'intérêt du mot-clé const, qui permet au
compilateur de renforcer notre attention.
</LEÇON_SUIVANTE>


Donc pour en revenir à printf(), il y a une différence fondamentale
entre %i/%o/%c/%p d'un côté, qui imprime la valeur des paramètres passés
comme variables locales pour printf, et %s de l'autre côté, qui imprime
la valeur du tableau global, externe à printf et donc seulement connu de
cette fonction par l'adresse à laquelle il commence.


Le point H va dans ce sens
printf("n%s", &Chaine[0] ) ; /* H: OK */



Mmmm. Il faut dire que dans la plupart des cas, Chaine est équivalent à
&Chaine[0] (les cas où ce n'est pas équivalent sont: derrière sizeof,
derrière &, à gauche de = ou dans l'initialisation d'un objet).


Surtout que dans ce cas scanf ajoute de l'insulte
à la blessure car : scanf("%s", Chaine) au lieu de scanf("%s", &Chaine)
comme on peut s'y attendre ;



scanf() est une fonction différente de printf(), et soumise à d'autre
contraintes : en particulier, pour que scanf("%i",???) fonctionne on ne
peut plus se contenter de la valeur d'une variable _avant_ l'appel à
scanf() dont on se moque éperdument : il faut que scanf() puisse
_modifier_ la variable, et pour cela il est nécessaire de passer son
adresse, autrement dit un pointeur vers sa position, à la fonction scanf
scanf("%i", &i);

Seulement, comme expliqué ci-dessus, avec les chaînes on a déjà passé ce
fameux pointeur, donc pour économiser on utilise directement le pointeur
scanf("%s", &Chaine[0]);
ou, ce qui est équivalent
scanf("%s", Chaine);



scanf("%s", &Chaine) affiche
warning: format ‘%s’ expects type ‘char *’, but argument 2 has type
‘char (*)[10]’



J'espère que tu vois pourquoi maintenant.

mais la saisie se fait quand même



Je ne pense pas. Au mieux, la saisie va s'effectuer en écrasant des
données inutiles sur la pile de main(), et il n'y aura pas de
conséquences graves (mais la chaîne entrée au clavier ne sera pas dans
Chaine) ; au pire, tu vas avoir droit à un SIGSEGV pour avoir écraser
tes propres variables...


printf("n%s", *Chaine) ; /* B : warning: format ‘%s’ expects type
‘char *’, but argument 2 has type ‘int’ ... Segmentation fault : */
Resultat : Segmentation fault



Si tu as bien suivi, c'est la même chose dans l'autre sens : cette
fois-ci tu n'essaye pas d'écrire dans ta pile, mais tu fais croire à
printf() que le caractère 'a' est l'adresse où se trouve la chaîne à
écrire...

Le warning dit que le 2ème argument est de type int.
Ce n'est pas la première fois que j'ai ce message. Est-ce que je dois
comprendre que dans printf, %s est le 1er argument, et *Chaine est le
second?



Oui.


Alors comme le pointeur *Chaine n'a pas été déclaré,



Bin si (sauf que ce n'est pas un pointeur) : l'objet Chaine est déclaré
comme tableau de char, donc lorsqu'on utilise l'opérateur * qui en
extrait le premier élément, on obtient le premier élément du tableau, et
le type du résultat est char.
Au niveau des conventions d'appel, char est trop petit, donc on
l'agrandit en int, ce qui explique le message ci-dessus

Là aussi, on est dans les choses de base concernant les pointeurs (en
l'occurrence, ne pas confondre l'opérateur * avec l'utilisation de *
pour déclarer des pointeurs), il faut absolument que tu maîtrises cela
avant de prétendre aller plus loin.


Antoine
Publicité
Poster une réponse
Anonyme