Question

En C ++, les variables locales sont toujours allouées sur la pile. La pile est une partie de la mémoire a permis que votre application peut occuper. Cette mémoire est conservée dans votre RAM (sinon troqué le disque). Maintenant, est-ce un compilateur C de créer toujours le code assembleur qui stocke les variables locales sur la pile?

Prenons, par exemple, le simple code suivant:

int foo( int n ) {
   return ++n;
}

Dans MIPS code assembleur, cela pourrait ressembler à ceci:

foo:
addi $v0, $a0, 1
jr $ra

Comme vous pouvez le voir, je ne l'ai pas besoin d'utiliser la pile du tout pour n. Est-ce que le compilateur C ++ reconnaître que, et utiliser directement les registres du CPU?

Modifier Wow, merci beaucoup pour vos réponses presque immédiates et étendues! Le corps de la fonction de foo doit bien sûr être return ++n;, pas return n++;. :)

Était-ce utile?

La solution

Disclaimer: Je ne sais pas MIPS, mais je ne sais que certains x86, et je pense que le principe devrait être le même ..

Dans la convention d'appel habituel fonction, le compilateur va pousser la valeur de n sur la pile pour passer à la fonction foo. Cependant, il y a la convention fastcall que vous pouvez utiliser pour dire à gcc pour passer la valeur à travers les registres à la place. (MSVC a également cette option, mais je ne suis pas sûr de ce que sa syntaxe est.)

test.cpp:

int foo1 (int n) { return ++n; }
int foo2 (int n) __attribute__((fastcall));
int foo2 (int n) {
    return ++n;
}

Compiler ci-dessus avec g++ -O3 -fomit-frame-pointer -c test.cpp, je reçois pour foo1:

mov eax,DWORD PTR [esp+0x4]
add eax,0x1
ret

Comme vous pouvez le voir, il lit la valeur de la pile.

Et voici foo2:

lea eax,[ecx+0x1]
ret

Maintenant, il prend directement la valeur du registre.

Bien sûr, si vous en ligne la fonction du compilateur fera une simple addition dans le corps de votre fonction plus grande, quelle que soit la convention d'appel que vous spécifiez. Mais quand vous ne pouvez pas obtenir inline, cela va se passer.

Disclaimer 2: Je ne dis pas que vous devriez deviner sans cesse le compilateur. Il est probablement pas pratique et nécessaire dans la plupart des cas. Mais ne présumez pas produit code parfait.

Modifier 1:. Si vous parlez de simples variables locales (fonction pas d'arguments), alors oui, le compilateur de les affecter dans les registres ou sur la pile comme il l'entend

Edit 2: Il semble que la convention d'appel est spécifique à l'architecture, et MIPS passera les quatre premiers arguments sur la pile, comme Richard Pennington a déclaré dans sa réponse. Donc, dans votre cas, vous ne devez pas spécifier l'attribut supplémentaire (qui est en fait un attribut spécifique x86).

Autres conseils

Oui. Il n'y a pas de règle que les « variables sont toujours allouées sur la pile ». Le standard C ++ ne dit rien sur un stack.It ne suppose pas qu'une pile existe, ou qui existent registres. Il dit à quel point le code doit se comporter, comment il devrait être mis en œuvre.

Le compilateur stocke que des variables sur la pile quand il doit - quand ils doivent vivre après un appel de fonction, par exemple, ou si vous essayez de prendre l'adresse d'entre eux

.

Le compilateur est pas stupide. ;)

Oui, un bon, l'optimisation C / C ++ optimisera que. Et même BEAUCOUP plus: Voir ici: Felix von Leitners compilateur de l'enquête.

Un compilateur C normal / C ++ ne sera pas mis toutes les variables sur la pile de toute façon. Le problème avec votre fonction foo() pourrait être que la variable pourrait se passer par la pile à la fonction (l'ABI de votre système (matériel / OS) définit que).

Avec le mot-clé register de C vous pouvez donner le compilateur indice qu'il serait probablement bon de stocker une variable dans un registre. Echantillon:

register int x = 10;

Mais rappelez-vous: Le compilateur est libre de ne pas stocker x dans un registre si elle veut

La réponse est oui, peut-être. Cela dépend du compilateur, le niveau d'optimisation, et le processeur cible.

Dans le cas des MIPS, les quatre premiers paramètres, si petits, sont passés dans les registres et la valeur de retour est retourné dans un registre. Ainsi, votre exemple n'a pas besoin d'affecter quoi que ce soit sur la pile.

En fait, la vérité est plus étrange que la fiction. Dans votre cas, le paramètre est retourné inchangé: la valeur retournée est celle de n avant que l'opérateur ++:

foo:
    .frame  $sp,0,$ra
    .mask   0x00000000,0
    .fmask  0x00000000,0

    addu    $2, $zero, $4
    jr      $ra
    nop

Depuis votre exemple fonction foo est une fonction d'identité (il retourne juste c'est l'argument), mon compilateur C ++ (VS 2008) supprime complètement cet appel de fonction. Si je change à:

int foo( int n ) {
   return ++n;
}

le compilateur inline avec cette

lea edx, [eax+1] 

Oui, Les registres sont utilisés en C ++. Le MDR (Les registres de données de mémoire) contient les données étant extraites et stockées. Par exemple, pour récupérer le contenu de la cellule 123, nous remplissions la valeur 123 (en binaire) dans le MAR et effectuer une opération de récupération. Une fois l'opération terminée, une copie du contenu de la cellule 123 serait dans le MDR. Pour stocker la valeur 98 dans la cellule 4, on charge un 4 dans le MAR et 98 dans le MDR et de réaliser un magasin. Lorsque l'opération terminée, le contenu de la cellule 4 aura été fixée à 98, en éliminant tout ce qui était là auparavant. Les registres de données et adresse travailler avec eux pour y parvenir. En C ++ aussi, quand on initialise un var avec une valeur ou demander sa valeur, le même phénomène se passe-t.

Et, une chose, moderne Compilateurs effectuer également l'allocation des registres, ce qui est un peu plus rapide que l'allocation de mémoire.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top