2009-06-02 7 views
5

Ich möchte aus einer Reihe von Gründen in der Lage zu bestimmen, ob ein Zeiger auf dem Stapel ist oder nicht zur Laufzeit. Wie wenn ich es in einen Funktionsaufruf übergebe, kann ich feststellen, ob ich es klonen muss oder nicht. oder ob ich es löschen muss.C++/C Objekt-> isOnStack()

In Microsft C (VC 6,7,8) gibt es eine Möglichkeit, einen Zeiger zu begrenzen, um zu sehen, ob es auf dem Stapel ist oder nicht? Es geht mir nur darum, dies auf dem Thread zu bestimmen, dem der Stapel gehört, auf dem das Objekt platziert wurde.

so etwas wie

static const int __stack_size und __stack_top

???? Danke!

+3

Abgesehen von "in die Montage Sturz- und überprüfen es" keine Antwort in mir klar. Außerdem ist die Frage seltsam genug, um zu vermuten, dass Sie etwas unnötig Ungewöhnliches tun könnten. Vielleicht sogar falsch. Die Chancen, gute Hilfe zu bekommen, erhöhen sich, wenn Sie ein wenig ... – dmckee

+0

Die Gründe, dies zu tun, könnten Sie zu einer echten Lösung für Ihr Problem führen ... StackOverflow hilft nicht wirklich dabei, es sei denn, Sie fragen mehr allgemeine Frage wie "Gibt es einen besseren Weg, dies zu tun". – Kieveli

+0

Sie müssen Smart Pointer und Referenzübergabe betrachten. Es sieht so aus, als ob Sie versuchen, ein Paradigma aus einer anderen Sprache zu holen, die nicht in die C++ Welt passt. Der Benutzer eines Objekts sollte sich nicht darum kümmern, wo es zugeordnet ist. Wenn Sie das Objekt nur per Referenz bearbeiten möchten. Wenn Sie das Eigentumsrecht übernehmen wollen, übergeben Sie einen auto_ptr. Wenn es Ihnen nichts ausmacht zu teilen, verwenden Sie eine shared_ptr –

Antwort

3

Dies hängt von der Aufrufkonvention der Funktion ab. Einige Aufrufkonventionen platzieren Argumente in Registern, andere speichern sie nach dem Kopf des Stapels. Jeder ist eine andere Vereinbarung zwischen dem Anrufer/Angerufenen. An jeder Funktionsgrenze im Stapel könnte also eine andere Konvention verwendet worden sein. Dies zwingt Sie, die auf allen Ebenen verwendete Aufrufkonvention zu verfolgen.

Zum Beispiel können in fastcall ein oder mehrere Argumente über Register übergeben werden. See MSDN for more. Dies würde jedes Schema durcheinander bringen, um herauszufinden, ob eine Adresse in einem bestimmten Bereich existiert. In MS's diesem Aufruf wird der This-Zeiger über Register weitergegeben. Die & würde nicht zu irgendwo zwischen einem Bereich von Werten zwischen dem Anfang und dem Ende des Stapels aufgelöst werden.

Unterm Strich, Forschungs-Aufrufkonventionen, gibt es an, wie Stapelspeicher ausgelegt wird. Hier ist ein good tutorial

Hinweis dies ist sehr Plattform-spezifisch!

+0

Ich glaube, er möchte wissen, ob das Objekt, auf das ein Zeiger zeigt, aktiviert ist der Stapel, nicht der eigentliche Zeiger selbst. In diesem Fall glaube ich nicht, dass Aufrufkonventionen von Bedeutung wären, da Objekte selten (wenn überhaupt) in Registern übergeben werden - nur der Zeiger wäre. – DougN

+0

Ich bin nicht davon überzeugt, dass das Layout des Speichers im Stack die Methode beeinflusst, um zu bestimmen, ob sich eine Variable auf dem Stack oder dem Heap befindet. Es ist eine C++ - Frage, also erwarten Sie überall cdecl-Aufrufe. – Kieveli

+0

cdecl wird nicht für Methoden verwendet, bei denen ein this-Zeiger beteiligt ist. –

7

Interessante Frage.

Hier ist eine Idee, wie Sie es zu bestimmen, aber kein Funktionsaufruf. Erstellen Sie eine Dummy-Variable zu Beginn Ihrer Anwendung auf dem Stapel. Erstellen Sie eine Variable auf dem Stapel in einer Funktion isOnStack (void * ptr) Überprüfen Sie, ob das 'ptr' zwischen der Dummy-Variable und der lokalen Variablen ist.

Denken Sie daran, dass der Stapel zusammenhängend für einen bestimmten Thread ist. Ich bin nicht sicher, was passieren würde, wenn Sie für diese Informationen von einem Thread zu einem anderen suchen.

