it-swarm.com.de

Testzeiger auf Gültigkeit (C/C++)

Gibt es eine Möglichkeit, (programmgesteuert natürlich) festzustellen, ob ein bestimmter Zeiger "gültig" ist? Das Prüfen auf NULL ist einfach, aber wie sieht es mit 0x00001234 aus? Beim Versuch, diese Art von Zeiger zu dereferenzieren, tritt eine Ausnahme/ein Absturz auf.

Eine plattformübergreifende Methode wird bevorzugt, plattformspezifisch (für Windows und Linux) ist jedoch auch in Ordnung.

Update zur Verdeutlichung: Das Problem besteht nicht bei veralteten/freigegebenen/nicht initialisierten Zeigern. Stattdessen implementiere ich eine API, die Zeiger vom Aufrufer übernimmt (z. B. einen Zeiger auf eine Zeichenfolge, ein Dateihandle usw.). Der Aufrufer kann (mit Absicht oder aus Versehen) einen ungültigen Wert als Zeiger senden. Wie verhindere ich einen Absturz?

78
noamtm

Update zur Klarstellung: Das Problem besteht nicht bei veralteten, freigegebenen oder nicht initialisierten Zeigern. Stattdessen implementiere ich eine API, die Zeiger vom Aufrufer übernimmt (z. B. einen Zeiger auf eine Zeichenfolge, ein Dateihandle usw.). Der Aufrufer kann (mit Absicht oder aus Versehen) einen ungültigen Wert als Zeiger senden. Wie verhindere ich einen Absturz?

Sie können diesen Check nicht machen. Es gibt einfach keine Möglichkeit, zu überprüfen, ob ein Zeiger "gültig" ist. Sie müssen darauf vertrauen, dass Personen, die eine Funktion verwenden, die einen Zeiger verwendet, wissen, was sie tun. Wenn sie 0x4211 als Zeigerwert übergeben, müssen Sie darauf vertrauen, dass sie auf die Adresse 0x4211 zeigt. Und wenn sie "versehentlich" ein Objekt treffen, würden Sie selbst dann, wenn Sie eine unheimliche Betriebssystemfunktion (IsValidPtr oder was auch immer) verwenden würden, in einen Fehler geraten und nicht schnell versagen.

Verwenden Sie Nullzeiger, um diese Art von Dingen zu signalisieren, und teilen Sie dem Benutzer Ihrer Bibliothek mit, dass sie keine Zeiger verwenden sollten, wenn sie dazu neigen, ungültige Zeiger aus Versehen zu übergeben.

Das Vermeiden eines Absturzes, der durch den Aufruf des ungültigen Zeigers durch den Anrufer verursacht wird, ist ein guter Weg, um lautlose Fehler zu finden, die schwer zu finden sind. 

Ist es nicht besser für den Programmierer, der Ihre API verwendet, um eine klare Nachricht zu erhalten, dass sein Code falsch ist, indem er abgestürzt wird, anstatt ihn zu verstecken? 

30
Nailer

Unter Win32/64 gibt es eine Möglichkeit, dies zu tun. Versuchen Sie, den Zeiger zu lesen und die resultierende SEH-Exception zu erfassen, die bei einem Fehler ausgegeben wird. Wenn es nicht wirft, ist es ein gültiger Zeiger.

Das Problem bei dieser Methode ist jedoch, dass sie nur zurückgibt, ob Sie Daten vom Zeiger lesen können. Es gibt keine Garantie für die Typsicherheit oder eine Anzahl anderer Invarianten. Im Allgemeinen eignet sich diese Methode nur für etwas anderes als zu sagen "Ja, ich kann diesen bestimmten Speicherplatz zu einer Zeit lesen, die jetzt vergangen ist". 

Kurz gesagt, mach das nicht;)

Raymond Chen hat einen Blogeintrag zu diesem Thema: http://blogs.msdn.com/oldnewthing/archive/2007/06/25/3507294.aspx

26
JaredPar

