IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C Discussion :

Retourner un string depuis une fonction en C


Sujet :

C

  1. #1
    Invité de passage
    Femme Profil pro
    Recherche scientifique
    Inscrit en
    Avril 2020
    Messages
    3
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Dordogne (Aquitaine)

    Informations professionnelles :
    Activité : Recherche scientifique
    Secteur : Industrie Pharmaceutique

    Informations forums :
    Inscription : Avril 2020
    Messages : 3
    Par défaut Retourner un string depuis une fonction en C
    Hello,

    Je suis programmeuse, plutôt anglophone ( oui je ne suis pas parfaite ) en C, j'ai des notions de base.
    J'ai quelques soucis avec le "return" d'un string ou d'un tableau depuis une function.
    Pour donner un exemple, je tente de simuler en C la function MID$ du basic.
    Pour remémorer, on a:

    Code BASIC : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
       string_2$ = MID$( string_1$, start% [, NBytes% ] )        function  .BASIC
       MID$( string_1$, start% [, NBytes% ] ) = string_2$        statement .BASIC

    Je laisse de côté la variante "statement" qui ne m'intéresse pas.
    Si j'utilise ( sans options ):

    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    void MID( const char * string_1, char * string_2, int start, int NBytes );

    Je récupère le résultat string_2 extrait du string_1.
    Pas de souci. Cela me semble propre.
    Si je veux copier le style MID$, cela se complique...
    Un exemple ( retourne le jour de la semaine ):

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
     
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <ctype.h>
     
    void MID( const char * string_1, char * string_2, int start, int NBytes )
    /* char * MID( const char * string_1, int start, int NBytes ) */ {  /* => string_2 */
        int k = 0, l = 0;
        for( k = start; k < (start + NBytes); k++ )
        {
            string_2[l] = string_1[k ];
            l++;
        }
           string_2[l] = '\0';
    }
     
    int main()
    {
       char buf[64] = "SUNMONTUEWEDTHUFRISAT";
       char buf2[64] = ".....................";
       int i;
       for ( i = 0; i < 7; i++ ) {
          MID( buf, buf2, 3 * i, 3);
          printf( "%i => %s\n", i, buf2 );
       }
       return 0;
    }
    Pour le moment, pas de problème.
    Mais si je veux utiliser la fonction en tant que:


    avec un


    cela cafouille.

    Pas très étonnée, string_2 est un... string ! pas une variable.
    Mais si je veux mettre un


    cela fonctionne mais ce n'est pas le string_2 voulu !
    Un pointer... Pas trouvé comment faire ( si c'est possible ). À moins d'utiliser des string en global mais pas joli !
    Pourquoi cela fonctionne avec un string explicite et pas la variable correspondant au string ?
    La méthode bourrin utilisant getc et transférer lettre par lettre... Pas chaude du tout ( et vu la canicule... ).
    Bien sûr, je peux renoncer à la forme "function", mais j'aime comprendre.

    Mon but est d'aller plus loin dans l'exploitation de MID pour traiter des 1000-3000 lignes de données en ASCII dans un file séparé combinant diverses sortes de valeurs, int, double, char. De coder en préliminaire les doubles du fichier source en 8 bytes IEEE à décoder ensuite en double en lecture.

    Merci pour vos idées.

  2. #2
    Expert confirmé
    Homme Profil pro
    Responsable Données
    Inscrit en
    Janvier 2009
    Messages
    5 551
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Responsable Données

    Informations forums :
    Inscription : Janvier 2009
    Messages : 5 551
    Par défaut
    Bonjour,
    Il y a (très) longtemps que je n'ai pas fait de C, donc je vais peut-être sortir une annerie.
    Si ta fonction doit renvoyer une chaîne, il ne faut pas mettre CONST dans sa déclaration :
    Avec CONST et un return "ABCDEF" ça fonctionne puisque ici tu renvoies une constante.

    Mettre un CONST n'est d'ailleurs pas très logique pour une fonction qui renvoie une valeur "calculée", donc tout sauf une constante.

    Tatayo.

  3. #3
    Expert confirmé
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    Juillet 2013
    Messages
    4 838
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Analyste/ Programmeur

    Informations forums :
    Inscription : Juillet 2013
    Messages : 4 838
    Par défaut
    Il faut utiliser malloc pour allouer sur le tas 1 chaîne de caractères (et ne pas oublier la désallocation avec free )
    Lorsque tu quittes 1 fonction (qui est 1 bloc { /* ... */ } toutes les variables locales sont détruites.


    Voici du code:
    • tu as les 3 façons de faire : MID copie directement, MID_1 retourne 1 chaîne de caractères, MID_2 crée et retourne dans 1 paramètre sortie.
    • mon code n'est sûrement pas très propre, mais c'est l'idée
    • je préfère utiliser des unsigned char pour les chaînes de caractères mais toute la bibliothèque standard utilise des char. Et en C il n'y a pas de booléen (0 c'est faux, le reste est vrai) : donc soit 1 char soit 1 unsigned char (que j'utilise)
    • en C, il faut toujours avoir la taille d'une chaîne en même temps que la chaîne (tu peux englober tout cela avec 1 structure struct)


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
     
    unsigned char MID(const /*unsigned*/ char* str, /*unsigned*/ char* copy, size_t str_len, size_t start, size_t NBytes) {
        unsigned char ret;
     
        if ((str != NULL) && (copy != NULL) && (start < str_len) && ((start + NBytes + 1) < str_len)) {
    //      strncpy(copy, (str + start), NBytes /* * sizeof(char)*/);
            memcpy(copy, (str + start), NBytes /* * sizeof(char)*/);
            copy[NBytes] = '\0';
     
            ret = 1;
        } else {
            ret = 0;
        }
     
        return ret;
    }
     
     
    /*unsigned*/char* MID_1(const /*unsigned*/ char* str, size_t str_len, size_t start, size_t NBytes) {
        char* ret;
     
        if ((str != NULL) && (start < str_len) && ((start + NBytes + 1) < str_len)) {
            ret = malloc((NBytes + 1) * sizeof(char));
     
            if (ret != NULL) {
                strncpy(ret, (str + start), NBytes /* * sizeof(char)*/);
    //          memcpy(ret, (str + start), NBytes /* * sizeof(char)*/);
                ret[NBytes] = '\0';
            }
        } else {
            ret = NULL;
        }
     
        return ret;
    }
     
     
    unsigned char MID_2(const /*unsigned*/ char* str, /*unsigned*/ char** copy, size_t str_len, size_t start, size_t NBytes) {
        unsigned char ret;
     
        if ((str != NULL) && (copy != NULL) && (start < str_len) && ((start + NBytes + 1) < str_len)) {
            (*copy) = malloc((NBytes + 1) * sizeof(char));
     
            if (((*copy) != NULL)) {
    //          strncpy((*copy), (str + start), NBytes /* * sizeof(char)*/);
                memcpy((*copy), (str + start), NBytes /* * sizeof(char)*/);
                (*copy)[NBytes] = '\0';
     
                ret = 1;
            } else {
                ret = 0;
            }
        } else {
            ret = 0;
        }
     
        return ret;
    }
     
     
    int main(int argc, char* argv[])
    {
        /*unsigned*/ char  buf[64]  = "SUNMONTUEWEDTHUFRISAT\0";
        /*unsigned*/ char  buf1[64] = ".....................\0";
        /*unsigned*/ char* buf2     = NULL;
     
        if ( MID(buf, buf1, 64, 6, 3) ) {
            printf("1) %3s\n", buf1);
        } else {
            printf("1) problem\n");
        }
     
        if ( MID(buf, buf1, 64, 9, 60) ) {
            printf("2) %3s\n", buf1);
        } else {
            printf("2) problem\n");
        }
     
        buf2 = MID_1(buf, 64, 9, 9);
     
        if (buf2 != NULL) {
            printf("3) %3s\n", buf2);
            free(buf2);
        } else {
            printf("3) problem\n");
        }
     
        if ( MID_2(buf, &buf2, 64, 12, 3) ) {
            printf("4) %3s\n", buf2);
            free(buf2);
        } else {
            printf("4) problem\n");
        }
     
        return EXIT_SUCCESS;
    }

  4. #4
    Membre prolifique Avatar de Artemus24
    Homme Profil pro
    Agent secret au service du président Ulysses S. Grant !
    Inscrit en
    Février 2011
    Messages
    7 523
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Agent secret au service du président Ulysses S. Grant !
    Secteur : Finance

    Informations forums :
    Inscription : Février 2011
    Messages : 7 523
    Par défaut
    Salut à tous.

    @ Tatayo : CONST sert uniquement à rendre la variable non modifiable.
    Dans le cas d'un tableau, cela ne sert strictement à rien. Autant ne pas l'utiliser dans ce contexte.

    Citation Envoyé par Fluorine
    Pour donner un exemple, je tente de simuler en C la function MID$ du basic.
    De quel BASIC parles tu ?

    Je suppose que la fonction MID$ extrait une sous-chaîne de caractères à partir d'une chaîne de caractères. Exemple :
    Code basic : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    A$ = "BONJOUR"
    B$ = MID$(A$, 4, 3)
    Dans B$ tu récupères "JOU". C'est juste un rappel de la fonction basic "MID()".

    Allouer de la mémoire pour retourner la valeur de la fonction est correcte.
    Mais elle a l'inconvénient qu'il faut la libérer ensuite, après l'avoir utilisé, ce qui alourdit la gestion de ton traitement.
    Et cela peut être sujet à des erreurs, ou à l'usage trop important de l'allocation de mémoire.
    Donc oui l'idée de Foetus est correcte mais je ne la préconise pas du tout.

    I) Voici la solution de Foetus :

    Soit ce que nous voulons tester :
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    int main(void)
    {
    	char buf[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    	char *p = MID(buf, 6, 3);
    	printf("%s\n", p);
    	free(p);
    	return 0;
    La fonction MID doit être de ce genre :
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    #include <stdlib.h>
    #include <string.h>
     
    char *MID(const char *src, int start, int len)
    {
    	char *dst = malloc(len + 1);
     
    	if (!dst)
    		return NULL;
     
    	memcpy(dst, src + start, len);
    	dst[len] = '\0';
    	return dst;
    }

    II) Solution avec allocation static :

    Au lieu d'un malloc, on utilise ici un "static char".
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    char *MID(const char *src, int start, int len)
    {
    	static char dst[256];
    
    	memcpy(dst, src + start, len);
    	dst[len] = '\0';
    
    	return dst;
    }
    Cela évite de faire à chaque fois une allocation mémoire, puis ensuite de la llibérer après l'appel de la fonction "MID()".
    L'inconvénient est qu'un second apppel de la fonction "MID()" écrase le résultat du premier appel.

    III) Ce que je préconise :

    Allouer la variable "dst" dans la section appelante.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    int main(void)
    {
    	char buf[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    	char dst[256];
    
    	printf("%s\n", MID(buf, 6, 3, dst));
    	return 0;
    Cela oblige de modifier légèrement la fonction "MID()" en introduisant en tant que paramètre la variable "DST".
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    {include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    char *MID(const char *src, int start, int len, char *dst)
    {
    	memcpy(dst, src + start, len);
    	dst[len] = '\0';
    	return dst;
    }

    Il n'y a pas de solution propre pouvant simuler exactement la fonction basic "MID()".

  5. #5
    Membre Expert
    Avatar de edgarjacobs
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mai 2011
    Messages
    850
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 66
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Mai 2011
    Messages : 850
    Par défaut
    Hello,

    En reprenant ton code, cela fonctionne parfaitement avec une fonction qui retourne un char * :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
     
    char *mid(const char *string_1, char *string_2, int start, int nbytes) {
    	int l=0;
    	for(int k=start; k<start+nbytes; k++)	{
    		string_2[l]=string_1[k];
    		l++;
    	}
    	string_2[l]='\0';
     
    	return(string_2);
    }
     
    int main(void) {
    	char buf[64]="SUNMONTUEWEDTHUFRISAT";
    	char buf2[64];
    	for(int i=0; i<7; i++)
    		printf("%i => %s\n", i, mid(buf, buf2, 3*i, 3));
     
    	return 0;
    }
    On écrit "J'ai tort" ; "tord" est la conjugaison du verbre "tordre" à la 3ème personne de l'indicatif présent ;)

  6. #6
    Invité de passage
    Femme Profil pro
    Recherche scientifique
    Inscrit en
    Avril 2020
    Messages
    3
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Dordogne (Aquitaine)

    Informations professionnelles :
    Activité : Recherche scientifique
    Secteur : Industrie Pharmaceutique

    Informations forums :
    Inscription : Avril 2020
    Messages : 3
    Par défaut Mes essais
    Hello,

    Merci à tous pour toutes ces infos très intéressantes.
    Je vois que finalement mon "petit" problème n'est pas simple.

    "Quel BASIC" ?
    Divers BASIC, tel que ceux de Microsoft : QB du MSDOS, QB 4.5 et... QB64.
    Je crois que c'est la même chose.
    Dans Excel ? Pas testé.
    PureBasic : c'est proche avec MID au lieu de MID$.
    Je note que la difficulté c'est pour passer le résultat en tant que résultat de MID, donc en utilisant la gauche de MID.

    Les diverses méthodes jouent sur ce point épineux.
    On peut changer ma boucle for en memcpy, c'est plus élégant.
    Un piège si l'on ne prévoit pas de place pour les données.
    char buf[256] = ".............."; j'ai mis au pif un remplissage pour initialiser.
    ou
    malloc
    servent beaucoup à cela.

    J'aime beaucoup la méthode élégant d'Artemus24 utilisant dst en plus.

    La méthode edgarjacobs...
    J'ai cherché un moment alors que cela ne fonctionnait pas avec moi.
    Le problème :
    Cela utilise un paramètre que l'on saute facilement:
    char *mid(const char *string_1, char *string_2, int start, int nbytes)
    On ne sait plus si le retour se fait en tant que fonction ( par le "char *" de tête ) ou en tant que sub
    Le :
    return string_2;
    n'a aucune action et peut être supprimé.
    Dommage, cela eusse été une solution confortable.
    On retombe ainsi sur ma première version, en supprimant le "return" inutile et le "char *"

    J'ai surtout deux solution qui me plaisent :
    - celle que j'ai au tout débu en mode SUB.
    - celle ajoutant le dst en mode fonction..

    Mon but est de réutiliser mes nombreux programmes en BASIC, dont j'ai conservé les sources.
    Des code source avec de nombreuses lignes de calcul... Mais rien de complexe la plupart du temps.
    Pour ne pas tout rectifier à la main, je lance une AI pour me traduire en robot le trivial, travail de fourmi.
    J'ai un peu honte demander cela à une AI alors que l'intelligence n'est guère sollicitée. Le marteau pour écrase un moucheron.

    Je pourrais passer en C++ ou autres plus évolués, mais je préfère garder un code simple qui puisse être facilement traduits dans d'autres langages sans avoir à se prendre la tête sur des subtilités. Tout cela afin que mes programmes ne finissent pas dans l'oubli étant inutilisables. Cela a failli être le cas pour mes programmes en BASIC, j'ai eu la bonne idée de garder mes sources. J'en ai aussi en C, au temps de MSDOS ( 16 bits... ). Le temps des diskettes...

    Merci à tous.

  7. #7
    Membre Expert
    Avatar de edgarjacobs
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mai 2011
    Messages
    850
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 66
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Mai 2011
    Messages : 850
    Par défaut
    Cela utilise un paramètre que l'on saute facilement:
    char *mid(const char *string_1, char *string_2, int start, int nbytes)
    Dans ce cas, le compilateur t'aurait averti du problème. Sans doute par une warning, et il faut toujours régler toutes les warnings d'un programme (à moins de savoir ce que l'on fait). Et si le compilateur n'a rien dit, c'est que les options de compilation -Wall et -Wextra ne sont pas présentes. Il faut toujours les activer pour avoir un maximum de messages lors de la compilation.
    On écrit "J'ai tort" ; "tord" est la conjugaison du verbre "tordre" à la 3ème personne de l'indicatif présent ;)

  8. #8
    Expert confirmé
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    Juillet 2013
    Messages
    4 838
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Analyste/ Programmeur

    Informations forums :
    Inscription : Juillet 2013
    Messages : 4 838
    Par défaut
    Citation Envoyé par Fluorine Voir le message
    La méthode edgarjacobs... […] Cela utilise un paramètre que l'on saute facilement
    C'est 1 truc classique en retournant le "pointeur résultat" cela permet de chaîner les fonctions (mais tu peux aussi ignorer le retour)

    En gros ici cela permet de faire 1 truc mid(mid(str, copy, 3, 12), copy2, 5, 7); (<- attention il faut sûrement que la fonction mid puisse travailler même si la source et la destination sont la même chaîne)

    1 truc classique parce la bibliothèque le fait Par exemple, memcpy retourne l'adresse du bloc de mémoire de destination, strncpy retourne l'adresse de la chaîne dupliquée


    Citation Envoyé par Fluorine Voir le message
    Je pourrais passer en C++ ou autres plus évolués, mais je préfère garder un code simple qui puisse être facilement traduits dans d'autres langages sans avoir à se prendre la tête sur des subtilités
    Que ce soit le C ou le C++, 1 "code simple facile traduisible sans se prendre la tête sur des subtilités" : tu as mal choisis tes langages Mais apparemment pour toi le C, c'est simple
    Surtout lorsqu'on parle de code simple pour 1 code qui manipule les chaînes de caractères le C n'a aucun type "chaîne de caractères", il ne gère que l'ASCII avec des tableaux d'entiers signés … et comme c'est du C il faut allouer/ désallouer les tableaux (à moins d'utiliser des tableaux statiques ou des buffers créés 1 seule fois)

    Après pour la gestion de l'UTF-16 il y a le type wchar_t et la bibliothèque wchar.h (<- lien cplusplus.com en anglais. Attention c'est 1 site pour le C et le C++: wchar.h c'est le C, cwchar c'est le C++. En C++, les entêtes C perdent l'extension .h et sont préfixés d'un c)
    Mais je ne sais pas si beaucoup de développeurs l'utilisent

    Je préfère utiliser les std::string en C++ qui permettent de créer de rien et de manipuler facilement les chaînes de caractères … mais cela reste de l'ASCII
    Le C++ moderne ajoute des types pour manipuler l'Unicode mais je ne sais pas si c'est "efficace"/ pratique et beaucoup utilisé.

Discussions similaires

  1. Retourner une valeur depuis une fonction
    Par Katden dans le forum Shell et commandes GNU
    Réponses: 2
    Dernier message: 15/04/2026, 21h44
  2. Retourner un load_html depuis une fonction
    Par Lekno dans le forum EDI, CMS, Outils, Scripts et API
    Réponses: 8
    Dernier message: 24/10/2013, 12h36
  3. Retourner un tableau depuis une fonction
    Par obalais dans le forum C++
    Réponses: 2
    Dernier message: 20/10/2005, 16h49
  4. Ecrire dans un tableau html depuis une fonction js ?
    Par botanica dans le forum Général JavaScript
    Réponses: 2
    Dernier message: 05/10/2005, 12h48
  5. convertir une string en une fonction
    Par danny_67 dans le forum XML
    Réponses: 2
    Dernier message: 26/08/2005, 13h55

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo