[C++] Pointeur char*, priorité d'opérateur

Fermé
the_spectre Messages postés 1 Date d'inscription dimanche 25 février 2001 Statut Membre Dernière intervention 15 septembre 2006 - 15 sept. 2006 à 17:36
mamiemando Messages postés 33401 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 28 novembre 2024 - 24 juin 2007 à 21:34
ya tu quelqu'un qui peut m'aider a mettre la ligne dans le while sur plusieurs ligne. J'ai essayé plusieurs chose mais a matin on dirait qui a rien qui marche. :P

voici le code : (pDst et pSrc sont des pointeurs de char)

bool bStop = false;

while(!bStop || *pSrc)
{
bStop = (*pDst++ = *pSrc++) == _T('\0');
}

*pDst++ = _T('\0');

6 réponses

mamiemando Messages postés 33401 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 28 novembre 2024 7 804
16 sept. 2006 à 02:28
Tu peux interrompre une boucle avec l'instruction break :
bool bStop = false;
while(!bStop){
  if(*pSrc) break;
  bStop = (*pDst++ = *pSrc++) == _T('\0');
}
*pDst++ = _T('\0');

Bonne chance
0
Je me suis retrouvé ici par hasard :) Le sujet est passé depuis pas mal de temps mais je tenais à "hurler" face à la solution donnée.

Je ne pense pas que cette solution soit celle recherchée. Là c'est créer une ligne de plus pour le plaisir d'en créer une de plus. De plus, en algorithmie, les ruptures de boucles dans le bloc n'existent pas (ça correspond à du goto et c'est très mauvais de programmer ainsi).

La ligne qu'il fallait éclater est : bStop = (*pDst++ = *pSrc++) == _T('\0');

(il a bien dit à l'intérieur du while ;-) )


J'ai un peu de mal avec les post et pré incrémentations (je les évite un maximum à l'intérieur d'une expression qui est tout sauf simple -pas de problème dans une instruction d'affichage ou d'affectation toute simple et non combinée-) donc je vais juste donner une ébauche de solution :

*pDst++ = *pSrc++
if(*pDst == _T('\0')) bStop = true; else bStop = false;

L'opérateur ++ (ici en post-incrémentation) a une priorité plus forte que le *. Donc, en utilisant les parenthèses on détermine que :

*pSrc++ c'est le même que *(pSrc++).
*pDst++ c'est le même que *(pDst++).

Pourtant, si on se réfère à la définition de la post incrémentation on trouve que l'incrémentation est effectuée après le calcul de l'expression. Par exemple :

int a=5;
printf("%d", a++); // imprime 5 (post incrémentation. "a" vaudra 6 après l'instruction d'affichage).
printf("%d", ++a); // imprime 6 (pré-incrémentation)

Ce qui voudrait dire qu'avant l'affectation, le pointeur est porté sur pSrc et non pas sur pSrc+1. Je me trompe ? (sinon on aurait écrit *++pSrc, qui revient à écrire *(++pSrc) ou *(pSrc+1), ce qui n'est pas notre cas à cause de la post incrémentation.)

Donc ça reviendrait à faire :

*pDst = *pSrc;
pDst++ ;
pSrc++ ;

(Si je comprends l'intention du code, c'est de copier des valeurs sources vers des espaces mémoire destination, parallèlement... Une copie pure et simple de valeurs de pointeurs. Si ce n'est pas ça c'est que je me suis planté).
0
Il me semblait bien, je me suis planté ! Toutes mes excuses.

L'erreur se situe au niveau de l'affirmation des priorités des opérateurs. Le tableau sur lequel je m'étais basé n'était pas complet et ne présentait pas les opérateurs unaires (pour ça que je ne voyais pas l'opérateur de pointeur que j'ai confondu avec celui de multiplication).

Ils ont donc la même priorité que les opérateurs d'incrémentation/décrémentation et, à même priorité, l'évaluation se fait de droite à gauche ; cela dit ça reste une post incrémentation et donc fidèle à sa réputation (parenthèses ou pas ça ne change rien dans ce cas précis, l'incrémentation est effectuée après).

Minute je teste ainsi on sera fixés...


... Ok, j'ai testé :

char *a = "ABCDEF";
printf("%c", *++a); // Affiche "B" (pré incrémentation)
printf("%c", *a++); // Affiche "A" (post incrémentation, le pointeur "a" n'a pas été incrémenté avant affichage).

(Bien entendu, comme l'exemple sur ma réponse précédente, les printf() ont été lancés un par un, sinon on obtient évidemment "B" dans les deux cas et la variable "a" contiendra l'adresse où est situé le caractère "C".)

Ma précédente solution tient donc toujours debout mais oubliez le passage sur la priorité des opérateurs.

Conclusion : A utiliser avec BEAUCOUP de précaution. Je fais du C depuis pas mal de temps et je n'ai toujours pas la maîtrise de ces pratiques farfelues. On n'est jamais certain de la définition de "expression" et vous pouvez vous en aperçevoir si vous essayez d'imprimer a, *a, *a++ et *++a dans un seul printf("%s%c%c%c"). Ca donne CDEF - C - B - B. Là si quelqu'un voulait bien m'expliquer je suis preneur ^^

Je tente : Si on estime qu'il imprime de droite à gauche : Il incrémente le pointeur "a" (il pointe vers la valeur "B" => pré incrémentation). Ok ensuite il fait *a++, ce qui imprime "B" mais incrémente le pointeur après affichage (post incrémentation) qui pointe maintenant vers la valeur "C" et... ok, ça tient la route. printf fonctionne de droite à gauche, je ne le savais pas.
0
mamiemando Messages postés 33401 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 28 novembre 2024 7 804
22 juin 2007 à 21:13
En fait la question était mal posée je viens de capter que ce n'était pas le while qu'il voulait éclater mais ce qu'il y avait dans le bloc while (en tout c'est pour ça qu'à l'époque j'avais mis un break).

Pour revenir à ta remarque sur les ruptures de boucles je suis plus ou moins d'accord. Un break en début de boucle agit comme un test supplémentaire dans le while. Les "continue" entreraient également dans ce contexte mais sont équivalent à mettre ce qu'il suit dans un test "if". Bref on est loin du goto et parfois un continue et un break bien placé ne nuisent pas à la lisibilité ou à l'efficacité du code.

Dans son cas il a voulu manifestement faire une sorte de strcpy.
Voici une manière plus élégante de l'écrire
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

void my_strcpy(char *dst,char *src){
    while(*src != '\0'){
        *dst = *src;
        ++src;
        ++dst;
    }
}

int main(){
    char *src = "tapir";
    char *dst = (char *)calloc((strlen(src)+1),sizeof(char));
    my_strcpy(dst,src);
    printf("src = %s\n",src);
    printf("dst = %s\n",dst);
    free(dst);
    return 0;
}

Ce qui donne bien :
(mando@polgara) (~) $ gcc -W -Wall plop.c
(mando@polgara) (~) $ ./a.out
src = tapir
dst = tapir

La fonction my_strcpy peut aussi s'écrire ainsi
void my_strcpy2(char *dst,char *src){
    while(*src != '\0') *dst++ = *src++;
}

ou encore
void my_strcpy3(char *dst,char *src){
    for(;*src != '\0';++src,++dst) *dst = *src;
}

Pour revenir aux histoires de pré et post incrémentation, la différence est simple. Une post incrémentation est évaluée après exécution de l'instruction, un pré incrémentation avant. Quand tu incrémentes une variable tu peux écrire indifferemment l'une ou l'autre.
for(i=0;i<10;++i) printf("%d\n",i);
for(i=0;i<10;i++) printf("%d\n",i);

Par contre l'exemple my_strcpy2 est un exemple clair ou il faut bien distinguer les deux :
1) on récupère la référence de *dst
2) on la passe à l'opérateur =
3) on récupère la référence de *src
4) on rentre dans le code de l'opérateur = : on affecte la valeur de *dst à *src
5) on post incrémente dst
6) on post incrémente src