Es gibt drei einfache Möglichkeiten für ein C-Programm unter Linux, sich introspektiv über den Status des Speichers zu informieren, in dem es ausgeführt wird, und warum die Frage in einigen Kontexten entsprechend ausgereifte Antworten enthält.

  1. Nachdem Sie getpagesize () aufgerufen und den Zeiger auf eine Begrenzung der Seite gerundet haben, können Sie mincore () aufrufen, um herauszufinden, ob eine Seite gültig ist, und , Wenn sie zufällig Teil des Arbeitssatzes des Prozesses ist. Beachten Sie, dass dies einige Kernel-Ressourcen erfordert. Sie sollten also einen Benchmark erstellen und feststellen, ob der Aufruf dieser Funktion in Ihrer API wirklich angemessen ist. Wenn Ihre api Interrupts verarbeitet oder von den seriellen Ports In den Speicher liest, sollten Sie dies aufrufen, um unvorhersehbare Verhalten zu vermeiden.
  2. Nachdem Sie stat () aufgerufen haben, um zu ermitteln, ob ein Verzeichnis/proc/self verfügbar ist, können Sie /proc/self/maps öffnen und lesen, um Informationen zu der Region zu finden, in der sich ein Zeiger befindet Manpage für proc, die Prozessinformations-Pseudo-Datei System. Offensichtlich ist dies relativ teuer, aber es ist möglich, dass Sie das Ergebnis der Analyse in einem Array zwischenspeichern können. Sie können mit einer binären Suche effizient nachschlagen. Beachten Sie auch die/proc/self/smaps. Wenn Ihr API für Hochleistungs-Computing ausgelegt ist, möchte Das Programm über die Datei/proc/self/numa erfahren, die unter der Manpage für numa, der nicht einheitlichen Speicher , Dokumentiert ist.
  3. Der get_mempolicy (MPOL_F_ADDR) -Aufruf ist für Hochleistungs-Computing-Api-Arbeiten geeignet, bei denen mehrere Threads von Ausgeführt werden und Sie Ihre Arbeit verwalten, um eine Affinität für nicht einheitlichen Speicher zu haben Ressourcen. Eine solche api Sagt natürlich auch aus, ob ein Zeiger gültig ist.

Unter Microsoft Windows gibt es die Funktion QueryWorkingSetEx, die unter der Prozessstatus-API (auch in der NUMA-API) dokumentiert ist ..__ Als Folge der anspruchsvollen NUMA-API-Programmierung können Sie mit dieser Funktion auch einfache "Testzeiger für Gültigkeit" (C/C++) "arbeiten, so ist es unwahrscheinlich, dass es mindestens 15 Jahre lang veraltet ist.

24
George Carrette

AFAIK geht es nicht. Sie sollten versuchen, diese Situation zu vermeiden, indem Sie nach dem Freigeben des Speichers die Zeiger immer auf NULL setzen.

15
Ferdinand Beyer

Werfen Sie einen Blick auf diese und diese Frage. Schauen Sie sich auch intelligente Zeiger an.

7
tunnuz

In Bezug auf die Antwort oben in diesem Thread:

IsBadReadPtr (), IsBadWritePtr (), IsBadCodePtr (), IsBadStringPtr () für Windows.

Mein Rat ist, sich von ihnen fernzuhalten, jemand hat diesen bereits gepostet: http://blogs.msdn.com/oldnewthing/archive/2007/06/25/3507294.aspx

Ein anderer Beitrag zum gleichen Thema und vom selben Autor (denke ich) ist dieser: http://blogs.msdn.com/oldnewthing/archive/2006/09/27/773741.aspx ( "IsBadXxxPtr sollte eigentlich CrashProgramRandomly heißen").

Wenn die Benutzer Ihrer API fehlerhafte Daten senden, lassen Sie sie abstürzen. Wenn das Problem darin besteht, dass die übergebenen Daten erst später verwendet werden (was das Auffinden der Ursache erschwert), fügen Sie einen Debug-Modus hinzu, in dem die Zeichenfolgen usw. beim Eintrag protokolliert werden. Wenn sie schlecht sind, wird es offensichtlich sein (und wahrscheinlich abstürzen). Wenn dies häufig geschieht, lohnt es sich möglicherweise, Ihre API aus dem Prozess zu verschieben und den API-Prozess anstelle des Hauptprozesses zum Absturz bringen zu lassen.

7
Fredrik

