Pourquoi une fonction peut-elle retourner une configuration de tableau par malloc mais pas une configuration par «int cat = {0,0,0};»

Pourquoi puis-je retourner d’une fonction un tableau configuré par malloc:

int *dog = (int*)malloc(n * sizeof(int)); 

mais pas une configuration de tableau par

  int cat[3] = {0,0,0}; 

Le tableau “cat []” est renvoyé avec un avertissement.

Merci à tous pour votre aide

    Un programme en cours d’exécution comporte deux parties clés de la mémoire: la stack et le tas . La stack est également appelée stack d’appels .

    Lorsque vous effectuez un appel de fonction, les informations sur les parameters, le lieu de retour et toutes les variables définies dans l’étendue de la fonction sont placées dans la stack. (Auparavant, les variables C ne pouvaient être définies qu’au début de la fonction. Principalement parce que cela simplifiait la vie des rédacteurs du compilateur.)

    Lorsque vous revenez d’une fonction, tout ce qui est sur la stack est supprimé et est parti (et bientôt, lorsque vous ferez d’autres appels de fonction, vous écraserez cette mémoire afin que vous ne vouliez pas la pointer dessus!)

    Chaque fois que vous allouez de la mémoire, vous allouez si du tas . C’est une autre partie de la mémoire, maintenue par le gestionnaire d’allocation. Une fois que vous “réservez” une partie de celle-ci, vous en êtes responsable et si vous souhaitez arrêter de pointer dessus, vous êtes censé en informer le responsable. Si vous laissez tomber le pointeur et que vous ne pouvez plus demander qu’il soit publié, c’est une fuite .

    Vous êtes également censé ne regarder que la partie de la mémoire que vous avez dite vouloir. Écraser non seulement la partie que vous aviez souhaitée, mais bien avant (ou avant) cette partie de la mémoire est une technique classique des exploits: écrire des informations dans une partie de la mémoire contenant des instructions informatiques au lieu de données. La connaissance de la manière dont le compilateur et le moteur d’exécution gèrent les choses aide les experts à comprendre comment procéder. Des systèmes d’exploitation bien conçus les en empêchent.

    tas:

     int *dog = (int*)malloc(n*sizeof(int*)); 

    emstackr:

     int cat[3] = {0,0,0}; 

    C’est une question de scope.

     int cat[3]; // declares a local variable cat 

    Variables locales versus mémoire malloc’d

    Les variables locales existent sur la stack. Lorsque cette fonction reviendra, ces variables locales seront détruites. À ce stade, les adresses utilisées pour stocker votre tableau sont recyclées. Vous ne pouvez donc rien garantir quant à leur contenu.

    Si vous appelez malloc, vous allouerez à partir du tas, de sorte que la mémoire persistera au-delà de la durée de vie de votre fonction.

    Si la fonction est supposée renvoyer un pointeur (dans ce cas, un pointeur sur int, qui est la première adresse du tableau d’entiers), ce pointeur doit pointer sur une bonne mémoire. Malloc est le moyen d’assurer cela.

    Éviter Malloc

    Vous n’êtes pas obligé d’appeler malloc à l’intérieur de votre fonction (bien que ce soit normal et approprié de le faire).

    Vous pouvez également passer une adresse dans votre fonction censée contenir ces valeurs. Votre fonction effectuerait le travail de calcul des valeurs et remplirait la mémoire à l’adresse donnée, puis elle reviendrait.

    En fait, c’est un schéma commun. Si vous procédez ainsi, vous constaterez que vous n’avez pas besoin de renvoyer l’adresse, car vous connaissez déjà l’adresse en dehors de la fonction que vous appelez. De ce fait, il est plus courant de renvoyer une valeur indiquant le succès ou l’échec de la routine, comme un int, que de renvoyer l’adresse des données pertinentes.

    De cette façon, l’appelant de la fonction peut savoir si les données ont été renseignées avec succès ou si une erreur s’est produite.

     #include  // include stdio for the printf function int rainCats (int *cats); // pass a pointer-to-int to function rainCats int main (int argc, char *argv[]) { int cats[3]; // cats is the address to the first element int success; // declare an int to store the success value success = rainCats(cats); // pass the address to the function if (success == 0) { int i; for (i=0; i<3; i++) { printf("cat[%d] is %d \r", i, cats[i]); getchar(); } } return 0; } int rainCats (int *cats) { int i; for (i=0; i<3; i++) { // put a number in each element of the cats array cats[i] = i; } return 0; // return a zero to signify success } 

    Pourquoi ça marche

    Notez que vous n'avez jamais eu à appeler malloc ici car les chats [3] ont été déclarés à l'intérieur de la fonction principale. Les variables locales dans main ne seront détruites qu'à la sortie du programme. À moins que le programme ne soit très simple, malloc sera utilisé pour créer et contrôler la durée de vie d'une structure de données.

    Notez également que rainCats est codé en dur pour renvoyer 0. Rien ne se produit à l’intérieur de rainCats qui risquerait de l’échouer, tel que la tentative d’access à un fichier, une requête réseau ou d’autres allocations de mémoire. Les programmes plus complexes ont de nombreuses raisons pour échouer, il existe donc souvent une bonne raison pour renvoyer un code de succès.

    Parce que int cat[3] = {0,0,0}; déclare une variable automatique qui n’existe que pendant l’appel de la fonction.

    Il existe une “dispense” spéciale en C pour les tableaux automatiques de caractères entrés, afin que les chaînes entre guillemets puissent être renvoyées, mais cela ne se généralise pas aux autres types de tableaux.

    cat [] est alloué sur la stack de la fonction que vous appelez, lorsque cette stack est libérée, cette mémoire est libérée (lorsque la fonction renvoie, la stack doit être considérée comme libérée).

    Si vous voulez remplir un tableau d’entiers dans le cadre d’appel, passez un pointeur sur un contrôle que vous contrôlez à partir du cadre d’appel.

     void somefunction() { int cats[3]; findMyCats(cats); } void findMyCats(int *cats) { cats[0] = 0; cats[1] = 0; cats[2] = 0; } 

    Bien sûr, cela est artificiel et j’ai codé en dur que la longueur du tableau est de 3 mais c’est ce que vous devez faire pour obtenir les données d’une fonction invoquée.

    Une valeur unique fonctionne car elle est recopiée dans la trame d’appel;

     int findACat() { int cat = 3; return cat; } 

    Dans findACat 3 est copié de findAtCat dans la trame appelante car il s’agit d’une quantité connue, le compilateur peut le faire pour vous. Les données pointées par un pointeur ne peuvent pas être copiées car le compilateur ne sait pas quelle quantité copier.

    Lorsque vous définissez une variable comme “cat”, le compilateur lui atsortingbue une adresse. L’association entre le nom et l’adresse n’est valide que dans le cadre de la définition. Dans le cas des variables auto, cette scope est le corps de la fonction à partir du sharepoint définition.

    Les variables automatiques sont allouées sur la stack. La même adresse sur la stack est associée à différentes variables à des moments différents. Lorsque vous retournez un tableau, ce qui est réellement renvoyé est l’adresse du premier élément du tableau. Malheureusement, après le retour, le compilateur peut et va réutiliser ce stockage à des fins totalement indépendantes. Ce que vous verriez au niveau du code source serait votre variable renvoyée changeant mystérieusement sans raison apparente.

    Maintenant, si vous devez vraiment retourner un tableau initialisé, vous pouvez déclarer ce tableau comme statique. Une variable statique a une allocation de stockage permanente plutôt que temporaire. N’oubliez pas que la même mémoire sera utilisée lors d’appels successifs à la fonction. Par conséquent, il peut être nécessaire de copier les résultats de l’appel précédent ailleurs avant de passer l’appel suivant.

    Une autre approche consiste à passer le tableau en argument et à l’écrire dans votre fonction. La fonction appelante est alors propriétaire de la variable et les problèmes liés aux variables de stack ne se posent pas.

    Cela n’a aucun sens si vous n’étudiez pas attentivement le fonctionnement de la stack. Bonne chance.

    Vous ne pouvez pas retourner un tableau. Vous retournez un pointeur . Ce n’est pas la même chose.

    Vous pouvez renvoyer un pointeur sur la mémoire allouée par malloc() car malloc() a alloué la mémoire et l’a réservée pour utilisation par votre programme jusqu’à ce que vous utilisiez explicitement free() pour la désallouer.

    Vous ne pouvez pas renvoyer de pointeur sur la mémoire allouée par un tableau local car dès que la fonction se termine, le tableau local n’existe plus.

    C’est une question de durée de vie d’ object – pas de scope, de stack ou de tas. Bien que ces termes soient liés à la durée de vie d’un object, ils ne sont pas équivalents à la durée de vie et c’est la durée de vie de l’object que vous retournez qui est importante. Par exemple, un object alloué dynamicment a une durée de vie allant de l’allocation à la désallocation. La durée de vie d’une variable locale peut se terminer à la fin de la scope de la variable, mais si elle est statique, sa durée de vie ne s’arrête pas là.

    La durée de vie d’un object alloué avec malloc() jusqu’à ce que cet object soit libéré à l’aide de la fonction free() . Par conséquent, lorsque vous créez un object à l’aide de malloc() , vous pouvez légitimement renvoyer le pointeur sur cet object tant que vous ne l’avez pas libéré – il sera toujours actif à la fin de la fonction. En fait, vous devez faire attention à faire quelque chose avec le pointeur pour qu’il soit mémorisé quelque part ou il en résulterait une fuite.

    La durée de vie d’une variable automatique se termine à la fin de l’étendue de la variable (l’étendue est donc liée à la durée de vie). Par conséquent, il est inutile de renvoyer un pointeur sur un tel object depuis une fonction – le pointeur sera invalide dès le retour de la fonction.

    Désormais, si votre variable locale est static au lieu d’automatique, sa durée de vie s’étend au-delà de sa scope (sa scope n’est donc pas équivalente à sa durée de vie). Ainsi, si une fonction a une variable statique locale, l’object sera toujours actif, même lorsque la fonction est renvoyée, et il serait légitime de renvoyer un pointeur sur un tableau statique à partir de votre fonction. Bien que cela pose un tout nouvel ensemble de problèmes car il n’ya qu’une instance de cet object, le fait de le renvoyer plusieurs fois à partir de la fonction peut poser des problèmes de partage des données (cela ne fonctionne en principe que si les données ne changent pas après l’initialisation ou là règles claires pour quand cela peut et ne peut pas changer).

    Un autre exemple tiré d’une autre réponse concerne les littéraux de chaîne. Les pointeurs qui les pointent peuvent être renvoyés d’une fonction non pas à cause d’une règle d’étendue, mais à cause d’une règle qui dit que les littéraux de chaîne ont une durée de vie qui s’étend jusqu’à la fin du programme.