2009-07-25 6 views
3

Ceci est une question de la version la plus récente de "The C++ Programming Language" de Stroustrup. J'ai réfléchi à cela dans ma tête ces deux derniers jours.TCPL 5.9.9 (C++): Où serait-il logique d'utiliser un nom dans son propre initialiseur?

La seule chose que je peux venir avec, (ce qui est probablement incorrect) est quelque chose comme ceci:

int* f(int n) { 
    int* a = &a - n * sizeof(int*); 
    return a; 
} 

Mon intention est d'obtenir l'adresse de quelque chose plus haut sur la pile. Cela a-t-il un sens? Est-ce que quelqu'un d'autre a d'autres réponses? Rappelez-vous, c'est dans le chapitre 5 (pointeurs, tableaux et structures) donc la réponse ne devrait pas impliquer quelque chose plus tard dans le livre.

+2

Qu'est-ce qui vous fait penser que cela devrait * toujours * avoir du sens? : p – jalf

+0

Eh bien, comme un code portable, le vôtre est U.B. Je suppose que cela signifie que cela n'a pas de sens :) –

+0

Pour être plus précis: "pile" est un détail d'implémentation; il n'y a pas une telle chose en standard C++. Il a seulement "stockage automatique", sans préciser comment il est mis en œuvre. De plus, l'arithmétique du pointeur qui résulte en un pointeur vers le premier élément d'un tableau (une seule variable peut être traitée comme un tableau à un élément à cette fin) décrémenté est U.B. –

Répondre

3

Le seul (à peine) cas raisonnable que je connaisse est quand vous voulez passer un pointeur sur l'objet lui-même à son constructeur. Par exemple, supposons que vous avez un nœud de liste chaînée cyclique:

class Node 
{ 
public: 
    Node(Node* next): next(next) {} 
private: 
    Node* next; 
}; 

et que vous voulez créer une liste cyclique unique élément sur la pile. Vous pouvez le faire:

Node n(&n); 

Quelques autres exemples qui ne sont pas vraiment pratique (c.-à-je ne vois pas pourquoi vous auriez besoin de ce genre de chose), mais par ailleurs valide:

int n = sizeof(n); 
void* p = &p; 
+0

"Node (Node * suivant): next (next) {}" ligne est très subtile, j'aime cette solution :) –

+0

Bien que légèrement rappelant le "spam" esquisse :-) –

+0

Le premier exemple doesn ' Je parais juste pour moi. 'next' et' next' dans ce ctor-initialiser ne sont pas le même nom; ils se réfèrent chacun à des objets distincts. –

0

Je pense que c'est une façon correcte. Juste vous devez prendre soin de beaucoup de choses :)

D'abord, vous n'avez pas besoin sizeof car n sera multiplié par la taille de un. Fondamentalement, vous devez choisir le bon type de pointeur pour obtenir l'adresse que vous voulez sur la pile.

int* a = &a - n; // so if n==1 => a = &a - (1*4) 
char* b = &b - n; // so if n==1 => b = &b - (1*1) 

Deuxièmement, vous devez prendre soin de l'endianess.

Aussi, je ne sais pas si j'oublié quelque chose :)

+0

vous avez oublié une chose: c'est un comportement indéfini. Vous n'êtes pas autorisé à faire de l'arithmétique de pointeur au-delà des limites du tableau original (où les types non-tableaux sont considérés comme des tableaux d'un seul élément) :) – jalf

+0

bien que ce soit vrai, je suis d'accord avec vous après cela :) – AraK

0

Tout d'abord, il n'y a aucune garantie quant à la direction dans laquelle la pile se développe. Votre code suppose qu'il se développe, mais il pourrait aussi grandir (c'est-à-dire, abaisser les adresses). De plus, il y a des choses que vous ne connaissez pas (adresse de retour, cadre de pile, registres, etc.) et qui ne sont pas "sautantes" lorsque vous essayez d'attraper des objets plus haut sur la pile.

Qu'essayez-vous finalement de faire?

+1

En fait, il n'y a aucune garantie qu'il y ait même une pile ... –

+1

J'essaie de trouver une raison pour utiliser un nom dans son propre initialiseur. L'exemple que j'ai donné était une tentative de telle sorte. –

4

Un exemple que j'utilise très souvent dans le code C est:

C *c = (C*) malloc(sizeof *c); 
... 
free(c); 

Il implique des pointeurs et des structures. Sûrement, new vous libère de devoir utiliser cet idiome en disant new C à la place en C++.

+0

Donne plus de sens à tout ce qui est posté ici jusqu'à maintenant! –

+0

Est-ce que sizeof (C) ne serait pas plus clair? –

+2

Non, car si vous changez le type de 'c', vous devrez le changer à deux endroits, et manquer probablement celui de' sizeof' (et ce ne sera pas une erreur de compilation). De cette façon, vous n'avez qu'à changer de type en un seul endroit. –

0

Considérons un réseau de type intégral qui devrait maintenir sa taille dans le premier élément:

// clang++ --std=c++11 initializer.cpp -o initializer 

#include <iostream> 

using namespace std; 

void Print(int* array) { 
    cout << "array has " << *array << " elements" << endl; 
    for(int count {*array}; count; --count) { 
     cout << *++array << endl; 
    } 
} 

int main(int, char* []) { 
    { 
     int elements[] {sizeof(*elements), 1, 2, 3}; 
     *elements = sizeof(elements)/*elements - 1; 
     Print(elements); 
    } 
    cout << "---" << endl; 
    { 
     int elements[] {sizeof(*elements)}; 
     *elements = sizeof(elements)/*elements - 1; 
     Print(elements); 
    } 
    return 0; 
} 

sortie:

./initializer                                                  
array has 3 elements 
1 
2 
3 
--- 
array has 0 elements 

P.S. Exemple dans GitHub: Chapter 8: Exercise 9.3