Typiquement si tu préincrémentes tu vas louper le premier caractère de src et de dst.
void my_strcpy_bug(char *dst,char *src){
    while(*src != '\0') *++dst = *++src;
}

Dans ce cas là tu vas avoir
dst = "\0apir\0" ce qui à l'affichage donnera une chaîne vide.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

void my_strcpy(char *dst,char *src){
    while(*src != '\0') *++dst = *++src;
}

int main(){
    unsigned int i;
    char *src = "tapir";
    char *dst = (char *)calloc((strlen(src)+1),sizeof(char));
    my_strcpy(dst,src);
    printf("src = %s\n",src);
    printf("dst = %s\n",dst);

    for(i=0;i<strlen(src);++i) printf("dst[%i]='%c'\n",i,dst[i]);
    free(dst);
    return 0;
}

donne bien :
(mando@polgara) (~) $ ./a.out
src = tapir
dst =
dst[0]=''
dst[1]='a'
dst[2]='p'
dst[3]='i'
dst[4]='r'

En espérant que tout ceci a éclairé ta lanterne ;)
0
Génial mamiemando ! ;-)

Ca fait plaisir de discuter avec un membre persistant de ce forum. :-)

Plus clairement expliqué que ça on ne pouvait pas faire. Ce sera utile à beaucoup de programmeurs qui passeront par là. Bravo !
0
Posotaz Messages postés 489 Date d'inscription samedi 23 juin 2007 Statut Membre Dernière intervention 19 juin 2011 225
23 juin 2007 à 00:12
(Pour la peine je me suis inscrit ^^) Désolé pour ce post inutile mais je ne savais plus modifier le message plus haut.
0

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
lami20j Messages postés 21331 Date d'inscription jeudi 4 novembre 2004 Statut Modérateur, Contributeur sécurité Dernière intervention 30 octobre 2019 3 569
23 juin 2007 à 03:02
Salut,

ou encore
void strcpy4 (char *src, char *dst){
    while ((*dst++ = *src++) != '\0')
        ;
}
ou encore
void strcpy5(char *src, char *dst){
    while (*dst++ = *src++)
        ;
}
0
mamiemando Messages postés 33401 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 28 novembre 2024 7 804
24 juin 2007 à 21:34
Pas de soucis ;)
0