Erstens sehe ich keinen Sinn darin, mich vor dem Anrufer zu schützen, der absichtlich versucht, einen Absturz zu verursachen. Sie könnten dies leicht tun, indem sie versuchen, über einen ungültigen Zeiger selbst zuzugreifen. Es gibt viele andere Möglichkeiten - sie könnten lediglich den Speicher oder den Stapel überschreiben. Wenn Sie sich vor solchen Dingen schützen müssen, müssen Sie einen separaten Prozess mit Sockets oder einem anderen IPC für die Kommunikation ausführen.

Wir schreiben eine Menge Software, mit der Partner/Kunden/Benutzer ihre Funktionalität erweitern können. Unweigerlich wird jeder Fehler zuerst an uns gemeldet. Es ist daher hilfreich, einfach anzeigen zu können, dass das Problem im Plug-In-Code liegt. Darüber hinaus gibt es Sicherheitsbedenken und einige Benutzer sind vertrauenswürdiger als andere.

Wir verwenden verschiedene Methoden, abhängig von den Leistungs-/Durchsatzanforderungen und der Vertrauenswürdigkeit. Am meisten bevorzugt:

  • getrennte Prozesse mithilfe von Sockets (häufig Weitergabe von Daten als Text).

  • getrennte Prozesse, die gemeinsam genutzten Speicher verwenden (wenn große Datenmengen übertragen werden sollen).

  • dieselbe Prozedur separate Threads über die Nachrichtenwarteschlange (bei häufigen Kurznachrichten).

  • gleicher Prozess - separate Threads - alle übergebenen Daten werden aus einem Speicherpool zugewiesen.

  • gleicher Prozess über direkten Prozeduraufruf - alle übergebenen Daten werden aus einem Speicherpool zugewiesen. 

Wir versuchen niemals, auf das zurückzugreifen, was Sie im Umgang mit Software von Drittanbietern versuchen - insbesondere, wenn wir die Plug-Ins/Bibliothek als Binärcode und nicht als Quellcode erhalten.

Die Verwendung eines Speicherpools ist in den meisten Fällen recht einfach und muss nicht ineffizient sein. Wenn Sie die Daten an erster Stelle zuordnen, ist es einfach, die Zeiger mit den von Ihnen zugewiesenen Werten zu vergleichen. Sie können auch die zugewiesene Länge speichern und vor und nach den Daten "magische" Werte hinzufügen, um den gültigen Datentyp und Datenüberschreitungen zu überprüfen.

6
Dipstick

