10
votes

Essayer de comprendre comment fonctionne un type de retour de pointeur fonctionne

J'essaie de comprendre comment le retour d'un pointeur fonctionne dans les scénarios suivants:

5
This string was created in the function.


1 commentaires

Essayez également: Char * stringfromfunction () {Char [] preturn = "Cette chaîne a été créée dans la fonction."; Return de retour; }


6 Réponses :


1
votes

Oui, les pointeurs des littéraux à cordes sont sans danger pour être transmis, tant que vous ne les lisez que d'eux. C'est parce qu'ils sont fondamentalement alloués statiquement par le compilateur, comme celui-ci: xxx pré>

si vous voulez faire sauter les choses, essayez d'allouer un tableau réel dans la fonction au lieu d'obtenir un pointeur sur le String littéral. P>

char* StringFromFunction()
{
    char myString[6] = {'a', ' ',  'l', 'i', 't', '\0'};
    return &myString[0];
}


0 commentaires

1
votes

littéraux de chaîne comme "Cette chaîne a été créée dans la fonction." est placé en mémoire en lecture seule. Vous n'êtes autorisé que d'attribuer à un char * pour la compatibilité en arrière, il est plus approprié d'utiliser const char * qui reflète avec précision leur nature.


0 commentaires

4
votes

Les littéraux de chaîne ne sont pas réellement stockés sur la pile pour la fonction comme des variables automatiques, mais sont stockées sur un emplacement spécial (comme des variables globales).

Notez que l'écriture à eux n'est pas portable, il est donc préférable de les utiliser comme const char * et non char * . .


3 commentaires

Est-ce que cet "emplacement spécial" est partie du tas, ou est-il séparé de la pile / du tas?


@ The111 séparé. C'est une section spéciale de l'exécutable, comme la section de code. Dans les fichiers ELF (utilisés sous Linux et d'autres endroits), il s'appelle .rodata . Voir la réponse de Sarnold pour plus d'informations.


@ The111 Ils sont généralement stockés dans la section en lecture seule de l'exécutable.



3
votes

Lorsque je compile votre programme, je reçois un avertissement supplémentaire de g ++ code>, mon compilateur: xxx pré>

Pour éviter l'avertissement, ajouter const Code> devant la déclaration de la variable, le type de retour de la fonction et la variable dans la fonction principale () code>. p>

La boîte à outils GNU stockera le Cette chaîne a été créée dans la fonction. CODE> String dans la section de données .RODATA code> Lecture seule, qui est valide pour la durée de vie du programme: P>

$ readelf -p .rodata strings

String dump of section '.rodata':
  [     8]  This string was created in the function.


0 commentaires

3
votes

Le moyen le plus simple de voir la différence consiste à générer le disséasemblage d'un simple bonjour-world-ish exemple: xxx pré>

ceci est le diassemblage avec GCC dans FreeBSD avec optimisation éteinte P>

    .file   "hellow.c"
    .section    .rodata
.LC0:
    .string "test"
    .text
    .p2align 4,,15
.globl test
    .type   test, @function
test:
    pushl   %ebp
    movl    %esp, %ebp
    movl    $.LC0, %eax
    popl    %ebp
    ret
    .size   test, .-test
    .p2align 4,,15
.globl main
    .type   main, @function
main:
    leal    4(%esp), %ecx
    andl    $-16, %esp
    pushl   -4(%ecx)
    pushl   %ebp
    movl    %esp, %ebp
    pushl   %ecx
    call    test
    movl    $0, %eax
    popl    %ecx
    popl    %ebp
    leal    -4(%ecx), %esp
    ret
    .size   main, .-main
    .ident  "GCC: (GNU) 4.2.1 20070719  [FreeBSD]"


3 commentaires

Cela pourrait être intéressant aussi, mais un peu plus avancé: cs.umd.edu/class/sum2003/cmsc311/notes/mips/dataseg.html


Merci pour cela. Vous venez de m'aider à comprendre beaucoup de réponses. Par exemple, je sais maintenant ce que Rodata fait référence, et je peux maintenant voir comment il existe une zone dans l'exécutable où les littéraux de chaîne sont réellement stockés et la fonction n'a pas réellement créé le littéral à chaîne. Cela me conduit à une autre question. Cela signifie-t-il que je peux accéder au littéral à chaîne sans jamais appeler la fonction?


Oui, vous pouvez dans le même sens que vous pouvez accéder à toutes les variables globales sans les référencer jamais (elles entreraient dans le segment .Data) - sans optimisation. Je ne sais pas que ce compilateur ne les optimise pas si activé - comme cela n'incluait pas la fonction de test si elle n'est pas appelée jamais. Il serait difficile d'y accéder si vous n'avez pas de pointeur. Vous auriez besoin d'astuces sales ou d'assemblage inliné.



2
votes

La principale chose à comprendre est la la vie de l'objet que vous retournez un pointeur (en réalité, l'objet vie est une chose essentielle à comprendre à peu près n'importe quelle instance d'objet). La norme C utilise la terminologie de «durée de stockage» pour la durée de vie de l'objet, car dans C un objet est littéralement une région de stockage de données qui représente des valeurs.

Un littéral à chaîne a une "durée de stockage statique", ce qui signifie (C99 6.2.4 / 3):

sa durée de vie est l'ensemble de l'exécution du programme et sa valeur stockée n'est initialisée qu'une seule fois, avant le démarrage du programme.

Il n'y a donc aucun problème à renvoyer un pointeur sur un littéral à chaîne d'une fonction (aussi loin que la durée de vie de l'objet, le pointeur se réfère à Go). L'objet littéral à chaîne sera toujours un objet valide. Une chose veille à ce que le pointeur retourné permettra à quelqu'un d'essayer de modifier la gamme de données contenant le littéral à chaîne, qui n'est pas autorisé (ce qui est un comportement indéfini).

Le local int renvoyéValue variable de l'autre exemple a la "durée de stockage automatique" qui signifie (C99 6.2.4 / 4):

sa durée de vie s'étend de l'entrée dans le bloc avec lequel elle est associée jusqu'à ce que l'exécution de ce bloc se termine de quelque manière que ce soit

(Notez que la durée de vie d'une matrice de longueur variable automatique est légèrement différente).

Donc, le pointeur sur retourvalue devient invalide le moment où la fonction renvoie.

Je pense que la durée de vie de l'objet est l'une des choses fondamentales que chaque programmeur doit comprendre, et il est particulièrement important dans C et C ++, car le programmeur est largement responsable de la manipulation correctement, surtout lorsqu'il s'agit d'un pointeur.


1 commentaires

+1 pour décrire correctement pourquoi le comportement est la façon dont il est au lieu de simplement disséquer ce que du comportement de la mise en œuvre arbitraire est.