2009-07-25 5 views
3

Dies ist eine Frage aus der neuesten Version von Stroustrups "C++ Programmiersprache".TCPL 5.9.9 (C++): Wo wäre es sinnvoll, einen Namen in seinem eigenen Initialisierer zu verwenden?

Ich habe dies in den letzten paar Tagen in meinem Kopf überlegt.

Das einzige, was ich mit oben kommen kann, (und dies ist wahrscheinlich falsch) ist so etwas wie dieses:

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

Meine Absicht ist es, die Adresse von etwas weiter oben auf dem Stapel zu erhalten. Macht das irgendeinen Sinn? Hat noch jemand andere Antworten? Denken Sie daran, dies ist in Kapitel 5 (Zeiger, Arrays und Strukturen), also sollte die Antwort nicht etwas später im Buch beinhalten.

+2

Was bringt Sie dazu zu denken, dass es überhaupt Sinn machen sollte? : p – jalf

+0

Nun, als tragbarer Code, ist Ihr U. B. Ich denke, es bedeutet, dass es keinen Sinn macht :) –

+0

Um genauer zu sein: "Stack" ist ein Implementierungsdetail; Im C++ - Standard gibt es so etwas nicht. Es hat nur "automatischen Speicher", ohne anzugeben, wie es implementiert ist. Darüber hinaus ist die Zeigerarithmetik, die zu einem Zeiger auf das erste Element eines Arrays führt (einzelne Variable kann zu diesem Zweck als 1-Element-Array behandelt werden), dekrementiert, U. B. –

Antwort

3

Der einzige (gerade noch) angemessen Fall ich kenne, ist, wenn Sie einen Zeiger auf das Objekt selbst an den Konstruktor übergeben werden sollen. Zum Beispiel, sagen Sie einen zyklischen verkettete Liste Knoten haben:

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

und Sie mögen eine Einzelelement-zyklische Liste auf dem Stapel erstellen. Sie können dies tun:

Node n(&n); 

ein paar andere Beispiele, die nicht wirklich praktisch sind (dh ich sehe nicht, warum Sie so etwas brauchen würde), aber ansonsten gilt:

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

"Knoten (Knoten * nächste): nächste (nächste) {}" Zeile ist sehr subtil, ich mag diese Lösung :) –

+0

Obwohl etwas erinnert an die "Spam" -Skizze :-) –

+0

Das erste Beispiel doesn ' t scheint mir richtig. 'next' und' next' in diesem ctor-initialiser sind nicht der gleiche Name; sie beziehen sich jeweils auf unterschiedliche Objekte. –

0

Ich denke, es ist ein richtiger Weg. Nur müssen Sie kümmern sich um viele Dinge nehmen :)

Zuerst brauchen Sie nicht sizeof weil n durch die Größe der ein multipliziert wird. Grundsätzlich müssen Sie den richtigen Typ des Zeigers auswählen, um die gewünschte Adresse auf dem Stapel zu erhalten.

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

Zweitens müssen Sie auf Endianess achten.

Auch bin ich nicht sicher, ob ich etwas vergessen :)

+0

Sie haben eines vergessen: Es ist undefiniertes Verhalten. Sie dürfen Zeigerarithmetik nicht über die Grenzen des ursprünglichen Arrays hinaus ausführen (wo Nicht-Array-Typen als Arrays eines einzelnen Elements betrachtet werden) :) – jalf

+0

Nun, das stimmt, ich stimme Ihnen zu, dass es nur für Lernende nützlich und nutzlos ist danach :) – AraK

0

Zunächst einmal gibt es keine Garantie, in welche Richtung der Stack wächst. Ihr Code geht davon aus, dass er kleiner wird, aber er kann auch größer werden (dh Adressen verringern). Außerdem werden Dinge auf den Stapel gelegt, die Ihnen vielleicht nicht bekannt sind (Rücksprungadresse, Stapelrahmen, Register usw.) und nicht "vorbei" springen, wenn Sie versuchen, Dinge höher auf dem Stapel zu packen.

Was versuchen Sie letztendlich?

+1

In der Tat gibt es keine Garantie, dass es sogar einen Stapel gibt ... –

+1

Ich versuche, einen Grund für die Verwendung eines Namens in einem eigenen Initialisierer zu finden. Das Beispiel, das ich gab, war ein Versuch von solchem. –

4

Ein Beispiel, das ich sehr oft in C-Code verwenden ist:

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

Es geht um Zeiger und Strukturen. Sicher, new befreit Sie davon, dieses Idiom zu verwenden, indem Sie new C stattdessen in C++ sagen.

+0

Macht am meisten Sinn für alles, was bisher hier gepostet wurde! –

+0

Wäre die Größe von (C) nicht klarer? –

+2

Nein, denn wenn Sie den Typ von "c" ändern, müssen Sie ihn an zwei Stellen ändern, und Sie könnten versehentlich den in "sizeof" (und es wird kein Fehler bei der Kompilierung sein) vermissen. Auf diese Weise müssen Sie den Typ nur an einer Stelle ändern. –

0

Betrachten wir eine Reihe von integralen Typs, der seiner Größe in dem ersten Element halten sollte:

// 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; 
} 

Output:

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

P.S. Beispiel in GitHub: Chapter 8: Exercise 9.3

Verwandte Themen