Wenn es nicht im Stapel ist, dann muss es auf dem Haufen sein.

+0

+1 das ist eine ziemlich gute Lösung, aber natürlich ist es nicht tragbar. –

+1

und funktioniert nicht für Aufrufkonventionen, die Argumente in Registern laden. –

+0

@Iraimbilanja: Können Sie näher erläutern, warum dies nicht tragbar ist? Die Funktionalität eines Stacks hängt davon ab, dass die Adressen sequenziell sind, daher würde ich erwarten, dass sie auf allen Systemen funktioniert. –

5

Ich kenne keine Methode, um festzustellen, wo ein Objekt zugewiesen wurde.

Ich sehe diese Art von Verhalten sollte vermieden werden. Solche Dinge sollten im Vertrag zwischen Benutzer und Bibliotheksentwickler gelöst werden. Nennen Sie diese Dinge in der Dokumentation! Wenn Sie sich nicht sicher sind, kopieren Sie das Objekt (das erfordert einen Kopierkonstruktor und verhindert, dass Sie versuchen, nicht kopierbare Objekte zu kopieren).

Sie können auch smart pointers von Boost verwenden. Wenn Sie nicht sicher sind, wann ein Objekt länger benötigt wird, übergeben Sie es als gemeinsamen Zeiger.

+0

Gute Antwort auf die Frage nicht gefragt! – Kieveli

+2

+1 für den Wald zu sehen, nicht nur die Bäume. :-D –

+0

Es ist der typische Unterschied zwischen dem, was die Leute wollen und was sie wirklich brauchen. – ebo

9

Wenn Sie wissen, ob sich ein Objekt auf dem Stack oder Heap befindet, wird Ihnen nicht mitgeteilt, ob es von der aufgerufenen Funktion geklont oder gelöscht werden soll.Schließlich können Sie einen der beiden Dateitypen klonen, und während Sie nicht versuchen sollten, eine dem Stapel zugeordnete Funktion zu löschen, sollten Sie nicht versuchen, alle Heap-Zeiger zu löschen.

Wenn Sie eine Funktion haben, die eine obskure Überprüfung durchführt, um zu sehen, ob ein übergebener Zeiger gelöscht werden soll oder nicht, führt dies zu Verwirrung auf der ganzen Linie. Sie möchten nicht, dass Sie je nach Kontext auf Felder in einem übergebenen Objekt verweisen können oder nicht. Sie möchten auch keinen Fehler riskieren, der dazu führt, dass Sie versuchen, ein Stapelobjekt freizugeben.

Es gibt keine Standardmethode, um zu bestimmen, auf was ein Zeiger zeigt, und jeder nicht standardmäßige Weg wird wahrscheinlich unterbrochen. Insbesondere in Multithread-Anwendungen kann man nicht auf Stack-Kontiguität zählen (und jemand könnte einfach einen Thread zu einer Anwendung hinzufügen, ohne die Konsequenzen zu erkennen).

Die einzige sichere Möglichkeit besteht darin, über eine Aufrufkonvention zu verfügen, dass die aufgerufene Funktion ein übergebenes Objekt löscht oder nicht, oder um eine Art intelligenten Zeiger zu übergeben. Alles andere verlangt Ärger.

+1

+1 für "Sie sollten auch nicht versuchen, alle Heap-Zeiger zu löschen." Das Eigentum sollte genauer kontrolliert werden. –

0

Dies ist sehr plattformspezifisch und IMO nur für Debug-Build-Diagnose geeignet. Was Sie tun müssten (auf WIntel) ist:

Wenn ein Thread erstellt wird, erstellen Sie eine Stapelvariable, und speichern Sie ihre Adresse in einer globalen (Threadid, Stapel Basisadresse) Zuordnung.

IsOnStack muss eine eigene lokale Variable erstellen und prüfen, ob der übergebene Zeiger zwischen der Stack-Basis und der Adresse im aktuellen Stack-Frame liegt.

Dies wird Ihnen nichts über Variablen in anderen Threads sagen. Stapeladressen verringern sich, daher ist die Basisadresse höher als die aktuelle Adresse.

Als portable Lösung würde ich einen boost :: shared_ptr übergeben, der mit einem Deleter verknüpft werden kann. (In Boost ist dies kein Template-Parameter, also "infiziert" er nicht die Funktion, die den Pointer verbraucht).

Sie können einen „unmanaged“ Zeiger wie folgt erstellen:

inline void boost_null_deleter(void *) {} 

template <typename T> inline 
boost::shared_ptr<T> unmanaged_ptr(T * x) 
{ 
    return boost::shared_ptr<T>(x, ::boost_null_deleter); 
} 