Ich habe viel Verständnis für Ihre Frage, da ich selbst in einer fast identischen Position bin. Ich schätze, was viele Antworten sagen, und sie sind korrekt - die Routine, die den Zeiger sollte liefert, liefert einen gültigen Zeiger. In meinem Fall ist es fast unvorstellbar, dass sie den Zeiger hätten beschädigen können - aber wenn sie hatte geschafft haben, würde MEINE Software abstürzen, und ME würde die Schuld bekommen :-(

Meine Forderung ist nicht, dass ich nach einem Segmentierungsfehler weitermache - das wäre gefährlich - ich möchte dem Kunden nur mitteilen, was passiert ist, bevor er beendet wird, damit er seinen Code korrigieren kann, anstatt mich zu beschuldigen!

So habe ich es (unter Windows) gefunden: http://www.cplusplus.com/reference/clibrary/csignal/signal/

Um eine Zusammenfassung zu geben:

#include <signal.h>

using namespace std;

void terminate(int param)
/// Function executed if a segmentation fault is encountered during the cast to an instance.
{
  cerr << "\nThe function received a corrupted reference - please check the user-supplied  dll.\n";
  cerr << "Terminating program...\n";
  exit(1);
}

...
void MyFunction()
{
    void (*previous_sigsegv_function)(int);
    previous_sigsegv_function = signal(SIGSEGV, terminate);

    <-- insert risky stuff here -->

    signal(SIGSEGV, previous_sigsegv_function);
}

Nun, dieses erscheint verhält sich so, wie ich es mir wünschen würde (es gibt die Fehlermeldung aus und beendet dann das Programm) - aber wenn jemand einen Fehler entdecken kann, lass es mich wissen!

4
Mike Sadler

Es ist keine sehr gute Richtlinie, beliebige Zeiger als Eingabeparameter in einer öffentlichen API zu akzeptieren. Es ist besser, "einfache Datentypen" wie eine ganze Zahl, eine Zeichenfolge oder eine Struktur zu haben (ich meine natürlich eine klassische Struktur mit einfachen Daten; offiziell kann alles eine Struktur sein).

Warum? Nun, weil, wie andere sagen, es keine Standardmethode gibt, um zu wissen, ob Sie einen gültigen Zeiger erhalten haben oder einen, der auf Junk zeigt.

Aber manchmal haben Sie keine Wahl - Ihre API muss einen Zeiger akzeptieren.

In diesen Fällen ist es die Aufgabe des Anrufers, einen guten Zeiger zu übergeben. NULL kann als Wert akzeptiert werden, nicht jedoch als Hinweis auf Junk.

Können Sie das irgendwie überprüfen? Nun, ich habe in einem solchen Fall eine Invariante für den Typ definiert, auf den der Zeiger zeigt, und ihn aufrufen, wenn Sie ihn erhalten (im Debug-Modus). Zumindest wenn die Invariante fehlschlägt (oder abstürzt), wissen Sie, dass Ihnen ein falscher Wert übergeben wurde.

// API that does not allow NULL
void PublicApiFunction1(Person* in_person)
{
  assert(in_person != NULL);
  assert(in_person->Invariant());

  // Actual code...
}

// API that allows NULL
void PublicApiFunction2(Person* in_person)
{
  assert(in_person == NULL || in_person->Invariant());

  // Actual code (must keep in mind that in_person may be NULL)
}
2
Daniel Daranas

Unter Unix sollten Sie in der Lage sein, einen Kernel-Syscall zu verwenden, der die Zeigerprüfung durchführt und EFAULT zurückgibt, z. B .:

#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <stdbool.h>

bool isPointerBad( void * p )
{
   int fh = open( p, 0, 0 );
   int e = errno;

   if ( -1 == fh && e == EFAULT )
   {
      printf( "bad pointer: %p\n", p );
      return true;
   }
   else if ( fh != -1 )
   {
      close( fh );
   }

   printf( "good pointer: %p\n", p );
   return false;
}

int main()
{
   int good = 4;
   isPointerBad( (void *)3 );
   isPointerBad( &good );
   isPointerBad( "/tmp/blah" );

   return 0;
}

rückkehr:

bad pointer: 0x3
good pointer: 0x7fff375fd49c
good pointer: 0x400793

Es ist wahrscheinlich ein besserer Syscall zu verwenden als open () [möglicherweise Zugriff], da die Möglichkeit besteht, dass dies zu einem tatsächlichen Dateierstellungs-Codepfad und zu einer nachfolgenden Schließanforderung führt.

2
Peeter Joot

Es gibt keinen tragbaren Weg, dies zu tun, und für bestimmte Plattformen zu tun, kann zwischen hart und unmöglich liegen. In jedem Fall sollten Sie niemals Code schreiben, der von einer solchen Prüfung abhängt. Lassen Sie nicht zu, dass die Zeiger ungültige Werte annehmen.

2
anon

In C++ gibt es keine Bestimmungen, um die Gültigkeit eines Zeigers im Allgemeinen zu testen. Man kann natürlich davon ausgehen, dass NULL (0x00000000) schlecht ist und verschiedene Compiler und Bibliotheken hier und dort "spezielle Werte" verwenden, um das Debuggen zu vereinfachen (wenn ich zum Beispiel sehe, dass ein Zeiger im Visual Studio als 0xCECECECE angezeigt wird, kenne ich das.) Ich habe etwas falsch gemacht), aber die Wahrheit ist, da ein Zeiger nur ein Index in den Speicher ist, ist es fast unmöglich zu erkennen, wenn man nur den Zeiger betrachtet, ob er der "richtige" Index ist. 

Es gibt verschiedene Tricks, die Sie mit dynamic_cast und RTTI ausführen können, um sicherzustellen, dass das Objekt, auf das verwiesen wird, von dem Typ ist, den Sie möchten, aber für alle ist es erforderlich, dass Sie auf ein gültiges Element zeigen. 

