Fonction substr en C [Fermé]

Signaler
Messages postés
165
Date d'inscription
samedi 4 juin 2005
Statut
Membre
Dernière intervention
24 octobre 2007
-
ranzi
Messages postés
2
Date d'inscription
lundi 7 mai 2007
Statut
Membre
Dernière intervention
17 août 2010
-
Bonjour,
j'ai codé cette fonction, sensée être l'équivalent de string::substr(...) en C :

char * StrSub(const char *s, int pos, int len)
{
    char *ps = (char*)s;
    static char rs[256];
    char *prs = rs;
    int strLen = strlen(s);
   
    if(pos>=strLen || len==0) return NULL;
    ps+=pos;

    if(len==-1 || len>(strLen-pos)) len=strLen-pos;
   
    int i=0;
    while(i<len && *ps!='\0')
    {
        *prs=*ps;
        prs++; ps++;
        i++;
    }
    *prs='\0';
   
    return rs;
}

Elle semble marcher seulement elle présente des dysfonctionnement, exemple :

char *s="une phrase";
cout << StrSub(s, 0, 5) <<endl<<StrSub(s, 5, -1);

qui affichera deux fois "une p", au lieu de "une p" puis "hrase" .

Je ne comprends pas l'erreur, et comme je ne maitrise pas tout à fait les pointeurs, je demande à un oeil avisé de bien vouloir me dire où ca coince. Merci bcp

17 réponses

Messages postés
179
Date d'inscription
mardi 16 août 2005
Statut
Membre
Dernière intervention
25 août 2010

ce fonctionne ok en C si du fait