und rufen Sie Ihre Funktion wie diese

Foo local = { ... }; 
FooPtr heapy(new Foo); 

FunnyFunc(unmanaged_ptr(&local)); 
FunnyFunc(heapy); 
0

ich eine solche Funktion in C++ für eine Weile jetzt gewünscht haben, aber nichts Gutes gibt es wirklich. Das Beste, auf das Sie hoffen können, ist zu dokumentieren, dass Sie ein Objekt auf dem Heap übergeben müssen, und dann ein Idiom im Code einzurichten, damit jeder, der an der Codebasis arbeitet, Heap-allokierte Objekte an Ihren Code übergeben kann . Etwas wie auto_ptr oder boost :: shared_ptr zu verwenden ist eine gute Idee für diese Art von Anforderung.

0

Nun, ich stimme zu, es gibt wahrscheinlich eine bessere Art zu tun, was Sie versuchen zu tun. Aber es ist sowieso eine interessante Frage. Also um der Diskussion willen ...

Erstens gibt es keine Möglichkeit, dies zu tun, ist portable C oder C++. Sie müssen zur Baugruppe mit mindestens einem asm{ } Block fallen.

Zweitens würde ich das nicht im Produktionscode verwenden. Aber für VC++/x86 können Sie herausfinden, ob eine Variable auf Ihr Stapel ist, indem Sie überprüfen, dass seine Adresse zwischen den Werten ESP und EBP Register ist.

Ihr ESP (Extended Stack Pointer, der niedrige Wert) hält die Spitze Ihres Stapels und der EBP (Extended Base Pointer) den unteren Teil. Hier ist die Structure of the Call Stack auf x86.

Aufruf Konvention wird vor allem Funktionsparameter beeinflussen, und wie die Rücksendeadresse behandelt wird, usw. Es bezieht sich also nicht auf Ihren Stapel viel. Für Ihren Fall sowieso nicht.

Was Dinge ablenkt, sind Compiler-Optimierungen. Ihr Compiler kann den Rahmenzeiger (EBP) auslassen. Dies ist die -Oy flag in VC++. Anstatt den EBP als Basiszeiger zu verwenden, können Sie die Adresse von Funktionsparametern verwenden, falls Sie welche haben. Da sind die etwas höher auf dem Stack.

Aber was ist, wenn die Variable, die Sie testen, auf dem Stack Ihres Aufrufers ist? Oder ein Caller-Stack mehrere Generationen über dir? Nun, Sie können den gesamten Call-Stack laufen, aber Sie können sehen, wie dies sehr hässlich werden kann (als ob es nicht schon ist :-))

Da Sie leben gefährlich, ist eine andere Compiler-Flag, die Sie interessieren könnte - Gh flag. Mit diesem Flag und einer geeigneten _penter Hook-Funktion können Sie diese Berechnungen für die Funktionen oder Dateien oder Module usw. einfach einrichten. Aber bitte tu das nicht, es sei denn, du möchtest nur sehen, wie die Dinge funktionieren.

Herauszufinden, was auf dem Heap ist noch schlimmer ....

0

Auf einigen Plattformen kann der Stapel durch das Laufzeitsystem aufgeteilt werden. Das heißt, anstatt einen Stack-Overflow (ohne Wortspiel) zu bekommen, greift das System automatisch etwas mehr Stack-Platz. Natürlich ist der neue Stapelspeicherplatz normalerweise nicht mit dem alten Stapelspeicherbereich zusammenhängend.

Es ist daher wirklich nicht sicher, davon abhängig zu sein, ob etwas auf dem Stapel ist.

Die Verwendung von auto_ptr im Allgemeinen beseitigt die Notwendigkeit für diese Art von Sache, und ist außerdem viel cooler.

0

Die MSVC Windows-Compiler-spezifische Antwort. Dies ist natürlich spezifisch für den Thread, in dem sich das Objekt befindet.Es ist eine ziemlich schlechte Idee, andere jedes Auto-Stapel Element in jeden Thread zu passieren als die whos stapeln es auf so ist ich darüber nicht besorgt bin :)

Bool __isOnStack (const void * ptr)
{

  • // FS: [0x04] 4 Win9x und NT oben Stapel
    // FS: [0x08] 4 Win9x und NT Aktuelle unten im Stapel
    const char * sTop;
    const char * sBot;
    __asm ​​{
    mov EAX, FS: [04h]
    mov [STOPP], EAX
    mov EAX, FS: [08h]
    mov [SBOT], EAX

    }
    return (sTop> ((const char *) ptr) & & ((const char *) ptr)> sBot);

}