Wenn Sie sicherstellen möchten, dass Ihr Programm "ungültige" Zeiger erkennen kann, dann empfehle ich Folgendes: Setzen Sie jeden Zeiger, den Sie deklarieren, gleich nach der Erstellung entweder auf NULL oder auf eine gültige Adresse, und setzen Sie ihn gleich auf NULL, nachdem Sie den Speicher freigegeben haben, auf den er verweist. Wenn Sie mit dieser Vorgehensweise vorsichtig sind, müssen Sie nur nach NULL suchen. 

2
Toji

Es ist eine gute Technik, den Zeiger vor und nach der Verwendung auf NULL zu setzen. Dies ist in C++ einfach durchzuführen, wenn Sie beispielsweise Zeiger innerhalb einer Klasse (eine Zeichenfolge) verwalten:

class SomeClass
{
public:
    SomeClass();
    ~SomeClass();

    void SetText( const char *text);
    char *GetText() const { return MyText; }
    void Clear();

private:
    char * MyText;
};


SomeClass::SomeClass()
{
    MyText = NULL;
}


SomeClass::~SomeClass()
{
    Clear();
}

void SomeClass::Clear()
{
    if (MyText)
        free( MyText);

    MyText = NULL;
}



void SomeClass::Settext( const char *text)
{
    Clear();

    MyText = malloc( strlen(text));

    if (MyText)
        strcpy( MyText, text);
}
2
Tim Ring

Dieser Artikel MEM10-C. Definieren und Verwenden einer Zeigerüberprüfungsfunktion sagt, es ist möglich, eine gewisse Prüfung durchzuführen, insbesondere unter Linux. 

1
Marek Szanyi

Wie andere bereits gesagt haben, können Sie einen ungültigen Zeiger nicht zuverlässig erkennen. Betrachten Sie einige der Formulare, die ein ungültiger Zeiger annehmen kann:

Sie könnten einen Nullzeiger haben. Das könnte man leicht überprüfen und etwas dagegen unternehmen.

Sie könnten einen Zeiger haben, der sich außerhalb des gültigen Speichers befindet. Was als gültiger Speicher gilt, hängt davon ab, wie die Laufzeitumgebung Ihres Systems den Adressraum einrichtet. Auf Unix-Systemen ist dies normalerweise ein virtueller Adressraum, der bei 0 beginnt und zu einer großen Anzahl von Megabytes führt. Bei eingebetteten Systemen kann es recht klein sein. Es kann auf keinen Fall bei 0 beginnen. Wenn Ihre App im Supervisor-Modus oder einem gleichwertigen Gerät ausgeführt wird, kann Ihr Zeiger auf eine reale Adresse verweisen, die möglicherweise mit realem Speicher gesichert wird.

Sie könnten einen Zeiger auf irgendwo in Ihrem gültigen Speicher haben, sogar in Ihrem Datensegment, bss, stack oder heap, aber nicht auf ein gültiges Objekt zeigen. Eine Variante davon ist ein Zeiger, der verwendet wurde, um auf ein gültiges Objekt zu zeigen, bevor etwas mit dem Objekt passiert ist. Schlechte Dinge in diesem Zusammenhang sind die Freigabe von Speicherplätzen, Speicherbeschädigung oder Zeigerbeschädigung.

Möglicherweise haben Sie einen ungültigen ungültigen Zeiger, z. B. einen Zeiger mit illegaler Ausrichtung für die Sache, auf die verwiesen wird.

Das Problem wird noch schlimmer, wenn Sie segment-/versatzbasierte Architekturen und andere ungerade Zeigerimplementierungen berücksichtigen. Diese Art von Dingen wird dem Entwickler normalerweise durch gute Compiler und den umsichtigen Gebrauch von Typen verborgen. Wenn Sie jedoch den Schleier durchstechen und versuchen wollen, die Betriebssystem- und Compiler-Entwickler zu überlisten, können Sie das tun, aber es gibt keinen generischen Weg Um dies zu tun, werden alle Probleme behandelt, auf die Sie stoßen könnten.

Das Beste, was Sie tun können, ist den Absturz zuzulassen und einige gute Diagnoseinformationen herauszugeben.

1
John Grieggs

