problème avec une fonction qui renvoie un pointeur de char

problème avec une fonction qui renvoie un pointeur de char - C - Programmation

Marsh Posté le 11-08-2006 à 15:55:53    

bonjour à tous,
j'ai un problème qui me prend la tête depuis hier :
j'ai écrit une petite fonction qui ajoute un '0' à un entier passé en argument s'il est inférieur à 10 et renvoie un pointeur sur une chaîne de char :
 
char *AjouteZero (int entier)
{
   char chaine [3],tmp [3],*p;
 
   if (entier<=9)
   {
      strcpy (chaine,"0" );
      strcat (chaine,itoa (entier,tmp,10));
   }
   else
   {
      strcpy (chaine,itoa (entier,tmp,10));
   }
   p=chaine;
   return (p);
}
 
le problème est que quand j'essaie de récupérer la valeur renvoyée ça veut pas ...
j'essaie comme ça : strcpy (tmp,AjouteZero (8)); où tmp est : char tmp [10] => j'obtient des hiéroglyphes en faisant un printf de tmp
ou bien en faisant directement un printf ("\n%s\n",AjouteZero (8));
 
ce qui m'épate c'est que j'ai une autre fonction que j'utilise depuis des années qui est pareille au niveau des types de variables et ça marche sans problème.
 
merci d'avance pour toute aide.

Reply

Marsh Posté le 11-08-2006 à 15:55:53   

Reply

Marsh Posté le 11-08-2006 à 15:56:53    

L'espace alloué à "chaine" n'existe qu'à l'intérieur de ta fonction.
Utilise malloc() pour pouvoir propager ta variable.
 
Et utilise plutôt snprintf() que itoa(), qui n'est pas standard.


Message édité par Elmoricq le 11-08-2006 à 15:57:36
Reply

Marsh Posté le 11-08-2006 à 16:02:01    

ça c'est de la réponse rapide !
c'est ce que je suspectais, mais ce que je comprends pas c'est que ça marche dans un autre cas :
 
char *litchamp (int position,char lecligne[])
{
 int i,j;
 char lec[1000],*point;
 
 i=1;
 j=0;
 while (i<position)
 {
  while (lecligne[j]!='|' && lecligne[j]!='\0' && lecligne[j]!='\n')
  {
   j++ ;
  }
  j++;
  i++;
 }
 i=j;
 while (lecligne[j]!='|' && lecligne[j]!='\0' && lecligne[j]!='\n')
 {
  j++;
 }
 strncpy (lec,&lecligne[i],j-i);
 lec[j-i]='\0';
   point = lec;
 return point;
}
 
la seule différence c'est que LecLigne est globale, mais elle est recopiée dans lec qui elle est locale à la fonction, donc ça ne devrait pas marcher non plus ...
 
enfin le principal c'est d'avoir la solution, merci Elmoricq

Reply

Marsh Posté le 11-08-2006 à 16:04:33    

3 n'est pas un nombre suffisant pour stocker un entier non-signée dans une chaine de caractères

Reply

Marsh Posté le 11-08-2006 à 16:07:14    

western a écrit :

3 n'est pas un nombre suffisant pour stocker un entier non-signée dans une chaine de caractères


 
c'est pour des nombres de 2 chiffres au max (les jours et les mois en fait) donc avec le '\0' ça fait 3 ...

Message cité 1 fois
Message édité par manu le 11-08-2006 à 16:08:03
Reply

Marsh Posté le 11-08-2006 à 16:11:48    

Juste une question : tu fais ça pour apprendre à manipuler les chaines de caractères (donc dans ce cas programmer toi-même cette fonction est un but en soi), ou bien tu en as juste besoin pour une appli de plus grande envergure ?
 
Dans le deuxième cas, je te conseille d'utiliser tout simplement un snprinft("%02d", ... ) qui fera le boulot très bien sans que tu aies à t'occuper de ce qui se passe derrière.

Message cité 1 fois
Message édité par franceso le 11-08-2006 à 16:12:03

---------------
TriScale innov
Reply

Marsh Posté le 11-08-2006 à 16:12:17    

manu a écrit :

c'est pour des nombres de 2 chiffres au max (les jours et les mois en fait) donc avec le '\0' ça fait 3 ...


qui t'empeche de faire du code propre?

Reply

Marsh Posté le 11-08-2006 à 16:15:01    

manu a écrit :

ça c'est de la réponse rapide !
c'est ce que je suspectais, mais ce que je comprends pas c'est que ça marche dans un autre cas :


 
Sauf erreur, c'est juste un coup de bol. Comportement aléatoire.

Reply

Marsh Posté le 11-08-2006 à 16:21:29    

ça marche à tous les coups, j'ai plusieurs programmes qui tournent toutes les nuits avec cette fonction depuis plusieurs années et y aucun soucis.
 
maintenant c'est vrai que c'est pas logique ...
 
va vraiment falloir que je reprenne tous mes vieux prg, doit y avoir des belles merdes dedans ...
 
merci encore

Reply

Marsh Posté le 11-08-2006 à 16:25:19    

Citation :

char *AjouteZero (int entier)
{
   char chaine [3],tmp [3],*p;
 
   if (entier<=9)
   {
      strcpy (chaine,"0" );
      strcat (chaine,itoa (entier,tmp,10));
   }
   else
   {
      strcpy (chaine,itoa (entier,tmp,10));
   }
   p=chaine;
   return (p);
}


Quelle est l utilité de retourner p ???
p = chaine ne sert a rien ... tu devrais mieux retourner directectement "chaine", et tu pourrais te passer de "p"

Reply

Marsh Posté le 11-08-2006 à 16:25:19   

Reply

Marsh Posté le 11-08-2006 à 16:36:46    

ben on peut pas retourner une chaîne en C ... c'est soit une variable simple (int, char, float, ...) soit un pointeur (sauf erreur ou omission ...)

Reply

Marsh Posté le 11-08-2006 à 17:13:29    

manu a écrit :

ça marche à tous les coups, j'ai plusieurs programmes qui tournent toutes les nuits avec cette fonction depuis plusieurs années et y aucun soucis.


 
[:mlc]
 
En plus, ton char *p = lec, ça permet juste de masquer le warning suivant :