printf("%s\n",  StrSub(s, 0, 5);
printf("%s\n",  StrSub(s, 5, -1);

Mais pas en C++ du fait de ton buffer static dans ta fonction.....
Messages postés
3874
Date d'inscription
mardi 8 mars 2005
Statut
Modérateur
Dernière intervention
7 novembre 2014
10
Salut,

Si le compilo fait :

StrSub(s, 5, -1)
StrSub(s, 0, 5)

puis s'occupe des stream, il afficherat deux fois la variable statique rs qui contiendrat ce qui aurat été affecté lors du dernier appel.

cout << StrSub(s, 0, 5) <<endl;
cout << StrSub(s, 5, -1);

Ca fait le même problème ?
Messages postés
21042
Date d'inscription
jeudi 23 janvier 2003
Statut
Modérateur
Dernière intervention
21 août 2019
16
Essaie ceci, devrait aller.

Retourne combien remplis dans *szdst sans compter le zero final.


DWORD StrSub(const char *s, DWORD pos, DWORD len, char *szdst)
{
  char *p = szdst;
  if(!len) goto subEXIT;
  if(pos) { 
    do {
      if(*s++ == 0) goto subEXIT;
    } while(--pos);
  }
  do {    if((*p *s) 0) goto subEXIT;
    p++; s++;
  } while(--len);
subEXIT:
  *p = 0;
  return (p - szdst);
}

ciao...
BruNews, MVP VC++
Messages postés
12303
Date d'inscription
mardi 10 février 2004
Statut
Modérateur
Dernière intervention
30 juillet 2012
30
Salut

A quoi servent ces gotos exactement ?

enfin sans les gotos, ca reste digeste, y a t'il une difference de vitesse ?

DWORD StrSub(const char *s, DWORD pos, DWORD len, char *szdst)
{
  char *p = szdst;
  if(!len) goto subEXIT;
  if(pos) { 
    do {
      if(*s++ == 0) {
  *p = 0;


  return (p - szdst);
}
    } while(--pos);
  }
  do {    if((*p *s) 0) break;
    p++; s++;
  } while(--len);
  *p = 0;

  return (p - szdst);
}

histoire de rester en accord avec notre cher dijkstra...

<hr />une recherche sur exalead vous aurait peut-etre evite de poser cette question

In a dream, I saw me, drop dead...
U were there, U cried...
It was just a dream,
if I die, U won't cry, maybe, U'll be happy
Messages postés
21042
Date d'inscription
jeudi 23 janvier 2003
Statut
Modérateur
Dernière intervention
21 août 2019
16
à éviter de dupliquer du code, tu le sais puisque c''est ce que tu as fait.
En quoi est-ce plus 'digeste' ???

ciao...
BruNews, MVP VC++
Messages postés
165
Date d'inscription
samedi 4 juin 2005
Statut
Membre
Dernière intervention
24 octobre 2007

Merci pour vos codes, ca m'a permis de voir mon erreur. Mais il n'y a pas un moyen de retourner directement une chaine statique, bien que j'ai du mal à en voir un ? :s
Messages postés
12303
Date d'inscription
mardi 10 février 2004
Statut
Modérateur
Dernière intervention
30 juillet 2012
30
http://fr.wikipedia.org/wiki/Edsger_Dijkstra
A case against the GOTO statement.

apres, disons que je programme dans quelques langages d'une facon similaire (quand je le peux evidement), peut-etre pour pouvoir relire tout ce que je fais meme apres une pause de quelques mois sur un langage... Ainsi, je n'utilise pas goto, pas switch, ... je sais que ca ne fait pas souvent un code parfait, je demandais juste si pour goto, ca faisait gagner du temps d'execution, pour malgre les imprefections de mon travail, savoir ce que j'ai a gagner a le perfectionner... (ne pas dupliquer du code, c'est un bon argument, mais tu tapes un label, deux goto, je tapes un break; et deux lignes, donc malgre le fait d'avoir duplique du code, j'en ai ecrit autant)

<hr />une recherche sur exalead vous aurait peut-etre evite de poser cette question

In a dream, I saw me, drop dead...
U were there, U cried...
It was just a dream,
if I die, U won't cry, maybe, U'll be happy
Messages postés
21042
Date d'inscription
jeudi 23 janvier 2003
Statut
Modérateur
Dernière intervention
21 août 2019
16
Peu importe les lignes de C, ce qui est à comparer est l'asm généré par le compilo, je verrai en soirée.
Je ne pense pas qu'un simple label aussi causant que 'subEXIT' gênera la compréhension de la fonction même 10 ans plus tard.

ciao...
BruNews, MVP VC++
Messages postés
21042
Date d'inscription
jeudi 23 janvier 2003
Statut
Modérateur
Dernière intervention
21 août 2019
16
julien_boss > prends par habitude de ne jamais retourner une chaine ou autre tableau statique. L'appelant doit être responsable des params passés, ça garantit l'usage à long terme de la fonction.
Ex: longueur de ton buffer statique ira toujours ??? ben non.

ciao...
BruNews, MVP VC++
Messages postés
280
Date d'inscription
dimanche 7 septembre 2003
Statut
Membre
Dernière intervention
8 juillet 2014
3
salut coucou

les goto dans une fonction sont très désagréables dans certains cas, mais peuvent être très agréables dans d'autres

moi je l'utilise souvent quand il faut faire des free avec le retour de la fonction

ou quand il faut faire quelque chose de différent après une boucle selon la raison qui nous a fait sortir de la boucle

en effet le code à exécuter après la sortie de la boucle n'a aucune raison d'être placé dans la boucle, et donc un goto sera très pratique
Messages postés
3874
Date d'inscription
mardi 8 mars 2005
Statut
Modérateur
Dernière intervention
7 novembre 2014
10
Le goto c'est sale. Faut pas l'utiliser.

Mais pour nous tenter ils en ont mis partout : VB, Delphi, batch, bash... Même le plutôt récent C# à son goto.


En java, y en a pas. Quoi ? le break avec label ? Non, non, ce n'est pas la même chose.


En php, c'était domage qu'il y en ai pas quand même ! Donc un break modifié est prévu en version 6 !


Et puis d'abord, en assembleur, y a pas d'instruction goto, d'abord !!


(C'était ma minute 6 ans d'âge mental. En même temps, c'est assez puéril cette histoire de goto.)
Messages postés
12303
Date d'inscription
mardi 10 février 2004
Statut
Modérateur
Dernière intervention
30 juillet 2012
30
je ne suis pas aussi categorique... c'etait une question, pas une critique...
en php, ils ajoutent l'objet a la v5 et le goto a la v6... c'est un langage que je connais bien, mais ils font n'imp de ce cote... surtout en php, on est souvent limite a des choes simples, alors faire sans goto... c'est pas trop complique...

asm, j'en ai jamais fait, mais j'ai entendu parler de jump qui ressembleraient aux gotos...

tu prends un langage vraiment moche : le tibasic, t'as pas de procedures, si on veut decouper, faut inclure des "fichiers" mais pour passer des variables, c'est pas top...
tant qu'on a pas appuye sur une touche

:lbl 1
:getkey->k
:if k=0
:then
:goto 1
:end

tu vas beaucou rire :) (error memory au bout de 30 secondes, et un programme super lent si jamais t'appuis sur une touche entre temps... ca te bourre la pile d'une attente de ":end")

Bref, en C, c'est probablement parfaitement compile, mais en php, laisse moi rire...

<hr />une recherche sur exalead vous aurait peut-etre evite de poser cette question

In a dream, I saw me, drop dead...
U were there, U cried...
It was just a dream,
if I die, U won't cry, maybe, U'll be happy
Messages postés
3874
Date d'inscription
mardi 8 mars 2005
Statut
Modérateur
Dernière intervention
7 novembre 2014
10
Mon message est à prendre au deuxième degré.

Et vivi en asm y a jmp qui est tout à fait un goto sans compter les
je/jne et variantes qui servent de saut conditionnel. Mais il ne
s'écrit pas goto !


Sur le coup du tibasic, ça été mon deuxième langage après celui de ma
casio ! Ca me surprend le coup de l'erreur. M'enfin ça fait un bail que
j'ai pas touché une TI...
Messages postés
21042
Date d'inscription
jeudi 23 janvier 2003
Statut
Modérateur
Dernière intervention
21 août 2019
16
'JMP addr' asm correspond stricto senso au 'goto label' du C.

Un binaire est rempli d'instructions de saut.
if, do, while, etc... absolument partout le compilo génère des sauts. Les goto y seront qu'on les masque ou non, aucun autre moyen de faire un code réagissant à des conditions.
Même un appel de fonction est un saut après empilage de EIP et ensuite des params, retour de la fonction idem un saut.

ciao...
BruNews, MVP VC++
Messages postés
3874
Date d'inscription
mardi 8 mars 2005
Statut
Modérateur
Dernière intervention
7 novembre 2014
10
Bon, comme BruNews a pas l'air de se décider, voyons ce que ça donne côté compilation.

Config :
VC2005 pro (MSDN AA).
Release.
Optimisation complète.
Privilégie le code rapide.
Jeu de caractère multi octet.
Compiler comme code C.
Convention en stdcall.

J'ai remplacé le :
if(!len) goto subEXIT;
malencontreusement oublié dans les modifs de coucou747 par un :
if(!len) return 0;

Une question : pourquoi s est const ?
Cela ne devrait pas nous empècher de le modifier ?

Mon main :

int __cdecl main(int argc, char * argv[])
{
    char *s = "une phrase";
    char d[20];
    HANDLE hOutput;
    DWORD nWritten;

    hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
   
    StrSubWithGoto(s, 2, 5, d);
    lstrcat(d, "\n");
    WriteConsole(hOutput, d, 6, &nWritten, NULL);

    StrSubWithoutGoto(s, 2, 5, d);
    WriteConsole(hOutput, d, 5, &nWritten, NULL);

    return 0;
}

Débugueur/désassembleur : W32DASM.

Les deux routines ont été inlinées par le compilo.
Le tampon de réception est en esp+18.
eax pointera la chaîne source, ecx la chaîne de destination.
Le test sur la longueur à été supprimé puisque inutile dans ce cas particulier.
De même le test sur pos.
La valeur de retour n'est pas calculée puisque non utilisé.
Les add et les sub auraient dûes être logiquement des inc et des dec, plus compactes.

StrSubWithGoto :

  mov edi, 5
  mov esi, 2
  mov eax, s
  lea ecx, dword ptr [esp+18]
loop1:

  if(*s++ == 0) goto subEXIT;
  mov dl, byte ptr [eax]
  add eax, 1
  test dl, dl
  je subEXIT

  while(--pos);
  sub esi, 1
  jne loop1
loop2:
  if((*p *s) 0) goto subEXIT;
  mov dl, byte ptr [eax]
  test dl, dl
  mov byte ptr [ecx], dl
  je subEXIT

  p++; s++;
  add ecx, 1
  add eax, 1

  while(--len);
  sub edi, 1
  jne loop2
subEXIT:
  (..)
  *p = 0;
  mov byte ptr [ecx], 00

StrSubWithoutGoto :

Faudrat que l'on m'explique l'intérêt du lea ecx, dword ptr[ecx+00]...
Le mov edi, edi est lui aussi manifique...

  mov edi, 5
  mov esi, 2
  mov eax, s
  lea ecx, dword ptr[esp+18]
  lea ecx, dword ptr[ecx+00]
loop1:
    if(*s++ 0) { *p 0; return (p - szdst); }
  mov dl, byte ptr[eax]
  add eax, 1
  test dl, dl
  je trèsLoin
 
  while(--pos);
  sub esi, 1
  jne loop1

  mov edi, edi
loop2:
  if((*p *s) 0) break;
  mov dl, byte ptr [eax]
  test dl, dl
  mov byte ptr [ecx], dl
  je subEXIT

  p++; s++;
  add ecx, 1
  add eax, 1

  while(--len);
  sub edi, 1
  jne loop2
subEXIT:

  *p = 0;
  mov byte ptr [ecx], 0
  (..)
trèsLoin:

Comparatif :

Si on exclut les 2 instructions (très) bizarres, le code est quasi identique.
Le goto s'en sort donc un peu mieux dans ce cas précis, mais plutôt à cause du compilo qu'autre chose.
Mon compilo préféré s'est par exemple abstenu de générer ces deux lignes...

Je me demande si le seul vrai critère d'emploi ou pas du goto ne serait pas :
Mon chef aime-t-il les goto ?
Messages postés
21042
Date d'inscription
jeudi 23 janvier 2003
Statut
Modérateur
Dernière intervention
21 août 2019
16
Là où le compilo a un énorme avantage c'est sur l'alignement des instructions comme des données, c'est fait à tout coup.
'inc' et 'dec' étant plus compact, il fallait souvent injecter des octets de cadrage pour alignement.
On ajoutera que ces instructions ne sont plus encodables en x64 sur R16 et R32, elles ont été enlevées pour leur équivalent 'add' et 'sub' ainsi plus de problème.

ciao...
BruNews, MVP VC++
Messages postés
2
Date d'inscription
lundi 7 mai 2007
Statut
Membre
Dernière intervention
17 août 2010

Je propose ces deux fonctions simples, bien qu'elle ne gére pas des valeurs négatives pour 'start', 'end' et 'length'
char * substring(const char *str,char *sub,int start,int end)
{
  int i,j;
  for(i=start,j=0; i<end && str[i]!='\0'; i++,j++)
    sub[j]=str[i];
  sub[j]='\0';
  return sub;
}

char * substr(const char *str,char *sub,int start,int length)
{
  return substring(str,sub,start,start+length);
}


Exemple :
const char *mot="coucou c'est RANZI";
char sub[50];
printf("%s\n",substr(mot,sub,0,50));// -> "coucou c'est RANZI"
printf("%s\n",substr(mot,sub,0,6));// -> "coucou"
printf("%s\n",substr(mot,sub,13,5));// -> "RANZI"