In bestimmten Situationen kann etwas getan werden: Wenn Sie beispielsweise prüfen möchten, ob ein String-Zeigerzeichenfolge gültig ist, können Sie mit write (fd, buf, szie) syscall die Zauberei ausführen: Fd sei ein Dateideskriptor von temporär Datei, die Sie für den Test erstellen, und buf auf die Zeichenfolge zeigt, die Sie testen, wenn der Zeiger ungültig ist, würde write () -1 zurückgeben und errno auf EFAULT gesetzt, wodurch angezeigt wird, dass buf außerhalb des zugänglichen Adressraums liegt.

1
Wenlin.Wu

Im Allgemeinen ist es unmöglich zu tun. Hier ist ein besonders unangenehmer Fall:

struct Point2d {
    int x;
    int y;
};

struct Point3d {
    int x;
    int y;
    int z;
};

void dump(Point3 *p)
{
    printf("[%d %d %d]\n", p->x, p->y, p->z);
}

Point2d points[2] = { {0, 1}, {2, 3} };
Point3d *p3 = reinterpret_cast<Point3d *>(&points[0]);
dump(p3);

Auf vielen Plattformen wird dies ausgedruckt:

[0 1 2]

Sie zwingen das Laufzeitsystem, Speicherbausteine ​​falsch zu interpretieren, aber in diesem Fall wird es nicht zum Absturz führen, da alle Bits sinnvoll sind. Dies ist Teil des Sprachdesigns (siehe Polymorphismus im C-Stil mit struct inaddr, inaddr_in, inaddr_in6), so dass Sie sich auf keiner Plattform zuverlässig schützen können.

1
Tom

Es ist unglaublich, wie viele irreführende Informationen Sie in den obigen Artikeln lesen können ...

Und selbst in der Microsoft-MSDN-Dokumentation wird behauptet, dass IsBadPtr verboten ist. Na ja - ich bevorzuge die Anwendung lieber als das Abstürzen. Auch wenn die Begriffsbearbeitung möglicherweise nicht ordnungsgemäß funktioniert (sofern der Endbenutzer mit der Anwendung fortfahren kann).

Beim Googeln habe ich kein nützliches Beispiel für Windows gefunden - eine Lösung für 32-Bit-Apps gefunden, 

http: //www.tourproject.com/script/Content/ViewAssociatedFile.aspx?rzp=%2FKB%2Fsystem%2Fdetect-driver%2F%2F%20FrettTriverSrc.Zip&zep=DetectDriverSrc%2FDetectDriver%2Fsrc%2Frrc%2Frcpac = 2 & ovid = 2

ich muss aber auch 64-Bit-Apps unterstützen, sodass diese Lösung für mich nicht funktioniert hat.

Aber ich habe die Quellcodes von Wine geerntet und es geschafft, eine ähnliche Art von Code zu kochen, der auch für 64-Bit-Apps geeignet wäre.

#include <typeinfo.h>   

typedef void (*v_table_ptr)();   

typedef struct _cpp_object   
{   
    v_table_ptr*    vtable;   
} cpp_object;   



#ifndef _WIN64
typedef struct _rtti_object_locator
{
    unsigned int signature;
    int base_class_offset;
    unsigned int flags;
    const type_info *type_descriptor;
    //const rtti_object_hierarchy *type_hierarchy;
} rtti_object_locator;
#else

typedef struct
{
    unsigned int signature;
    int base_class_offset;
    unsigned int flags;
    unsigned int type_descriptor;
    unsigned int type_hierarchy;
    unsigned int object_locator;
} rtti_object_locator;  

#endif

/* Get type info from an object (internal) */  
static const rtti_object_locator* RTTI_GetObjectLocator(void* inptr)  
{   
    cpp_object* cppobj = (cpp_object*) inptr;  
    const rtti_object_locator* obj_locator = 0;   

    if (!IsBadReadPtr(cppobj, sizeof(void*)) &&   
        !IsBadReadPtr(cppobj->vtable - 1, sizeof(void*)) &&   
        !IsBadReadPtr((void*)cppobj->vtable[-1], sizeof(rtti_object_locator)))  
    {  
        obj_locator = (rtti_object_locator*) cppobj->vtable[-1];  
    }  

    return obj_locator;  
}  