test.c: In function `litchamp':
test.c:32: warning: function returns address of local variable


 
L'affectation à p n'enlève absolument rien au fait que tu retournes l'adresse d'une variable locale. C'est un pur coup de bol si rien n'écrase cet endroit de la mémoire au moment où tu l'utilises.
 
Il aurait fallu écrire (j'en profite au passage pour effectuer quelques modifications cosmétiques) :

Code :
  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <stdlib.h>
  4. char *litchamp(unsigned position,const char *p)
  5. {
  6.     char *champ = NULL;
  7.     for(unsigned i = 0; i < position && p; ++i)
  8.     {
  9.         if ( (p = strchr(p, '|')) != NULL )
  10.             ++p;
  11.     }
  12.    
  13.     if ( p )
  14.     {
  15.         const char *suivant = strchr(p, '|');
  16.         size_t longueur;
  17.         longueur = suivant ? (size_t)(suivant - p) : strlen(p);
  18.         /* La partie qui t'interesse est ici : on alloue de la memoire dynamiquement,
  19.             la memoire ainsi definie par calloc (ou malloc) n'a pas une duree de vie
  20.             limitee a ta fonction
  21.         */
  22.         champ = calloc(longueur + 1, sizeof *champ);
  23.         if ( champ )
  24.             strncpy(champ, p, longueur);
  25.         else
  26.             fprintf(stderr, "Erreur d'allocation" );
  27.     }
  28.     return champ;   
  29. }
  30. int main(void)
  31. {
  32.     const char *ahah = "abcde|fghij|klomnopqrstuv|wxyz0123456789";
  33.     for(unsigned i = 0; i < 10; ++i)
  34.     {
  35.         char *taiste = litchamp(i,ahah);
  36.         if ( taiste )
  37.             printf("Champ %u : %s\n", i, taiste);
  38.    
  39.         /* Contrepartie : puisque le pointeur retourne par litchamp() n'a pas de  
  40.             duree de vie limitee, il faut liberer la memoire ainsi definie. Si on ne le fait
  41.             pas, le programme grossira de plus en plus jusqu'a ce que le systeme mette
  42.             le hola, voire jusqu'a ce que le systeme plante.
  43.             Dans les deux cas, le programme meurt dans d'atroces souffrances (et dans
  44.             le second cas, il emporte le systeme avec lui :o )
  45.         */
  46.         free(taiste);
  47.     }
  48.     return 0;
  49. }


Message édité par Elmoricq le 11-08-2006 à 17:22:29
Reply

Marsh Posté le 11-08-2006 à 17:35:28    

manu a écrit :

ben on peut pas retourner une chaîne en C ... c'est soit une variable simple (int, char, float, ...) soit un pointeur (sauf erreur ou omission ...)


Le type chaine n existe pas en C. La preuve avec le code suivant j ai remis au propre ton code tout simplement -> ca fait la meme chose en trois lignes et sans faire appel a des fonctions de lib externes (a part malloc).
 

Code :
  1. #include <stdlib.h>
  2. char   *AjouteZero(int i)
  3. {
  4.   char  *str;
  5.   str = malloc(3 * sizeof(char));
  6.   str[0] = (i / 10 != 0) ? (char)(i / 10 + 48) : '0';
  7.   str[1] = (char)(i % 10 + 48);
  8.   str[2] = '\0';
  9.   return (str);
  10. }


Regarde bien j ai bien un pointeur au depart (char *str)
j alloue 3 octets pour stocker la chaine de carateres
ensuite quelques manipulations simples basees sur la table ASCII et un cast
si tu fais printf("%s", str); tu as bien ce que tu voulais.
tu peux donc en deduire que la chaine de carateres n est autres qu un pointeur en C.
Si je n ai pas ete clair dit le moi

Reply

Marsh Posté le 11-08-2006 à 17:39:26    

effectivement ç'est mieux ... par contre les modifications cosmétiques, comme tu dis, j'ai pas trop l'habitude ... mais va falloir que je m'y mette. à ma décharge, je n'ai eu qu'une semaine de formation sur le c ansi, on n'a même pas vu les pointeurs ni l'allocation dynamique ...
du coup je programme à la bourrin depuis des années (que avec des tableaux et les pointeurs quand vraiment je peux pas faire autrement, et le malloc que quand y a pas assez de place en mémoire).
 
mais bon la j'arrête, je veux faire bien.
 
sur tes conseils j'ai modifié ma fonction comme ça :
 
char *AjouteZero (int entier)
{
   char *chaine,tmp [3];
 
   chaine=(char *) malloc (3*sizeof (char));
   if (entier<=9)
   {
      strcpy (chaine,"0" );
      strcat (chaine,itoa (entier,tmp,10));
   }
   else
   {
      strcpy (chaine,itoa (entier,tmp,10));
   }
   return (chaine);
}
 
et ça marche nickel. on verra après pour snprintf et autres standardisation et amélioration, faut que j'avance un peu dans la suite du prg.

Reply

Marsh Posté le 11-08-2006 à 17:41:09    

Au fait je vois pas mal d exemple de code dans lesquels il n y a pas de test de valeur de retour lors d utilisation de fonctions provenant de librairies externes ... je ne l ai pas fait juste avant mais il faut toujours tester la valeur de retour d une allocation dynamique de memoire car d une part ca permet d eviter les SEGFAULT et au moins quand on a un probleme on sait d ou ca vient

Reply

Marsh Posté le 11-08-2006 à 17:44:48    

deuxsous a écrit :

Le type chaine n existe pas en C. La preuve avec le code suivant j ai remis au propre ton code tout simplement -> ca fait la meme chose en trois lignes et sans faire appel a des fonctions de lib externes (a part malloc).
 

Code :
  1. #include <stdlib.h>
  2. char   *AjouteZero(int i)
  3. {
  4.   char  *str;
  5.   str = malloc(3 * sizeof(char));
  6.   str[0] = (i / 10 != 0) ? (char)(i / 10 + 48) : '0';
  7.   str[1] = (char)(i % 10 + 48);
  8.   str[2] = '\0';
  9.   return (str);
  10. }


Regarde bien j ai bien un pointeur au depart (char *str)
j alloue 3 octets pour stocker la chaine de carateres
ensuite quelques manipulations simples basees sur la table ASCII et un cast
si tu fais printf("%s", str); tu as bien ce que tu voulais.
tu peux donc en deduire que la chaine de carateres n est autres qu un pointeur en C.
Si je n ai pas ete clair dit le moi


 
ok, c'est ce que j'ai fait aussi, pour le pointeur. ce que je voulais dire c'est qu'on ne pouvait pas retourner un tableau de char (ce que j'appelle une chaîne de caractères).
 
par contre je suis pas trop habitué aux trucs style : (i / 10 != 0) ? (char)(i / 10 + 48) : '0'; j'aime bien qu'il y ait des if, strcpy, etc ...
question d'habitude.
merci à toi

Reply

Marsh Posté le 11-08-2006 à 17:46:13    

deuxsous a écrit :

Au fait je vois pas mal d exemple de code dans lesquels il n y a pas de test de valeur de retour lors d utilisation de fonctions provenant de librairies externes ... je ne l ai pas fait juste avant mais il faut toujours tester la valeur de retour d une allocation dynamique de memoire car d une part ca permet d eviter les SEGFAULT et au moins quand on a un probleme on sait d ou ca vient


 
exact, ça par contre j'en mets un max de tests et tout tracé dans une log, là je voulais juste faire fonctionner le bazarre, je vais blinder après

Reply

Marsh Posté le 11-08-2006 à 17:57:56    

Citation :

j'aime bien qu'il y ait des if, strcpy, etc ...


surtout eviter les strcpy, c est une fonction qui n est pas fiable et qui peut permettre des buffer overflow ... m enfin apres c est au niveau de la securite du programme, je ne pense pas que ca te gene enormement.
 

Citation :

exact, ça par contre j'en mets un max de tests et tout tracé dans une log, là je voulais juste faire fonctionner le bazarre, je vais blinder après


attention tout de meme a l utilisation du if (pour les test), c est l une des instructions les plus gourmandes en ressources, il faut eviter d en abuser et s en passer permet souvent de gagner pas mal de cycle d execution. Mais ca n est la encore qu une question d optimisation.
 

Citation :

par contre je suis pas trop habitué aux trucs style : (i / 10 != 0) ? (char)(i / 10 + 48) : '0';


il ne s agit que d une facon d ecrire une condition if / then / else. De plus j utilise des cast (char) ce qui est beaucoup moins gourmand que itoa dans ton cas car tu ne traite que des petits nombres.
Autre reproche, n utilise pas strcat dans ta condition else, en effet itoa alloue deja de la memoire et strcat le fait aussi c est donc inutile est tu pourrais ecrire par consequent chaine = itoa(entier, tmp, 10);, de plus avec strcat tu concatene une chaine vide (chaine) avec la valeur de retour de itoa, ce qui n est pas tres judicieux.
 
Fais attention avec les allocations memoires, car c est la encore une operation tres couteuse en ressource processeur (je sais de quoi je parle ... j ai recode malloc() free() et realloc()).
 
Mais ce ne sont que des conseils, libre a toi de faire ce qu il te plait  :)

Message cité 2 fois
Message édité par deuxsous le 11-08-2006 à 18:17:06
Reply

Marsh Posté le 11-08-2006 à 18:25:15    

la sécurité je m'en fous pas, c'est des prg qui tournent en exploitation réellement que je fais. j'ai quasiment aucun soucis avec malgré la façon dont c'est codé ... quand y a des problèmes c'est que j'ai oublié de tester des trucs, mais c'est tout.
 
tu utilises quoi à la place de strcpy (sur des tableaux de char) ? le risque c'est de dépasser la taille de la variable dans laquelle on copie, c'est ça ?
 
de même, quoi mettre à la place d'un if, si on veut dire "si c'est comme ça, tu fais ça" ... ?
 
l'écriture j'avais saisi, le truc c'est que c'est à peu près aussi parlant que du russe traduit en chinois par un ougandais quand tu le lis ...
 
en tout cas, merci pour toutes ces explications messieurs.

Reply

Marsh Posté le 11-08-2006 à 19:06:39    

Citation :

tu utilises quoi à la place de strcpy (sur des tableaux de char) ? le risque c'est de dépasser la taille de la variable dans laquelle on copie, c'est ça ?


J utilise mes propres fonctions (c est pour ca que j ai recodé malloc, printf et compagnie). strcpy (et scanf d ailleurs) contrairement a strncpy, ne controle pas la taille de la chaine a mettre en tampon, on peut donc ecrire librement dans la memoire jusqu a l adresse de retour de la fonction en cours d execution, on peut ainsi injecter du code qui sera executer en lieu et place de la suite de ton programme. Pour un programme s executant sans droits particuliers ce n est pas problematique mais pour un programme s executant en root ou en wheel ca peut vite aboutir a la catastrophe.
 
Pour le if il y a les automates mais c est tres complexe et ca ne vaut pas le coup pour ce genre de programme. On utilisera les automates pour faire de l IA et du pathfinding. Dans ton cas n en abuse pas essaye d'utiliser les conditions le moins souvent possible (tout en verifiant toujours les valeurs de retour des fonctions telles que malloc et compagnie).


Message édité par deuxsous le 11-08-2006 à 19:08:29
Reply

Marsh Posté le 11-08-2006 à 20:24:11    

franceso a écrit :

Juste une question : tu fais ça pour apprendre à manipuler les chaines de caractères (donc dans ce cas programmer toi-même cette fonction est un but en soi), ou bien tu en as juste besoin pour une appli de plus grande envergure ?
 
Dans le deuxième cas, je te conseille d'utiliser tout simplement un snprinft("%02d", ... ) qui fera le boulot très bien sans que tu aies à t'occuper de ce qui se passe derrière.


 
j'avais pas vu ton post franceso (je t'ai pas snobé ...)
je connaissais pas snprintf et comme Elmoricq en a parlé pour remplacer itoa, je pensais que ça ne faisait que ça. mais là je viens de regarder, ça fait ce que je veux en fait.
 
c'est dans le cadre d'un programme d'interface entre applis, et effectivement, moins c'est long à écrire + ça m'arrange (enfin surtout le boss ...)
 
je pense que deuxsous (qui a carrément écrit ses propre fonctions à la place de celles des librairies !) va dire que snprintf c'est pas bien, vu que ç'est comme printf et fprintf ...
à ce propos deuxsous (si tu as le temps hein, c'est pas de la plus haute importance, c'est juste pour comprendre ...), j'ai pas très bien compris le coup de strcpy qui risque de saturer un buffer et d'écrire un peu n'importe où en mémoire :
ça veut dire que les octets à copier sont mis dans un buffer de taille limitée avant de copier, et que strcpy ne contrôle pas s'il à atteint la limite et peut donc dépasser cette limite ? si c'est ça c'est complètement c.., je pensais qu'il copiait les caractères un par un.

Reply

Marsh Posté le 11-08-2006 à 23:44:17    

c est un peu plus complexe que ca ... mais si ca t interesse vraiment je ne peux que te conseiller ce document vraiment tres complet et tres abordable pour tout personne s etant deja essayée au C.
www.zone-h.fr/download/file=14/
je n aurais pas pu faire mieux en termes de details et clareté

Reply

Marsh Posté le 12-08-2006 à 03:16:19    

deuxsous a écrit :

tu peux donc en deduire que la chaine de carateres n est autres qu un pointeur en C.


C'est faux. Une chaine de caractères est un tableau de char initialisé et terminé par un 0.
 
Par contre, un pointeur a le droit d'avoir pour valeur l'adresse du premier élément d'une chaine...
 
http://mapage.noos.fr/emdel/notes.htm#char_star


---------------
Des infos sur la programmation et le langage C: http://www.bien-programmer.fr Pas de Wi-Fi à la maison : http://www.cpl-france.org/
Reply

Marsh Posté le 12-08-2006 à 13:07:11    

deuxsous a écrit :

Code :
  1. #include <stdlib.h>
  2. char   *AjouteZero(int i)
  3. {
  4.   char  *str;
  5.   str = malloc(3 * sizeof(char));
  6.   str[0] = (i / 10 != 0) ? (char)(i / 10 + 48) : '0';
  7.   str[1] = (char)(i % 10 + 48);
  8.   str[2] = '\0';
  9.   return (str);
  10. }



 
Magnifique, mais encore trop compréhensible. [:petrus75]
En plus les magic numbers, c'est Entreprisey©, donc c'est vraiment du code de qualitaÿ.
 

deuxsous a écrit :

surtout eviter les strcpy, c est une fonction qui n est pas fiable et qui peut permettre des buffer overflow ... m enfin apres c est au niveau de la securite du programme, je ne pense pas que ca te gene enormement.


 
Il y a pas mal de cas où tu connais, de manière certaine, la taille de tes chaînes source et destination, et dans ce cas-là, tu vas pas t'embêter avec un strncpy() hein. [:petrus75]
 

deuxsous a écrit :

attention tout de meme a l utilisation du if (pour les test), c est l une des instructions les plus gourmandes en ressources, il faut eviter d en abuser et s en passer permet souvent de gagner pas mal de cycle d execution. Mais ca n est la encore qu une question d optimisation.


 
 [:fou]  
 

deuxsous a écrit :

il ne s agit que d une facon d ecrire une condition if / then / else. De plus j utilise des cast (char) ce qui est beaucoup moins gourmand que itoa dans ton cas car tu ne traite que des petits nombres.


 
En dehors du fait que itoa() n'est pas une fonction standard, on se moque totalement de la "gourmandise" et des micro-optimisation de daube, hein. A moins de cas très spécifiques, et qui ne concernent certainement pas un débutant, tu t'en moques éperdument de la "gourmandise" !  
C'est inutile, ça rend le code illisible, quand tu ne maîtrises pas pleinement ton sujet ça mène n'importe où sauf au résultat escompté, et surtout, ça ne sert à rien, parce que les performances se mesurent essentiellement à l'aune de l'algorithme utilisé. [:itm]
 

deuxsous a écrit :

Autre reproche, n utilise pas strcat dans ta condition else, en effet itoa alloue deja de la memoire et strcat le fait aussi


 
Si c'est pour dire des âneries pareilles, tu ferais mieux de t'abstenir d'écrire.
http://www.linux-france.org/articl [...] cat-3.html
 
 

deuxsous a écrit :

Fais attention avec les allocations memoires, car c est la encore une operation tres couteuse en ressource processeur (je sais de quoi je parle ... j ai recode malloc() free() et realloc()).


[:rofl2]


Message édité par Elmoricq le 12-08-2006 à 13:15:07
Reply

Marsh Posté le 12-08-2006 à 13:33:57    

manu a écrit :


char *AjouteZero (int entier)
{
   char chaine [3],tmp [3],*p;
(...)
   p=chaine;
   return (p);
}
 


non mais rassurez moi là : y'a que moi que ça choque ce code ?


---------------
J'ai un string dans l'array (Paris Hilton)
Reply

Marsh Posté le 12-08-2006 à 13:34:54    

Bien sûr qu'il n'y a que toi qu'il choque, ce code. [:mlc]
 
(relis le topic :o en commençant par la toute première réponse [:dawao] )

Message cité 1 fois
Message édité par Elmoricq le 12-08-2006 à 13:35:37
Reply

Marsh Posté le 12-08-2006 à 13:38:35    

Elmoricq a écrit :


(relis le topic :o en commençant par la toute première réponse [:dawao] )


ok je préfere [:joce]


Message édité par Harkonnen le 12-08-2006 à 13:38:56

---------------
J'ai un string dans l'array (Paris Hilton)
Reply

Marsh Posté le 12-08-2006 à 14:18:27    

deuxsous a écrit :


attention tout de meme a l utilisation du if (pour les test), c est l une des instructions les plus gourmandes en ressources, il faut eviter d en abuser et s en passer permet souvent de gagner pas mal de cycle d execution. Mais ca n est la encore qu une question d optimisation.


C'est ptet surtout le meilleur moyen d'avoir un programme bancale [:petrus75]
 
Et tu utilise quoi à la place [:dawa] ???
 
 

Reply

Marsh Posté le 12-08-2006 à 14:23:38    

Ben, rien. Cf. son magnifique snippet, destiné à un débutant, montrant un malloc() sans aucun test pour valider que la mémoire a été allouée. [:dawa]

Reply

Marsh Posté le    

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed