Partage
  • Partager sur Facebook
  • Partager sur Twitter

Caractère de fin de chaîne en allocation dynamique

    22 août 2021 à 9:37:49

    Bonjour,

    J'ai créé un petit programme qui demande à l'utilisateur de former un tableau de la taille de son choix, qui le remplit ensuite de nombres aléatoires et qui les trie dans l'ordre croissant grâce à une fonction que j'ai créée.

    Pour que cela fonctionne, la taille de mon tableau est déterminée par l'allocation dynamique de la mémoire, mais en inspectant mon code, je me suis demandé où se trouvait le caractère de fin de chaîne \0. Voici le premier programme que j'avais réalisé (il fonctionne) :

    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>
    
    void   sorting_array (int array[], int array_size);
    
    int    main (int argc, char *argv[])
    {
        int i;
        int array_size;
        int *array;
        
        i = 0;
        array_size = 0;
        array = NULL;
        
        srand(time(NULL));
        
        printf("How many compartments do you want?\n");
        scanf("%d", &array_size);
        
        array = malloc((array_size) * sizeof(int));
        
        if (array == NULL)
        {
            printf("Malloc failed\n");
            exit(0);
        }
        
        printf("Here's the intial array:\n");
        
        while (i < array_size)
        {
            array[i] = (rand() % (99 - 10 + 1)) + 10;
            printf("%d ", array[i]);
            i++;
        }
        
        printf("\n\n");
        
        sorting_array(array, array_size);
        
        printf("Here's the sorted array:\n");
        
        i = 0;
        
        while (i < array_size)
        {
            printf("%d ", array[i]);
            i++;
        }
        
        free(array);
            
        printf("\n");
        
        return 0;
        
    }
    
    void   sorting_array (int array[], int array_size)
    {
        int i;
        int j;
        int tmp;
        
        i = 0;
    
        tmp = 0;
        
        while (i < array_size)
        {
            
            j = i + 1;
            
            while (j < array_size)
            {
                if (array[i] > array[j])
                {
                    tmp = array[i];
                    array[i] = array[j];
                    array[j] = tmp;
                    
                }
                
                j++;
            }
            
            i++;
        }
    
    }


    En y regardant de plus près, je me suis aperçu que toutes les valeurs du tableau étaient remplies, ce qui ne laissait pas la place au caractère \0, j'ai donc changé le code pour obtenir celui ci-dessous (il fonctionne aussi)

    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>
    
    void   sorting_array (int array[], int array_size);
    
    int    main (int argc, char *argv[])
    {
        int i;
        int array_size;
        int *array;
        
        i = 0;
        array_size = 0;
        array = NULL;
        
        srand(time(NULL));
        
        printf("How many compartments do you want?\n");
        scanf("%d", &array_size);
        
        array = malloc((array_size + 1) * sizeof(int));
        
        if (array == NULL)
        {
            printf("Malloc failed");
            exit(0);
        }
        array[array_size] = '\0';
        
        printf("Here's the intial array:\n");
        
        while (i < array_size)
        {
            array[i] = (rand() % (99 - 10 + 1)) + 10;
            printf("%d ", array[i]);
            i++;
        }
        
        printf("\n\n");
        
        sorting_array(array, array_size);
        
        printf("Here's the sorted array:\n");
        
        i = 0;
        
        while (i < array_size)
        {
            printf("%d ", array[i]);
            i++;
        }
        
        free(array);
            
        printf("\n");
        
        return 0;
        
    }
    
    void   sorting_array (int array[], int array_size)
    {
        int i;
        int j;
        int tmp;
        
        i = 0;
    
        tmp = 0;
        
        while (i < array_size)
        {
            
            j = i + 1;
            
            while (j < array_size)
            {
                if (array[i] > array[j])
                {
                    tmp = array[i];
                    array[i] = array[j];
                    array[j] = tmp;
                    
                }
                
                j++;
            }
            
            i++;
        }
    
    }


    Sachant que les deux programmes fonctionnent, dois-je favoriser le second et ajouter manuellement le caractère de fin de chaîne, ou est-il ajouté automatiquement par le programme en utilisant malloc ?

    Merci d'avance à ceux qui me répondront ! :)

    PS : je sais que j'aurais pu utiliser des boucles for à certains moments, j'ai utilisé while volontairement ;)

    • Partager sur Facebook
    • Partager sur Twitter
      22 août 2021 à 10:22:38

      Je n'ai pas regardé ton code en détail, mais je peux te dire que malloc n'ajoute pas les caractères de fin de chaîne. malloc sert juste à allouer de la mémoire sur le tas.

      Edit : 

      Où veux-tu ajouter des caractères de fin de chaîne ?  Il n'y a aucun tableau de char dans ton code ?!?

      -
      Edité par rouIoude 22 août 2021 à 10:26:30

      • Partager sur Facebook
      • Partager sur Twitter
      ...
        22 août 2021 à 10:45:21

        rouIoude a écrit:

        Je n'ai pas regardé ton code en détail, mais je peux te dire que malloc n'ajoute pas les caractères de fin de chaîne. malloc sert juste à allouer de la mémoire sur le tas.

        Edit : 

        Où veux-tu ajouter des caractères de fin de chaîne ?  Il n'y a aucun tableau de char dans ton code ?!?

        -
        Edité par rouIoude il y a 15 minutes

        Merci pour ta réponse. Effectivement, il n'y a pas de tableau de char. Si je remplace mon allocation dynamique par malloc((array_size + 1) * sizeof(char)), et que j'ajoute le caractère de fin de chaîne, ça va ?

        Pour t'expliquer brièvement, j'ai ajouté un char supplémentaire à l'allocation dynamique, puis j'ai remplis ce char par le caractère \0. C'est bien de cette façon que je dois l'ajouter ?

        • Partager sur Facebook
        • Partager sur Twitter
          22 août 2021 à 12:01:05

          C'est pas bien cohérent tout ça ! Le '\0' sert pour désigner la fin d'une chaîne de caractère, toi tu fais des tableaux d'entiers (int) triés.

          Quel est le rapport entre les deux ?

          Pourquoi à tu besoin d'un caractère de fin de chaîne pour ce tableau ?

          Explique quel est le but de ton code, peut-être ça nous aidera à comprendre ! 

          Boum14 a écrit:

          Si je remplace mon allocation dynamique par malloc((array_size + 1) * sizeof(char)), et que j'ajoute le caractère de fin de chaîne, ça va ?

          Ça va, à condition que le pointeur recevant l'adresse soit un pointeur su char. 

          • Partager sur Facebook
          • Partager sur Twitter
          ...
            22 août 2021 à 16:01:51

            rouIoude a écrit:

            C'est pas bien cohérent tout ça ! Le '\0' sert pour désigner la fin d'une chaîne de caractère, toi tu fais des tableaux d'entiers (int) triés.

            Quel est le rapport entre les deux ?

            Pourquoi à tu besoin d'un caractère de fin de chaîne pour ce tableau ?

            Explique quel est le but de ton code, peut-être ça nous aidera à comprendre ! 

            Boum14 a écrit:

            Si je remplace mon allocation dynamique par malloc((array_size + 1) * sizeof(char)), et que j'ajoute le caractère de fin de chaîne, ça va ?

            Ça va, à condition que le pointeur recevant l'adresse soit un pointeur su char. 


            Les caractères sont au final des valeurs numériques, donc toutes les chaînes, peu importe leur contenu doivent bien avoir une fin, je me trompe ?

            Le but du code est de trier le tableau dans l'ordre croissant (je l'ai écrit dans mon message initial) :)

            • Partager sur Facebook
            • Partager sur Twitter
              22 août 2021 à 16:41:27

              Le '\0' sert uniquement à délimiter la fin d'une chaîne de caractères ! Un tableau d'entiers (int) n'est pas une chaîne de caractère. Il n'a pas besoin de ce délimiteur. Normalement on connait sa taille !

              • Partager sur Facebook
              • Partager sur Twitter
              ...
                22 août 2021 à 16:53:27

                Boum14 a écrit:

                toutes les chaînes, peu importe leur contenu doivent bien avoir une fin

                Sous forme de caractère nul ? Oui.

                Mais pas tous les tableaux.

                Attention : les chaînes de caractères sont des tableaux, mais les tableaux ne sont pas forcément des chaînes de caractères.

                Ici, ton tableau de 'int' n'est pas une chaîne de caractères. Les tableaux qui ne sont pas des chaînes doivent être manipulés en utilisant leur taille (leur nombre d'éléments). Il n'y a donc pas besoin d'élément terminal spécial.

                -
                Edité par robun 22 août 2021 à 16:59:33

                • Partager sur Facebook
                • Partager sur Twitter
                  22 août 2021 à 18:20:59

                  Dans ton code, c'est array_size qui va te dire la longueur de ton tableau.
                  Tu devras conserver cette valeur tout au long de ton code.
                  Un regard rapide me laisse croire que ton code fonctionne.
                  Sais-tu utiliser les boucles for? Ton code dans ta fonction sorting() serait grandement simplifié.
                  • Partager sur Facebook
                  • Partager sur Twitter

                  Le Tout est souvent plus grand que la somme de ses parties.

                    22 août 2021 à 18:48:18

                    En fait, cet indicateur de fin est une valeur que tout le monde met à la fin d'une série de... quelque chose. Il s'agit d'une valeur sentinelle, indiquant aux utilisateurs (les fonctions en général): inutile d'aller plus loin, vous êtes arrivé à la fin de ce que vous cherchiez. Que vous ayez trouvé ou non importe peu, mais inutile d'aller plus loin.

                    Pour une chaine de caractère en C, c'est le caractère 0x00, pour un flux d'entrée, c'est souvent EOF, pour une liste chainée, la valeur NULL, pour une suite de nombres positifs, la première valeur négative (par exemple), etc. Toute valeur arbitraire ne faisant pas partie de "ce que l'on peut rechercher" est valable.

                    Ce qui est important, c'est que tout le monde utilise la même manière de détecter la fin de "ce quelque chose".

                    -
                    Edité par edgarjacobs 22 août 2021 à 18:54:12

                    • Partager sur Facebook
                    • Partager sur Twitter

                    On écrit "j'ai tort", pas "tord" qui est le verbe "tordre" à la 3ème personne de l'indicatif présent

                      22 août 2021 à 19:32:02

                      @edgarjacobs: il y a des circonstances où on doit recourir à ce procédé, mais je ne crois pas que ce soit le cas ici.
                      Tout au long de ce code, on sait la longueur du tableau.
                      J'ai constaté que les while sont utilisés de façon intentionnelle.
                      Je me demande pourquoi. Les vrais problèmes seront noyés dans le reste.
                      On s'apercevrait que des variables sont initialisées inutilement.
                      Ce code ne suit pas les derniers standards de C.
                      Les variables devraient être déclarées et initialisées au moment où on en a besoin.
                      On devrait séparer l'initialisation du tableau avec rand() et son affichage.
                      Puisque le tableau est affiché deux fois, cela pourrait être fait dans une fonction.
                      • Partager sur Facebook
                      • Partager sur Twitter

                      Le Tout est souvent plus grand que la somme de ses parties.

                        22 août 2021 à 20:54:58

                        PierrotLeFou a écrit:

                        Les variables devraient être déclarées et initialisées au moment où on en a besoin...

                        Ça ressemble à du code imposé par certaines écoles donc il n'a pas le choix. C'est pour cela qu'il n'y a pas de for aussi, le while est imposé ...

                        edgarjacobs a écrit:

                        En fait, cet indicateur de fin est une valeur que tout le monde met à la fin d'une série de... quelque chose. Il s'agit d'une valeur sentinelle, 

                        Ici on est sur de simple tableau d'entier. Il est quand même rare de mettre une sentinelle sur ce genre de tableau (hormis les chaînes de caractères) . D'ailleurs, il n'utilise pas de sentinelle pour les afficher. J'ai bien peur que l'on s'égare si on part dans cette direction et qui va ne faire que l'embrouiller.




                        • Partager sur Facebook
                        • Partager sur Twitter
                        ...
                          23 août 2021 à 1:47:21

                          Bon, si on lui impose une façon de coder, on va faire avec.
                          Il faut cependant oublier l'idée d'une sentinelle de fin de tableau.
                          Comme je l'ai dit, on connait la longueur du tableau tout au long du code.
                          Je garde mon idée de séparer l'affichage et de l'évaluation avec rand().
                          Et il est facile de créer une fonction qui affiche. Elle sera utilisée avant et après le tri.
                          Pour ce qui est du tri lui-même, on peut faire une légère amélioration.
                          On fait aller la variable i de 0 à array_size -1 (i<array_size)
                          Si j part de i+1, que vaudra j lorsque i vaudra array_size - 1 ? On fait une boucle inutile.
                          Il faut plutôt mettre (i < array_size-1)
                          • Partager sur Facebook
                          • Partager sur Twitter

                          Le Tout est souvent plus grand que la somme de ses parties.

                            23 août 2021 à 8:26:58

                            Merci pour vos réponses !

                            robun a écrit:

                            Attention : les chaînes de caractères sont des tableaux, mais les tableaux ne sont pas forcément des chaînes de caractères.

                            Ici, ton tableau de 'int' n'est pas une chaîne de caractères. Les tableaux qui ne sont pas des chaînes doivent être manipulés en utilisant leur taille (leur nombre d'éléments). Il n'y a donc pas besoin d'élément terminal spécial.

                            -
                            Edité par robun il y a environ 15 heures

                            Il n'est pas une chaîne de caractère car j'ai utilisé malloc et que je ne sais donc pas si toutes les adresses sont situées les unes à côté des autres, c'est ça ?

                            PierrotLeFou a écrit:

                            Dans ton code, c'est array_size qui va te dire la longueur de ton tableau.
                            Tu devras conserver cette valeur tout au long de ton code.
                            Un regard rapide me laisse croire que ton code fonctionne.
                            Sais-tu utiliser les boucles for? Ton code dans ta fonction sorting() serait grandement simplifié.


                            Yes bien sûr je sais utiliser for, ce sont des expérimentations que je fais :)

                            rouIoude a écrit:

                            PierrotLeFou a écrit:

                            Les variables devraient être déclarées et initialisées au moment où on en a besoin...

                            Ça ressemble à du code imposé par certaines écoles donc il n'a pas le choix. C'est pour cela qu'il n'y a pas de for aussi, le while est imposé ...

                            Ce n'est pourtant pas le cas :) C'est juste des expérimentations personnelles que je fais en suivant des tutos américains, rien à voir avec d'éventuelles écoles :)

                            PierrotLeFou a écrit:

                            Bon, si on lui impose une façon de coder, on va faire avec.
                            Il faut cependant oublier l'idée d'une sentinelle de fin de tableau.
                            Comme je l'ai dit, on connait la longueur du tableau tout au long du code.
                            Je garde mon idée de séparer l'affichage et de l'évaluation avec rand().
                            Et il est facile de créer une fonction qui affiche. Elle sera utilisée avant et après le tri.
                            Pour ce qui est du tri lui-même, on peut faire une légère amélioration.
                            On fait aller la variable i de 0 à array_size -1 (i<array_size)
                            Si j part de i+1, que vaudra j lorsque i vaudra array_size - 1 ? On fait une boucle inutile.
                            Il faut plutôt mettre (i < array_size-1)


                            Oui bien vu ! Je n'avais pas remarqué mais ça paraît logique maintenant, c'est corrigé :)


                            • Partager sur Facebook
                            • Partager sur Twitter
                              23 août 2021 à 9:50:01

                              Boum14 a écrit:

                              Il n'est pas une chaîne de caractère car j'ai utilisé malloc et que je ne sais donc pas si toutes les adresses sont situées les unes à côté des autres, c'est ça ?

                              Les allocation faite avec malloc alloue bien des blocs mémoire consécutif (les adresses de chaque case mémoire du bloc se suivent).

                              Après rien n'interdit d'utiliser un bloc alloué avec malloc pour en faire une chaîne de caractère. 

                              Une chaîne de caractère, c'est un tableau de char avec la particularité de désigner la fin de chaîne par un caractère spécial, le '\0' (le caractère nul). 

                              Boum14 a écrit:

                              rouIoude a écrit:

                              PierrotLeFou a écrit:

                              Les variables devraient être déclarées et initialisées au moment où on en a besoin...

                              Ça ressemble à du code imposé par certaines écoles donc il n'a pas le choix. C'est pour cela qu'il n'y a pas de for aussi, le while est imposé ...

                              Ce n'est pourtant pas le cas :) C'est juste des expérimentations personnelles que je fais en suivant des tutos américains, rien à voir avec d'éventuelles écoles :)

                              Ok, je me suis fait avoir, ton code ressemblait tellement à leur codes.

                              -
                              Edité par rouIoude 23 août 2021 à 10:00:58

                              • Partager sur Facebook
                              • Partager sur Twitter
                              ...
                                23 août 2021 à 11:02:11

                                Juste pour rajouter quelques précisions :

                                • En C, un tableau A de n éléments de types T c'est toujours, du point de vue du programme C,  n objets de type T consécutifs en mémoire, toujours ;
                                • L'opérateur sizeof permet d'obtenir la taille en byte de son opérande. Je parle de byte et non d'octets car en C le byte est la taille d'un char et celle-là n'est pas forcément égale à 8. La taille d'un byte est donné par la macro CHAR_BIT qui donne le nombre de bits codant un char ;
                                • On peut déterminer la taille (en byte) d'un tableau grâce à l'opérateur sizeof. Pour connaître le nombre d'éléments qu'un tableau peut contenir il suffit de diviser la taille du tableau par la taille du type des éléments du tableau ;

                                • En C, pointeur et tableau sont deux choses différentes. Un pointeur, quel que soit l'objet pointé, aura toujours une taille prédéterminée et fixe sur les plateformes usuelles (il peut y avoir des subtilités entre pointeurs sur données et pointeurs sur fonctions, pointeurs proches et éloignés, mais on passe ça ici). Il est impossible de connaître la taille du bloc alloué de façon programmatique de manière standard.
                                • tableaux et pointeurs sont souvent confondus car en C les passages de paramètres se font par copie. Pour des raisons de performances les créateurs du langage ont décidé de ne pas passer les tableaux par copie (trop lourd pour les gros tableaux) mais en passant de manière transparente un pointeur sur le premier élément du tableau ;

                                Comme on trimballe souvent les tableaux de fonctions à fonctions, on perd la capacité de déterminer simplement leur taille. C'est pourquoi on utilise deux stratégies différentes :

                                • on passe en plus du paramètre tableau un paramètre taille. En C moderne on pourrait trouver par exemple une fonction déclarée ainsi :


                                  void sort(size_t size, int array[size]);
                                  
                                  // qui est totalement équivalent à 
                                  void sort(size_t size, int array[]);
                                  
                                  //ou
                                  void sort(size_t size, int *array);
                                  
                                • On indique par une valeur sentinelle la fin du tableau. Il faut que cette valeur soit réservée à cet unique usage. Par exemple, si on prend la convention qu'un tableau de chaînes se termine par un pointeur NULL alors on pourrait imaginer une fonction du genre :

                                  void display(char *strings[])
                                  {
                                      while (*strings)
                                          puts(*strings++);
                                  }

                                Remarques :

                                • le paramètre argv de main suit les deux conventions simultanément : on donne à la fois sa taille dans argc et c'est un tableau de chaînes de caractères terminé par un pointeur NULL ⇒ on a toujours argv[argc] == NULL. Cet exemple montre qu'il y a une différence entre capacité et taille … mais passons ;
                                • il ne faut pas confondre le caractère NUL (aka 0 aka '\0') avec le pointeur NULL !
                                • le choix fait par les créateurs de C d'encoder les chaînes de caractères par un tableau dont la fin est marquée par un caractère NUL a été fait pour des raisons d'économie de mémoire en gardant à l'esprit qu'une chaîne n'était souvent qu'affichée. Il est moins coûteux en mémoire d'utilise un byte pour marque la fin de chaînes arbitrairement longues que d'utiliser un entier pour indiquer la taille (choix fait par pascal) qui limite la taille des chaines et peut coûter plus qu'un byte;
                                • malloc alloue toujours un bloc mémoire, les adresses valides commencent à l'endroit pointé par le pointeur donné par malloc jusqu'à au moins la taille demandée si le pointeur renvoyé n'est pas NULL.

                                Edit: typo

                                -
                                Edité par White Crow 23 août 2021 à 11:19:29

                                • Partager sur Facebook
                                • Partager sur Twitter
                                  23 août 2021 à 11:38:31

                                  Juste pour bien préciser :

                                  Boum14 a écrit:

                                  Il n'est pas une chaîne de caractère car j'ai utilisé malloc et que je ne sais donc pas si toutes les adresses sont situées les unes à côté des autres, c'est ça ?

                                  Je dirais plutôt :

                                  Il est un tableau car j'ai utilisé malloc et que je sais donc sa taille et que toutes les adresses sont situées les unes à côté des autres.

                                  • Ce n'est pas une chaîne de caractères parce que c'est un tableau de 'int' (dynamique ou pas, si c'est un tableau de 'int', ce n'est pas une chaîne de caractères).
                                  • La taille est donnée dans le 'malloc'. On la connaît, il faut l'utiliser.
                                  • C'est un tableau donc, forcément, les éléments sont les uns à la suite des autres.

                                  -
                                  Edité par robun 23 août 2021 à 11:41:45

                                  • Partager sur Facebook
                                  • Partager sur Twitter
                                    23 août 2021 à 17:01:29

                                    Merci à vous pour vos réponses, j'ai tout lu et c'est intéressant.

                                    J'ai l'impression qu'on a cependant beaucoup dévié du sujet initial :D

                                    Je vais donc pratiquer davantage tout en me renseignant sur les subtilités du langage !

                                    • Partager sur Facebook
                                    • Partager sur Twitter

                                    Caractère de fin de chaîne en allocation dynamique

                                    × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                                    × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                                    • Editeur
                                    • Markdown