47 votes

Où les expressions et les constantes sont-elles stockées, sinon en mémoire?

De Langage de Programmation C par Brian W. Kernighan

opérateur & ne s'applique qu'à des objets en mémoire: les variables et les tableau éléments. Il ne peut pas être appliquée à des expressions, des constantes ou vous inscrire les variables.

Où sont les expressions et les constantes stockées si ce n'est dans la mémoire? Que signifie cette citation veut dire?

E. g:
&(2 + 3)

Pourquoi ne pouvons-nous pas prendre son adresse? Où est-il stocké?
Sera la réponse sera identique pour le C++ aussi, puisque C est sa mère?

Lié question explique que ces expressions sont rvalue objets et tous rvalue objets n'ont pas d'adresses.

Ma question est de savoir où ces expressions sont stockées telles que leurs adresses ne peuvent pas être récupérés?

63voto

aaaaaa123456789 Points 1251

Considérons la fonction suivante:

unsigned sum_evens (unsigned number) {
  number &= ~1; // ~1 = 0xfffffffe (32-bit CPU)
  unsigned result = 0;
  while (number) {
    result += number;
    number -= 2;
  }
  return result;
}

Maintenant, nous allons jouer le compilateur jeu et essayer de le compiler à la main. Je vais supposer que vous êtes en utilisant x86 parce que c'est ce que la plupart des ordinateurs de bureau utilisation. (x86 est le jeu d'instructions Intel Processeurs compatibles.)

Nous allons passer par un simple (unoptimized) version de comment cette routine pourrait ressembler lors de la compilation:

sum_evens:
  and edi, 0xfffffffe ;edi is where the first argument goes
  xor eax, eax ;set register eax to 0
  cmp edi, 0 ;compare number to 0
  jz .done ;if edi = 0, jump to .done
.loop
  add eax, edi ;eax = eax + edi
  sub edi, 2 ;edi = edi - 2
  jnz .loop ;if edi != 0, go back to .loop
.done
  ret ;return (value in eax is returned to caller)

Maintenant, comme vous pouvez le voir, l'une des constantes dans le code (0, 2, 1) en fait dans le cadre des instructions du PROCESSEUR! En fait, 1 ne pas s'afficher du tout; le compilateur (dans ce cas, il suffit de m') déjà calcule ~1 et utilise le résultat dans le code.

Alors que vous pouvez prendre l'adresse d'un PROCESSEUR d'instruction, il fait souvent pas de sens de prendre l'adresse d'une partie de celui-ci (en x86 parfois, vous pouvez, mais dans de nombreux autres Processeurs que vous ne peut tout simplement pas faire cela à tous), et les adresses de code sont fondamentalement différents de données d'adresses (qui est pourquoi vous ne pouvez pas traiter un pointeur de fonction (une adresse de code) en tant que régulier pointeur (une adresse de données)). Dans certaines architectures des processeurs, du code des adresses et des données d'adresses sont totalement incompatibles (bien que ce n'est pas le cas de x86 de la manière la plus moderne des Systèmes d'exploitation de l'utiliser).

Notez qu' while (number) est équivalent à while (number != 0). Qu' 0 n'apparaît pas dans le code compilé à tous! Il est implicite par l' jnz instruction (saut si pas de zéro). C'est une autre raison pourquoi vous ne pouvez pas prendre l'adresse de celui - 0 - il n'en a pas, c'est littéralement de nulle part.

J'espère que cela le rend plus clair pour vous.

42voto

Useless Points 18909

où ces expressions sont stockées telles que les adresses ne peuvent pas être récupérés?

Votre question n'est pas bien formé.

  • Sur le plan conceptuel

    C'est comme demander pourquoi les gens peuvent discuter de la propriété des noms, mais pas des verbes. Les noms se réfèrent à des choses qui peuvent (éventuellement) être détenue, et les verbes se référer à des actions qui sont effectuées. Vous ne pouvez pas posséder une action ou d'effectuer une chose.

  • En termes de spécification de langage

    Les Expressions ne sont pas stockées dans la première place, ils sont évalués. Ils peuvent être évalués par le compilateur, au moment de la compilation, ou ils peuvent être évalués par le processeur, au moment de l'exécution.

  • En termes de mise en œuvre de la langue

    Considérons la déclaration

    int a = 0;
    

    Cela ne signifie deux choses: tout d'abord, il déclare une variable de type entier a. Ce n'est défini pour être quelque chose dont l'adresse vous pouvez prendre. C'est au compilateur de faire quelque chose sur une plate-forme donnée, pour permettre à vous de prendre l'adresse de l' a.

    Deuxièmement, il établit que la valeur de la variable à zéro. Cela ne veut pas dire un nombre entier avec une valeur de zéro existe quelque part dans votre programme compilé. Il peut souvent être mis en œuvre comme

    xor eax,eax
    

    qui est-à-dire, XOR (ou-exclusif) l' eax s'inscrire avec lui-même. Cela aboutit toujours à zéro, tout ce qui était là avant. Cependant, il n'est pas fixe, objet de valeur, en 0 dans le code compilé pour correspondre à l'entier littéral 0 vous avez écrit dans la source.