Und folgender Code kann feststellen, ob der Zeiger gültig ist oder nicht, Sie müssen wahrscheinlich eine NULL-Prüfung hinzufügen:

    CTest* t = new CTest();
    //t = (CTest*) 0;
    //t = (CTest*) 0x12345678;

    const rtti_object_locator* ptr = RTTI_GetObjectLocator(t);  

#ifdef _WIN64
    char *base = ptr->signature == 0 ? (char*)RtlPcToFileHeader((void*)ptr, (void**)&base) : (char*)ptr - ptr->object_locator;
    const type_info *td = (const type_info*)(base + ptr->type_descriptor);
#else
    const type_info *td = ptr->type_descriptor;
#endif
    const char* n =td->name();

Dies erhält den Klassennamen vom Zeiger - ich denke, es sollte für Ihre Bedürfnisse ausreichend sein.

Ich fürchte immer noch die Leistung der Zeigerprüfung - im obigen Code-Snipet werden bereits 3-4 API-Aufrufe gemacht.

Es wäre gut, wenn jemand den Overhead der Zeigerprüfung im Vergleich zu C #/verwalteten C++ - Aufrufen messen könnte.

1
TarmoPikaro

Es gibt keine Möglichkeit, diese Überprüfung in C++ durchzuführen. Was sollten Sie tun, wenn anderer Code einen ungültigen Zeiger übergibt? Du solltest abstürzen. Warum? Schauen Sie sich diesen Link an: http://blogs.msdn.com/oldnewthing/archive/2006/09/27/773741.aspx

0
zildjohn01

diese Links können hilfreich sein 

_CrtIsValidPointer Überprüft, ob ein angegebener Speicherbereich zum Lesen und Schreiben gültig ist (nur Debugversion) . http://msdn.Microsoft.com/de-de/library/0w1ekd5e.aspx

_CrtCheckMemory Bestätigt die Integrität der im Debug-Heap zugewiesenen Speicherblöcke (nur Debug-Version) . http://msdn.Microsoft.com/en-us/library/e73x0s4b.aspx

0
Khachatur

Folgendes funktioniert in Windows (jemand hat es vorher vorgeschlagen):

statische void-Kopie (void * target, const void * source, int size) { __Versuchen { CopyMemory (Ziel, Quelle, Größe); } __except (EXCEPTION_EXECUTE_HANDLER) { doSomething (- was auch immer -); } }

Die Funktion muss eine statische, eigenständige oder statische Methode einer Klasse .. sein. Zum Testen im Nur-Lese-Modus müssen Sie die Daten in den lokalen Puffer kopieren. Wenn Sie beim Schreiben testen, ohne den Inhalt zu ändern, schreiben Sie sie über . Sie können nur die ersten/letzten Adressen testen. Wenn der Zeiger ungültig ist, wird die Kontrolle an 'doSomething',. Und dann außerhalb der Klammern übergeben. Verwenden Sie keine Destruktoren, wie z. B. CString.

0
user2089173

Nachtrag zu der Antwort (den Antworten):

Angenommen, Ihr Zeiger könnte nur drei Werte enthalten - 0, 1 und -1, wobei 1 einen gültigen Zeiger, -1 einen ungültigen und 0 einen anderen ungültigen Zeiger angibt. Wie groß ist die Wahrscheinlichkeit, dass Ihr Zeiger ist NULL, wobei alle Werte gleich wahrscheinlich sind? 1/3. Nehmen Sie jetzt den gültigen Fall heraus, so dass Sie für jeden ungültigen Fall ein Verhältnis von 50: 50 haben, um alle Fehler abzufangen. Sieht gut aus, richtig? Skalieren Sie dies für einen 4-Byte-Zeiger. Es gibt 2 ^ 32 oder 4294967294 mögliche Werte. Von diesen ist nur EINER Wert korrekt, einer ist NULL und Sie haben noch 4294967292 andere ungültige Fälle. Neuberechnung: Sie haben einen Test für 1 von (4294967292+ 1) ungültigen Fällen. Für die meisten praktischen Zwecke ist eine Wahrscheinlichkeit von 2.xe-10 oder 0. Dies ist die Sinnlosigkeit der NULL-Prüfung.

0
dirkgently

Unter Windows verwende ich diesen Code:

void * G_pPointer = NULL;
const char * G_szPointerName = NULL;
void CheckPointerIternal()
{
    char cTest = *((char *)G_pPointer);
}
bool CheckPointerIternalExt()
{
    bool bRet = false;

    __try
    {
        CheckPointerIternal();
        bRet = true;
    }
    __except (EXCEPTION_EXECUTE_HANDLER)
    {
    }

    return  bRet;
}
void CheckPointer(void * A_pPointer, const char * A_szPointerName)
{
    G_pPointer = A_pPointer;
    G_szPointerName = A_szPointerName;
    if (!CheckPointerIternalExt())
        throw std::runtime_error("Invalid pointer " + std::string(G_szPointerName) + "!");
}

Verwendungszweck:

unsigned long * pTest = (unsigned long *) 0x12345;
CheckPointer(pTest, "pTest"); //throws exception
0
Artkov

IsBadReadPtr (), IsBadWritePtr (), IsBadCodePtr (), IsBadStringPtr () für Windows.
Diese dauern eine Zeit, die proportional zur Länge des Blocks ist, daher überprüfe ich zur Überprüfung der Gültigkeit nur die Startadresse.

0
pngaz

Wissen Sie, ein neuer Treiber (zumindest unter Linux), der dazu in der Lage ist, wäre wahrscheinlich nicht so schwer zu schreiben.

Andererseits wäre es töricht, Ihre Programme so zu bauen. Wenn Sie nicht wirklich spezifische und einmalige Verwendung für so etwas haben, würde ich es nicht empfehlen. Wenn Sie eine große Anwendung erstellt haben, die ständig mit Gültigkeitsprüfungen für Zeiger geladen wird, ist dies wahrscheinlich äußerst langsam.

0
dicroce

Ich habe gesehen, dass verschiedene Bibliotheken eine Methode verwenden, um auf nicht referenzierten Speicher zu prüfen. Ich glaube, sie "überschreiben" einfach die Speicherzuweisungs- und Deallocation-Methoden (malloc/free), was eine gewisse Logik hat, die die Zeiger verfolgt. Ich nehme an, dies ist ein Overkill für Ihren Anwendungsfall, aber es wäre eine Möglichkeit, dies zu tun.

0
sebnow

Technisch gesehen können Sie den Operator new (und delete) überschreiben und Informationen über den gesamten zugewiesenen Speicher sammeln. Sie können also eine Methode verwenden, um zu überprüfen, ob der Heap-Speicher gültig ist.

  1. sie benötigen noch eine Möglichkeit, um zu überprüfen, ob der Zeiger auf stack () zugewiesen ist.

  2. sie müssen den gültigen Zeiger definieren:

a) Der Speicher dieser Adresse ist zugeteilt 

b) Speicher an dieser Adresse ist start Adresse des Objekts (z. B. Adresse nicht in der Mitte eines großen Arrays)

c) Speicher an dieser Adresse ist start Adresse des Objekts vom Typ erwartet

Bottom line: Der fragliche Ansatz ist keine C++ - Methode. Sie müssen einige Regeln definieren, die sicherstellen, dass die Funktion gültige Zeiger erhält.

0
noonex

sie sollten diese Methoden vermeiden, da sie nicht funktionieren. blogs.msdn.com/oldnewthing/archive/2006/09/27/773741.aspx - JaredPar 15. Februar 09 um 09:02 Uhr

Wenn sie nicht funktionieren - das nächste Windows-Update wird das Problem beheben? Wenn sie nicht auf Konzeptebene funktionieren, wird die Funktion wahrscheinlich vollständig aus der Windows-API entfernt. 

Die MSDN-Dokumentation behauptet, dass sie verboten sind, und der Grund dafür ist wahrscheinlich ein Fehler in der weiteren Gestaltung der Anwendung (z. B. sollten Sie im Allgemeinen keine ungültigen Zeiger - wenn Sie für die Gestaltung der gesamten Anwendung verantwortlich sind - und die Leistung/Zeit in Ruhe essen) der Zeigerprüfung.

Sie sollten jedoch nicht behaupten, dass sie aufgrund eines Blogs nicht funktionieren .. In meiner Testanwendung habe ich bestätigt, dass sie funktionieren.

0
TarmoPikaro