web-dev-qa-db-fra.com

L'adresse d'une variable locale est-elle constexpr?

Dans le livre de Bjarne Stroustrup "The C++ Programming Language (4th Edition)" à la p. 267 (Section 10.4.5 Expressions de constantes d'adresse), il utilise un exemple de code où l'adresse d'une variable locale est définie sur une variable constexpr. Je pensais que cela semblait étrange, j'ai donc essayé d'exécuter l'exemple avec g ++ version 7.3.0 et je n'ai pas pu obtenir les mêmes résultats. Voici son exemple de code textuellement (bien que légèrement abrégé):

extern char glob;

void f(char loc) {
    constexpr const char* p0 = &glob; // OK: &glob's is a constant
    constexpr const char* p2 = &loc;  // OK: &loc is constant in its scope
}

Quand je lance ceci, j'obtiens:

error: ‘(const char*)(& loc)’ is not a constant expression

Est-ce qu'il se passe quelque chose avec g ++ que je ne connais pas, ou y a-t-il quelque chose de plus dans l'exemple de Bjarne?

23
johnnyodonnell

Une impression antérieure du livre de Bjarne Stroustrup "The C++ Programming Language (4th Edition)" à la p. 267 contient l'erreur décrite dans la question du PO. L'impression et les copies électroniques actuelles ont été "corrigées" mais ont introduit une autre erreur décrite plus loin. Il fait désormais référence au code suivant:

constexpr const char* p1="asdf";

C'est OK car "asdf" est stocké dans un emplacement mémoire fixe. Dans l'impression précédente, le livre se trompe ici:

void f(char loc) {
    constexpr const char* p0 = &glob; // OK: &glob's is a constant
    constexpr const char* p2 = &loc;  // OK: &loc is constant in its scope
}

Cependant, loc ne se trouve pas dans un emplacement de mémoire fixe. il est sur la pile et aura des emplacements différents selon le moment où il sera appelé.

Cependant, l'impression actuelle de la 4e édition comporte une autre erreur. Voici le code textuellement de 10.5.4:

int main() {
    constexpr const char* p1 = "asdf";
    constexpr const char* p2 = p1;      // OK
    constexpr const char* p3 = p1+2;    // error:  the compiler does not know the value of p1
}

C'est faux. Le compilateur/éditeur de liens connaît la valeur de p1 et peut déterminer la valeur de p1+2 au moment de la liaison. Il compile très bien.

17
doug

Il semble que l'exemple de la section 10.4.5 fourni dans ma copie papier du "Langage de programmation C++ (4e édition)" est incorrect. Et j'ai donc conclu que l'adresse d'une variable locale n'est pas un constexpr.

L'exemple semble avoir été mis à jour dans certaines versions pdf comme on le voit ici:

enter image description here

10
johnnyodonnell

Cette réponse tente de clarifier pourquoi l'adresse d'une variable locale ne peut pas être constexpr en analysant un exemple pour l'architecture x86-64.

Considérez la fonction jouet suivante print_addr(), qui affiche l'adresse de sa variable locale local_var Et s'appelle récursivement n fois:

void print_addr(int n) {
   int local_var{};
   std::cout << n << " " << &local_var << '\n';

   if (!n)
      return; // base case

   print_addr(n-1);  // recursive case
}

Un appel à print_addr(2) a produit la sortie suivante sur mon système x86-64:

2 0x7ffd89e2cd8c
1 0x7ffd89e2cd5c
0 0x7ffd89e2cd2c

Comme vous pouvez le voir, les adresses correspondantes de local_var Sont différentes pour chaque appel à print_addr(). Vous pouvez également voir que plus l'appel de fonction est profond, plus l'adresse de la variable locale local_var Est faible. Cela est dû au fait que la pile se développe vers le bas (c'est-à-dire, des adresses supérieures aux adresses inférieures) sur la plate-forme x86-64.

Pour la sortie ci-dessus, le pile des appels ressemblerait à ceci sur la plate-forme x86-64:

                |     . . .     |
Highest address ----------------- <-- call to print_addr(2) 
                | print_addr(2) |    
                -----------------
                | print_addr(1) |
                -----------------
                | print_addr(0) | <-- base case, end of recursion
Lowest address  ----------------- Top of the stack

Chaque rectangle ci-dessus représente le cadre de pile pour chaque appel à print_addr(). Le local_var De chaque appel est situé dans son cadre de pile correspondant. Étant donné que le local_var De chaque appel à print_addr() se trouve dans son propre cadre de pile (différent), les adresses de local_var Diffèrent.

Pour conclure, comme l'adresse d'une variable locale dans une fonction peut ne pas être la même pour chaque appel à la fonction (c'est-à-dire que la trame de pile de chaque appel peut être située dans une position différente en mémoire), l'adresse d'une telle variable peut ' t être déterminé au moment de la compilation et ne peut donc pas être qualifié de constexpr.

3
眠りネロク

Juste pour ajouter à d'autres réponses qui ont souligné l'erreur, la norme C++ autorise uniquement les pointeurs constexpr vers des objets de durée de stockage statique , une fois la fin de tel ou nullptr. Voir [expr.const/8] spécifiquement # 8.2 ;

Il convient de noter que:

  • les chaînes de caractères ont la durée de stockage statique :
  • En fonction des contraintes de déclaration des variables extern, elles auront intrinsèquement une durée de stockage statique ou durée de stockage local du thread .

C'est donc valable:

#include <string>

extern char glob;
std::string boom = "Haha";

void f(char loc) {
    constexpr const char* p1 = &glob;
    constexpr std::string* p2 = nullptr;
    constexpr std::string* p3 = &boom;
}
1
WhiZTiM