En aparté, quand je dis qu' a ci-dessus est quelque chose dont l'adresse vous pouvez prendre, il est bon de rappeler qu'il ne peut pas vraiment avoir une adresse , à moins que vous le prenez. Par exemple, l' eax registre utilisé dans cet exemple n'est pas une adresse. Si le compilateur ne peut pas prouver que le programme est toujours correct, a peut vivre toute sa vie dans ce registre et n'existe jamais dans la mémoire principale. A l'inverse, si vous utilisez l'expression &a quelque part, le compilateur prendra soin de créer de l'espace adressable pour stocker as'valeur.


Note pour la comparaison que je peux facilement choisir une autre langue où je peux prendre l'adresse d'une expression.

Il va probablement être interprété, car la compilation généralement les rejets de ces structures, une fois que la machine-sortie exécutable remplace. Par exemple Python a exécution de l'introspection et de l' code objets.

Ou je peut commencer à partir de LISP et de l'étendre à assurer une sorte de addressof opération sur les S-expressions.

La clé de la chose qu'ils ont en commun est qu'ils ne sont pas C, qui, comme une question de conception et de définition ne fournit pas ces mécanismes.

10voto

Lundin Points 21616

De telles expressions finissent par faire partie du code machine. Une expression 2 + 3 probablement traduite en instruction de code machine "charge 5 dans le registre A". Les registres de la CPU n'ont pas d'adresse.

5voto

Broman Points 5642

Il ne fait pas vraiment de sens de prendre l'adresse d'une expression. La chose la plus proche que vous pouvez faire est un pointeur de fonction. Les Expressions ne sont pas stockés dans le même sens que les variables et les objets.

Les Expressions sont stockées dans la machine réelle, code. Bien sûr, vous pourriez trouver l'adresse où l'expression est évaluée, mais il suffit de ne pas faire de sens de le faire.

Lire un peu plus sur l'assemblée. Les Expressions sont stockées dans le segment de texte, tandis que les variables sont stockées dans d'autres secteurs, comme les données ou la pile.

https://en.wikipedia.org/wiki/Data_segment

Une autre façon de l'expliquer est que les expressions sont des instructions du processeur, tandis que les variables sont de pure data.

Une chose de plus à prendre en compte: Le compilateur souvent optimise loin les choses. Considérer ce code:

int x=0;
while(x<10)
    x+=1;

Ce code probobly être optimisé pour:

int x=10;

Alors, qu'aurait l'adresse d' (x+=1) moyenne dans ce cas? Il n'est même pas présent dans le code de l'ordinateur, de sorte qu'il est - par définition - pas d'adresse à tous.

4voto

Basile Starynkevitch Points 67055

Où sont les expressions et les constantes stockées si ce n'est dans la mémoire

Dans certains (en fait plusieurs) des cas, une expression constante est pas stocké à tous. En particulier, pensez à l'optimisation des compilateurs, et de voir CppCon 2017: Matt Godbolt de parler ", Ce qui A Mon Compilateur Fait pour Moi Dernièrement? Déverrouiller le Compilateur du Couvercle"

Dans votre cas en particulier de certains C du code 2 + 3, plus l'optimisation des compilateurs aurait constante plié en 5, et 5 de la constante peut être juste à l'intérieur de certaines machine code d'instruction (comme certains le champ de bits) de votre segment de code et de ne pas même avoir un sens bien défini à l'emplacement de la mémoire. Si la constante 5, est une boucle de limite, certains compilateurs pourrait avoir fait déroulement de la boucle, et cette constante ne plus apparaître dans le code binaire.

Voir aussi cette réponse, etc...

Être conscient que le C11 est une spécification écrite en anglais. Lire son n1570 standard. Lire aussi les beaucoup plus de spécifications de C++11 (ou version ultérieure).

Prendre l'adresse d'une constante est interdit par la sémantique de C (et C++).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X