Überprüfen, ob einem Zeiger Speicher zugewiesen ist oder nicht

74

Können wir überprüfen, ob ein an eine Funktion übergebener Zeiger in C mit Speicher belegt ist oder nicht?

Ich habe meine eigene Funktion in C geschrieben, die einen Zeichenzeiger - buf [Zeiger auf einen Puffer] und size - buf_siz [Puffergröße] akzeptiert . Tatsächlich muss der Benutzer vor dem Aufrufen dieser Funktion einen Puffer erstellen und ihm den Speicher von buf_siz zuweisen.

Da die Möglichkeit besteht, dass der Benutzer die Speicherzuweisung vergisst und einfach den Zeiger auf meine Funktion übergibt, möchte ich dies überprüfen. Gibt es also eine Möglichkeit, meine Funktion zu überprüfen, um festzustellen, ob der übergebene Zeiger wirklich mit der Speichermenge von buf_siz belegt ist?

EDIT1: Es scheint, dass es keine Standardbibliothek gibt, um es zu überprüfen. Aber gibt es einen schmutzigen Hack, um es zu überprüfen?

EDIT2: Ich weiß, dass meine Funktion von einem guten C-Programmierer verwendet wird ... Aber ich möchte wissen, ob wir prüfen können oder nicht. Wenn wir können, würde ich gerne davon hören.

Schlussfolgerung: Es ist daher unmöglich zu überprüfen, ob einem bestimmten Zeiger ein Speicher zugewiesen ist oder nicht

Codingfreak
quelle
3
Ich denke nicht wirklich, aber ich fühle mich nicht sicher genug, um als Antwort zu posten.
Ibrahim
Es gibt keine Möglichkeit zu überprüfen, es sei denn, Sie verwenden einen Speichermanager oder rollen Ihren eigenen.
Michael Foukarakis
Wenn es sich um einen Zeichenzeiger handelt, können wir strlen () oder sizeof () ausführen und überprüfen, wie viel Speicher zugewiesen ist (natürlich, wenn die Zeichenfolge mit NULL abgeschlossen ist). Bei anderen Typen bin ich mir nicht sicher, ob es einen Weg gibt. !!
mk ..
Ich weiß, dass dies eine alte Frage ist, aber es ist möglich, den zugewiesenen Speicher zu verfolgen, ohne Hacks zu verwenden. Mein Code unten enthält einige Ausschnitte, um Ihnen den Einstieg zu erleichtern.
c1moore
1
Die Schlussfolgerung, die gezogen werden sollte, ist, dass Sie nicht überprüfen sollten , selbst wenn es möglich war. Dieser Artikel erklärt das Problem. Während das Problem in einem Windows-Begriff geschrieben ist, ist es nicht Windows-spezifisch.
Ikegami

Antworten:

32

Sie können nur einige implementierungsspezifische Hacks überprüfen.

Zeiger haben keine anderen Informationen als die, auf die sie zeigen. Das Beste, was Sie tun können, ist zu sagen: "Ich weiß, wie diese bestimmte Compiler-Version Speicher zuweist, also werde ich den Speicher dereferenzieren, den Zeiger um 4 Bytes zurückbewegen, die Größe überprüfen, sicherstellen, dass er übereinstimmt ..." und so weiter. Sie können dies nicht auf standardmäßige Weise tun, da die Speicherzuweisung für die Implementierung definiert ist. Ganz zu schweigen davon, dass sie es möglicherweise überhaupt nicht dynamisch zugewiesen haben.

Sie müssen nur davon ausgehen, dass Ihr Client weiß, wie man in C programmiert. Die einzige Lösung, die ich mir vorstellen kann, besteht darin, den Speicher selbst zuzuweisen und zurückzugeben, aber das ist kaum eine kleine Änderung. (Es ist eine größere Designänderung.)

GManNickG
quelle
Ein Zeiger kann nicht null sein, es sind jedoch noch keine buf_siz-Bytes zugewiesen. Ich glaube nicht, dass es wirklich eine Möglichkeit gibt, zu überprüfen, was der Fragesteller will.
Ibrahim
1
Ok, wie wäre es damit? Da dies C ist, wird wahrscheinlich der Client verwendet, mallocder einen NULLZeiger zurückgibt, wenn er keinen Speicher zuordnen konnte. Also ... darauf mallocvertrauen wir?
Jacob
Es ist Sache des Kunden, sicherzustellen, dass malloc funktioniert, bevor er die Funktion aufruft, wenn Sie dies sagen.
GManNickG
@jacob - Ich weiß, dass wir bei malloc nachsehen können ... aber wenn der Client vergisst, malloc zu machen, führt dies zu einem Segmentierungsfehler ... und ich möchte es vermeiden.
Codingfreak
7
Ja. Die Endschlussfolgerung ist, dass Ihre Funktion nur eines und nur eines tun sollte. Stellen Sie sich den Overhead vor, wenn jede Funktion sicherstellt, dass der Speicher, auf den über Parameter zugegriffen wird, gültig ist. Lassen Sie einfach Ihre Funktion das tun, was sie tun soll.
GManNickG
10

Der folgende Code wurde einmal verwendet, um zu überprüfen, ob ein Zeiger versucht, auf illegalen Speicher zuzugreifen. Der Mechanismus besteht darin, ein SIGSEGV zu induzieren. Das SEGV-Signal wurde früher zu einer privaten Funktion umgeleitet, die longjmp verwendet, um zum Programm zurückzukehren. Es ist eine Art Hack, aber es funktioniert.

Der Code kann verbessert werden (verwenden Sie 'Sigaction' anstelle von 'Signal' usw.), aber es ist nur eine Idee. Es ist auch auf andere Unix-Versionen portierbar, für Windows bin ich mir nicht sicher. Beachten Sie, dass das SIGSEGV-Signal nicht an einer anderen Stelle in Ihrem Programm verwendet werden sollte.

#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>
#include <signal.h>

jmp_buf jump;

void segv (int sig)
{
  longjmp (jump, 1); 
}

int memcheck (void *x) 
{
  volatile char c;
  int illegal = 0;

  signal (SIGSEGV, segv);

  if (!setjmp (jump))
    c = *(char *) (x);
  else
    illegal = 1;

  signal (SIGSEGV, SIG_DFL);

  return (illegal);
}

int main (int argc, char *argv[])
{
  int *i, *j; 

  i = malloc (1);

  if (memcheck (i))
    printf ("i points to illegal memory\n");
  if (memcheck (j))
    printf ("j points to illegal memory\n");

  free (i);

  return (0);
}
Peter
quelle
3
@Saco i = malloc(1);ist ein gültiger C-Code und vorzuziehen i = (int*) malloc(1);. Vielleicht denken Sie an eine andere Sprache.
chux
Hinweis unter POSIX setjmp()und longjmp()sollte wahrscheinlich durch sigsetjmp()und ersetzt werden siglongjmp(). Siehe stackoverflow.com/questions/20755260/…
Andrew Henle
IMHO gibt es keine Garantie dafür, dass ein ungültiger Speicherzugriff eine SEGV verursacht - Sie c = *(char *)(x);könnten gut durchgehen , obwohl Sie xnicht auf einen zugewiesenen Bereich verweisen. SEGVWird nur ausgelöst, wenn der Zeiger innerhalb eines Speichersegments zeigt, auf das nicht zugegriffen werden kann, die Segmente jedoch mehrere KB groß sind. Wenn Sie also 4 Bytes zuweisen 10, ändert sich die Mem-Adresse 20trotz außerhalb eines zugewiesenen Bereichs immer noch im selben Segment Als Adresse 10, daher ohne Zuweisung, können Sie dennoch 20ohne SEGV auf die Adresse zugreifen.
Michael Beer
Deshalb sollen Sie immer nicht verwendeten Zeiger gesetzt sollen NULL, denn dieser Wert ist , garantiert eine SEGV verursachen , wenn Sie versuchen , es zu dereferenzieren ... Es ist nicht für andere Speicheradresse garantiert.
Michael Beer
9

Für eine plattformspezifische Lösung interessieren Sie möglicherweise die Win32-Funktion IsBadReadPtr(und andere, die sie mögen). Diese Funktion kann (fast) vorhersagen, ob beim Lesen aus einem bestimmten Speicherblock ein Segmentierungsfehler auftritt.

Dies schließt jedoch nicht schützt Sie im allgemeinen Fall, da das Betriebssystem nichts von dem C - Laufzeit - Heap - Manager kennt, und wenn ein Anrufer Stiche in einem Puffer, der nicht so groß ist wie Sie erwarten, dann den Rest der Heap - Blöcke wird aus Sicht des Betriebssystems weiterhin lesbar sein.

Greg Hewgill
quelle
@ Greg - Tut mir leid zu sagen, dass ich nicht sehr an WIN32-Funktionen interessiert bin. Wenn möglich, ist ein gut funktionierender Dirty Hack auch in Ordnung, da es KEINE Standard-C-Funktion gibt
Codingfreak
2
Okay, Sie hat nicht angegeben , welche Plattform Sie sind in interessiert. Angeben der Plattform und Compiler können Sie eine bestimmte Antwort bekommen.
Greg Hewgill
7

Ich initialisiere Zeiger immer auf Null. Wenn ich Speicher reserviere, ändert sich dies. Wenn ich überprüfe, ob Speicher zugewiesen wurde, tue ich das pointer != NULL. Wenn ich Speicher freigebe, setze ich auch den Zeiger auf null. Ich kann mir keine Möglichkeit vorstellen, festzustellen, ob genügend Speicher zugewiesen wurde.

Dies löst Ihr Problem nicht, aber Sie müssen darauf vertrauen, dass jemand, der C-Programme schreibt, geschickt genug ist, um es richtig zu machen.

Yelonek
quelle
@ Yelonek .. Ich stimme Ihnen zu, aber ich möchte wirklich wissen, ob es eine Möglichkeit gibt, dies zu überprüfen ....
Codingfreak
1
Ich auch, aber (besonders in Bibliotheken) s *** passiert.
Sellorio
7

Ich habe einmal einen Dirty Hack auf meinem 64-Bit-Solaris verwendet. Im 64-Bit-Modus beginnt der Heap bei 0x1 0000 0000. Durch Vergleichen des Zeigers konnte ich feststellen, ob es sich um einen Zeiger im Daten- oder Codesegment p < (void*)0x100000000, einen Zeiger im Heap p > (void*)0x100000000oder einen Zeiger in einem Speicherbereich handelt (intptr_t)p < 0(mmap gibt Adressen von oben zurück des adressierbaren Bereichs). Dies ermöglichte es meinem Programm, zugewiesene und speicherabgebildete Zeiger in derselben Karte zu halten und mein Kartenmodul die richtigen Zeiger freizugeben.

Diese Art von Trick ist jedoch höchst unportabel. Wenn Ihr Code auf so etwas basiert, ist es an der Zeit, die Architektur Ihres Codes zu überdenken. Du machst wahrscheinlich etwas falsch.

Patrick Schlüter
quelle
4

Nein, im Allgemeinen gibt es keine Möglichkeit, dies zu tun.

Wenn Ihre Schnittstelle nur "einen Zeiger auf einen Puffer übergeben, in den ich etwas einfügen werde", kann der Anrufer außerdem festlegen, dass überhaupt kein Speicher zugewiesen wird, und stattdessen einen statisch zugewiesenen Puffer fester Größe oder eine automatische Variable oder etwas anderes verwenden. Oder vielleicht ist es ein Zeiger auf einen Teil eines größeren Objekts auf dem Heap.

Wenn Ihre Schnittstelle ausdrücklich sagt "Übergeben Sie einen Zeiger auf den zugewiesenen Speicher (weil ich ihn freigeben werde)", sollten Sie damit rechnen, dass der Anrufer dies tut. Andernfalls können Sie dies nicht zuverlässig erkennen.

Greg Hewgill
quelle
Obwohl dies im Allgemeinen die beste und meist richtige Antwort ist, würde ich sagen: Bei genügend Aufwand können Sie Ihren eigenen benutzerdefinierten Loader implementieren, um alle Speicherzuordnungen zu verfolgen - oder ein vorhandenes Tool wie valgrind;) verwenden
Michael Beer
3

Ein Hack, den Sie versuchen können, besteht darin, zu überprüfen, ob Ihr Zeiger auf den zugewiesenen Speicher zeigt. Dies hilft Ihnen im Allgemeinen nicht weiter, da der zugewiesene Puffer möglicherweise zu klein ist oder der Zeiger auf einen globalen Speicherabschnitt (.bss, .const, ...) zeigt.

Um diesen Hack auszuführen, speichern Sie zuerst die Adresse der ersten Variablen in main (). Später können Sie diese Adresse mit der Adresse einer lokalen Variablen in Ihrer spezifischen Routine vergleichen. Alle Adressen zwischen beiden Adressen befinden sich auf dem Stapel.

Swegi
quelle
Ja ... Wenn ich die gesamte Anwendung schreibe, kann ich das tun. Aber um eine Funktion zum Überprüfen von Dingen zu verwenden, kann dies komplex sein.
Codingfreak
Dies kann dazu führen, dass jemand denkt, dass sich nicht initialisierte Zeiger auf dem Haufen befinden. Wenn jemand zufällig einen Zeiger auf etwas weiter unten (oben?) Des Stapels speichert, der später abgesprungen ist, um Ihre Funktion zu erhalten, wird dies auch auf dem Heap berücksichtigt.
Kevin
Das Unterscheiden von Zeigern in Bezug auf die Zuweisung auf dem Heap oder Stack hilft hier nicht wirklich - was wäre, char copy[255] = {0}; snprintf(copy, sizeof(copy), "%n: %s\n", error_code, error_msg); copy[sizeof(copy) -1] = 0; write(log_fd, copy, strnlen(copy) + 1); copy[0] = 0; wenn snprintfseltsame Überprüfungen, wie Sie vorgeschlagen haben, snprintffälschlicherweise als copyungültiger Zeiger angesehen würden ...
Michael Beer
3

Ich weiß , dass dies eine alte Frage, aber fast alles ist möglich in C. ein paar hackish Lösungen hier schon gibt es aber eine gültige Art und Weise zu bestimmen , ob Speicher richtig zugeordnet worden ist , ein Orakel zu verwenden , um den Platz zu nehmen malloc, calloc, realloc, und free. Auf diese Weise können Test-Frameworks (wie z. B. cmocka) Speicherprobleme erkennen (Seg-Fehler, Speicherfreigabe usw.). Sie können eine Liste der zugewiesenen Speicheradressen verwalten, während diese zugewiesen werden, und diese Liste einfach überprüfen, wenn der Benutzer Ihre Funktion verwenden möchte. Ich habe etwas sehr Ähnliches für mein eigenes Test-Framework implementiert. Ein Beispielcode:

typedef struct memory_ref {
    void       *ptr;
    int        bytes;
    memory_ref *next;
}

memory_ref *HEAD = NULL;

void *__wrap_malloc(size_t bytes) {
    if(HEAD == NULL) {
        HEAD = __real_malloc(sizeof(memory_ref));
    }

    void *tmpPtr = __real_malloc(bytes);

    memory_ref *previousRef = HEAD;
    memory_ref *currentRef = HEAD->next;
    while(current != NULL) {
        previousRef = currentRef;
        currentRef = currentRef->next;
    }

    memory_ref *newRef = (memory_ref *)__real_malloc(sizeof(memory_ref));
    *newRef = (memory_ref){
        .ptr   = tmpPtr,
        .bytes = bytes,
        .next  = NULL
    };

    previousRef->next = newRef;

    return tmpPtr;
}

Sie müßten ähnliche Funktionen für calloc, reallocund free, jede Wrapper mit dem Präfix __wrap_. Der Real mallocist über die Verwendung von verfügbar __real_malloc(ähnlich für die anderen Funktionen, die Sie einschließen). Wenn Sie überprüfen möchten, ob tatsächlich Speicher zugewiesen ist, durchlaufen Sie einfach die verknüpfte memory_refListe und suchen Sie nach der Speicheradresse. Wenn Sie es finden und es groß genug ist, wissen Sie mit Sicherheit, dass die Speicheradresse Ihr Programm nicht zum Absturz bringt. Andernfalls geben Sie einen Fehler zurück. In der Header-Datei, die Ihr Programm verwendet, würden Sie folgende Zeilen hinzufügen:

extern void *__real_malloc  (size_t);
extern void *__wrap_malloc  (size_t);
extern void *__real_realloc (size_t);
extern void *__wrap_realloc (size_t);
// Declare all the other functions that will be wrapped...

Meine Anforderungen waren ziemlich einfach, daher habe ich eine sehr grundlegende Implementierung implementiert. Sie können sich jedoch vorstellen, wie diese erweitert werden könnte, um ein besseres Tracking-System zu erhalten (z. B. ein System zu erstellen struct, das zusätzlich zur Größe den Speicherort verfolgt). Dann kompilieren Sie einfach den Code mit

gcc src_files -o dest_file -Wl,-wrap,malloc -Wl,-wrap,calloc -Wl,-wrap,realloc -Wl,-wrap,free

Der Nachteil ist, dass der Benutzer seinen Quellcode mit den oben genannten Anweisungen kompilieren muss. Es ist jedoch alles andere als das Schlimmste, das ich gesehen habe. Das Zuweisen und Freigeben von Speicher ist mit einem gewissen Aufwand verbunden, beim Hinzufügen von Sicherheit jedoch immer mit einem gewissen Aufwand.

c1moore
quelle
2

Ich kenne keine Möglichkeit, dies aus einem Bibliotheksaufruf heraus zu tun, aber unter Linux können Sie sich das ansehen /proc/<pid>/numa_maps . Es werden alle Speicherbereiche angezeigt und in der dritten Spalte wird "Heap" oder "Stack" angezeigt. Sie können sich den Rohzeigerwert ansehen, um zu sehen, wo er ausgerichtet ist.

Beispiel:

00400000 prefer:0 file=/usr/bin/bash mapped=163 mapmax=9 N0=3 N1=160
006dc000 prefer:0 file=/usr/bin/bash anon=1 dirty=1 N0=1
006dd000 prefer:0 file=/usr/bin/bash anon=9 dirty=9 N0=3 N1=6
006e6000 prefer:0 anon=6 dirty=6 N0=2 N1=4
01167000 prefer:0 heap anon=122 dirty=122 N0=25 N1=97
7f39904d2000 prefer:0 anon=1 dirty=1 N0=1
7f39904d3000 prefer:0 file=/usr/lib64/ld-2.17.so anon=1 dirty=1 N0=1
7f39904d4000 prefer:0 file=/usr/lib64/ld-2.17.so anon=1 dirty=1 N1=1
7f39904d5000 prefer:0 anon=1 dirty=1 N0=1
7fffc2d6a000 prefer:0 stack anon=6 dirty=6 N0=3 N1=3
7fffc2dfe000 prefer:0

Zeiger, die über 0x01167000, aber unter 0x7f39904d2000 liegen, befinden sich im Heap.

Mark Lakata
quelle
1

Sie können nichts überprüfen, was in Standard C verfügbar ist. Selbst wenn Ihr spezifischer Compiler eine Funktion dafür bereitstellen würde, wäre dies immer noch eine schlechte Idee. Hier ist ein Beispiel dafür, warum:

int YourFunc(char * buf, int buf_size);

char str[COUNT];
result = YourFunc(str, COUNT);
Mark Ransom
quelle
@Mark - In dem Code weisen Sie str als Array der Größe COUNT zu. Daher kann ich in 'YourFunc' weiterhin Operationen wie strcpy innerhalb der Größe von buf_size ausführen. Wenn str jedoch nur ein Zeichenzeiger ist, führt der Versuch, eine strcpy-Operation der Größe buf_size auszuführen, zu einem 'Segmentierungsfehler'
Codierungsfehler
2
Das ist SEHR SEHR falsch, Codingfreak. Der Segmentierungsfehler tritt auf, wenn 'str' ein Zeichenzeiger ist, der auf den Speicher zeigt, auf den Sie nicht zugreifen dürfen. Es passiert nicht, weil 'str' ein Zeichenzeiger ist, sondern weil Sie das Programm bitten, etwas zu tun, was es nicht darf.
Gnud
1

Wie alle anderen sagten, gibt es keinen Standardweg.

Bisher hat noch niemand ' Writing Solid Code ' von Steve Maguire erwähnt. Obwohl in einigen geißelte Viertel , hat das Buchkapitel zum Thema Speicherverwaltung und erläutert , wie mit Sorgfalt und die vollständige Kontrolle über alle Speicherzuweisung im Programm, können Sie tun , wie Sie fragen , und bestimmen , ob ein Zeiger Sie gegeben sind a gültiger Zeiger auf dynamisch zugewiesenen Speicher. Wenn Sie jedoch Bibliotheken von Drittanbietern verwenden möchten, werden Sie feststellen, dass nur wenige von ihnen es Ihnen ermöglichen, die Speicherzuweisungsroutinen in Ihre eigenen zu ändern, was eine solche Analyse erheblich erschwert.

Jonathan Leffler
quelle
@ Jonathan - Was meinst du mit Bibliotheken von Drittanbietern - ?? Ich benutze nur Standardbibliotheken und ISO C99. Aber ich werde nur das Buch ausprobieren, das Sie empfohlen haben.
Codingfreak
Bibliotheken von Drittanbietern sind alles, was Sie nicht geschrieben haben, einschließlich der Standardbibliotheken. Wenn malloc () irgendwo verwendet wird, fällt es Ihnen grob schwer, diese Anrufe durch Ihren eigenen Speicherzuweiser zu ersetzen, was bedeutet, dass es schwierig ist, Missbräuche zu verfolgen. Möglicherweise müssen Sie sich für anspruchsvollere Speicherverfolgungsaufgaben entscheiden - sehen Sie sich Debugging-Versionen von malloc, valgrind, Purify usw. an. (Es ist der Fluch meines Lebens - wir können die meisten Bibliotheken von außen nicht ohne harte Arbeit verwenden, weil das Produkt I Die Arbeit an hat unerträgliche Anforderungen an die Speicherverwaltung, die Bibliotheken weder kennen noch interessieren.)
Jonathan Leffler
1

Im Allgemeinen sind lib-Benutzer für die Eingabeprüfung und -verifizierung verantwortlich. Möglicherweise sehen Sie ASSERT oder etwas im lib-Code und sie werden nur für Debug-Zwecke verwendet. Dies ist eine Standardmethode beim Schreiben von C / C ++. während so viele Programmierer solche Überprüfungen und Verifizierungen in ihrem Lib-Code sehr sorgfältig durchführen möchten. wirklich "schlechte" Gewohnheiten. Wie in IOP / IOD angegeben, sollten lib-Schnittstellen die Verträge sein und klar machen, was die lib tun wird und was nicht, was ein lib-Benutzer tun sollte und was nicht notwendig sein sollte.

Prüfung
quelle
1

Es gibt einen einfachen Weg, dies zu tun. Wenn Sie einen Zeiger erstellen, schreiben Sie einen Wrapper darum. Zum Beispiel, wenn Ihr Programmierer Ihre Bibliothek verwendet, um eine Struktur zu erstellen.

struct struct_type struct_var;

Stellen Sie sicher, dass er Speicher mit Ihrer Funktion wie z

struct struct_type struct_var = init_struct_type()

Wenn diese struct_var Speicher enthält, der dynamisch zugewiesen wird, z.

wenn die Definition von struct_type war

typedef struct struct_type {
 char *string;
}struct_type;

Führen Sie dann in Ihrer Funktion init_struct_type () Folgendes aus:

init_struct_type()
{ 
 struct struct_type *temp = (struct struct_type*)malloc(sizeof(struct_type));
 temp->string = NULL;
 return temp;
}

Auf diese Weise bleibt er NULL, es sei denn, er weist einem Wert die temporäre Zeichenfolge zu. Sie können in den Funktionen, die diese Struktur verwenden, einchecken, ob die Zeichenfolge NULL ist oder nicht.

Eine weitere Sache, wenn der Programmierer so schlecht ist, dass er Ihre Funktionen nicht verwendet, sondern direkt auf nicht zugewiesenen Speicher zugreift, hat er es nicht verdient, Ihre Bibliothek zu verwenden. Stellen Sie einfach sicher, dass Ihre Dokumentation alles spezifiziert.

wlan0
quelle
1

Nun, ich weiß nicht, ob jemand es nicht schon hier platziert hat oder ob es eine Möglichkeit in Ihrem Programm sein wird. Ich hatte in meinem Universitätsprojekt mit ähnlichen Dingen zu kämpfen.

Ich habe es ganz einfach gelöst - Im Initialisierungsteil von main () habe LIST *ptrich das einfach gesagt , nachdem ich es deklariert habe ptr=NULL. So was -

int main(int argc, char **argv) {

LIST *ptr;
ptr=NULL;

Wenn die Zuordnung fehlschlägt oder Ihr Zeiger überhaupt nicht zugewiesen wird, ist er NULL. SO können Sie es einfach mit if testen.

if (ptr==NULL) {
  "THE LIST DOESN'T EXIST" 
} else {
  "THE LIST MUST EXIST --> SO IT HAS BEEN ALLOCATED"
}

Ich weiß nicht, wie Ihr Programm geschrieben ist, aber Sie verstehen sicherlich, worauf ich hinweisen möchte. Wenn es möglich ist, Ihre Zuordnung auf diese Weise zu überprüfen und dann Ihre Argumente an Ihre Funktion zu übergeben, könnten Sie eine einfache Lösung haben.

Natürlich müssen Sie darauf achten, dass Ihre Funktionen beim Zuweisen und Erstellen der Struktur gut ausgeführt werden, aber wo in C müssen Sie nicht vorsichtig sein.

Patrick J.
quelle
0

Nein, das kannst du nicht. Sie werden feststellen, dass dies in der Standardbibliothek oder anderswo nicht funktioniert. Das liegt daran, dass es keine Standardmethode gibt. Der aufrufende Code muss nur die Verantwortung für die korrekte Verwaltung des Speichers übernehmen.

Futter
quelle
@Chuck, wenn es keine Standardbibliotheksfunktion gibt, um zu überprüfen, ob es einen anderen Ausweg gibt ..?
Codingfreak
0

Ein nicht initialisierter Zeiger ist genau das - nicht initialisiert. Es kann auf irgendetwas verweisen oder einfach eine ungültige Adresse sein (dh eine, die nicht dem physischen oder virtuellen Speicher zugeordnet ist).

Eine praktische Lösung besteht darin, eine Gültigkeitsunterschrift in den Objekten zu haben, auf die verwiesen wird. Erstellen Sie einen malloc () - Wrapper, der die angeforderte Blockgröße plus die Größe einer Signaturstruktur zuweist, eine Signaturstruktur am Anfang des Blocks erstellt, aber den Zeiger auf die Position nach der Signatur zurückgibt. Sie können dann eine Validierungsfunktion erstellen, die den Zeiger verwendet, einen negativen Offset verwendet, um die Gültigkeitsstruktur abzurufen und zu überprüfen. Sie benötigen natürlich einen entsprechenden free () - Wrapper, um den Block durch Überschreiben der Gültigkeitssignatur ungültig zu machen und das Free ab dem tatsächlichen Start des zugewiesenen Blocks auszuführen.

Als Gültigkeitsstruktur können Sie die Größe des Blocks und dessen Ergänzung verwenden. Auf diese Weise können Sie nicht nur den Block validieren (XOR die beiden Werte und mit Null vergleichen), sondern auch Informationen zur Blockgröße erhalten.

Clifford
quelle
Vielleicht möchten Sie Ihren ersten Satz überprüfen: "Ein initialisierter Zeiger ist genau das - nicht initialisiert."
Chris Lutz
0

Ein Zeiger-Tracker verfolgt und überprüft die Gültigkeit eines Zeigers

Verwendung:

Speicher erstellen int * ptr = malloc (sizeof (int) * 10);

Fügen Sie die Zeigeradresse zum Tracker hinzu. Ptr (& ptr);

auf fehlerhafte Zeiger prüfen PtrCheck ();

und geben Sie alle Tracker am Ende Ihres Codes frei

PtrFree ();

 #include <stdlib.h>
 #include <string.h>
 #include <stdio.h>
 #include <stdint.h>
 #include <stdbool.h>

struct my_ptr_t { void ** ptr; size_t mem; struct my_ptr_t *next, *previous; };

static struct my_ptr_t * ptr = NULL;

void Ptr(void * p){ 
                struct my_ptr_t * tmp = (struct my_ptr_t*) malloc(sizeof(struct my_ptr_t));
                printf("\t\tcreating Ptr tracker:");    
                if(ptr){ ptr->next = tmp; }
                tmp->previous = ptr;
                ptr = tmp;
                ptr->ptr = p;
                ptr->mem = **(size_t**) ptr->ptr;
                ptr->next = NULL;
                printf("%I64x\n", ptr); 
};
void PtrFree(void){
                    if(!ptr){ return; }
                    /* if ptr->previous == NULL */
                    if(!ptr->previous){ 
                                    if(*ptr->ptr){
                                                free(ptr->ptr);
                                                ptr->ptr = NULL;
                                    }
                                    free(ptr);
                                    ptr = NULL; 
                            return;                 
                    }

                    struct my_ptr_t * tmp = ptr;    
                    for(;tmp != NULL; tmp = tmp->previous ){
                                            if(*tmp->ptr){
                                                        if(**(size_t**)tmp->ptr == tmp->mem){
                                                                                                                                                    free(*tmp->ptr);
                                                                        *tmp->ptr = NULL;
                                                        }
                                            }
                                        free(tmp);
                    } 
            return; 
};

void PtrCheck(void){
                if(!ptr){ return; }
                if(!ptr->previous){
                        if(*(size_t*)ptr->ptr){
                                    if(*ptr->ptr){
                                                if(**(size_t**) ptr->ptr != ptr->mem){
                                                                printf("\tpointer %I64x points not to a valid memory address", ptr->mem);
                                                                printf(" did you freed the memory and not NULL'ed the pointer or used arthmetric's on pointer %I64x?\n", *ptr->ptr);
                                                                return; 
                                                        }   
                                    }
                                    return;
                                }
                        return;
                }
                struct my_ptr_t * tmp = ptr;
                for(;tmp->previous != NULL; tmp = tmp->previous){   
                                if(*(size_t*)tmp->ptr){         
                                                   if(*tmp->ptr){
                                                            if(**(size_t**) tmp->ptr != tmp->mem){
                                                                        printf("\tpointer %I64x points not to a valid memory address", tmp->mem);
                                                                        printf(" did you freed the memory and not NULL'ed the pointer or used arthmetric's on pointer %I64x?\n", *tmp->ptr);                            continue;
                                                            } 
                                                    }
                                                    continue;
                                }

                } 
            return;
       };

 int main(void){
        printf("\n\n\t *************** Test ******************** \n\n");
        size_t i = 0;
        printf("\t *************** create tracker ********************\n");
        int * ptr = malloc(sizeof(int) * 10);
        Ptr(&ptr);
        printf("\t *************** check tracker ********************\n");
        PtrCheck();
        printf("\t *************** free pointer ********************\n");
        free(ptr);
        printf("\t *************** check tracker ********************\n");
        PtrCheck();
        printf("\t *************** set pointer NULL *******************\n");
        ptr = NULL;
        printf("\t *************** check tracker ********************\n");
                PtrCheck();
        printf("\t *************** free tracker ********************\n");
        PtrFree();
        printf("\n\n\t *************** single check done *********** \n\n");
        printf("\n\n\t *************** start multiple test *********** \n");
        int * ptrs[10];
        printf("\t *************** create trackers ********************\n");
        for(; i < 10; i++){
                        ptrs[i] = malloc(sizeof(int) * 10 * i);
                        Ptr(&ptrs[i]);
                 }
        printf("\t *************** check trackers ********************\n");
        PtrCheck();
        printf("\t *************** free pointers but set not NULL *****\n");
        for(i--; i > 0; i-- ){ free(ptrs[i]); }
        printf("\t *************** check trackers ********************\n");
        PtrCheck(); 
        printf("\t *************** set pointers NULL *****************\n");
        for(i=0; i < 10; i++){ ptrs[i] = NULL; }
        printf("\t *************** check trackers ********************\n");
        PtrCheck(); 
        printf("\t *************** free trackers ********************\n");
        PtrFree();
        printf("\tdone");
    return 0;
 }
Andre
quelle
-1

Es gibt fast nie "nie" in Computern. Plattformübergreifend ist weit über den Erwartungen. Nach 25 Jahren habe ich an Hunderten von Projekten gearbeitet, die alle plattformübergreifend waren, und es kam nie zustande.

Offensichtlich würde eine Variable auf dem Stapel auf einen Bereich auf dem Stapel zeigen, der fast linear ist. Plattformübergreifende Garbage Collectors markieren die Oberseite oder (Unterseite) des Stapels, rufen eine kleine Funktion auf, um zu überprüfen, ob der Stapel nach oben oder unten wächst, und überprüfen dann den Stapelzeiger, um festzustellen, wie groß der Stapel ist. Dies ist Ihre Reichweite. Ich kenne keine Maschine, die keinen Stack auf diese Weise implementiert (entweder beim Aufwachsen oder beim Aufwachsen).

Sie überprüfen einfach, ob sich die Adresse unseres Objekts oder Zeigers zwischen dem oberen und unteren Rand des Stapels befindet. So würden Sie wissen, ob es sich um eine Stapelvariable handelt.

Zu einfach. Hey, ist es richtig c ++? Ist richtig wichtig? In 25 Jahren habe ich viel mehr Einschätzung der Richtigkeit gesehen. Sagen wir es so: Wenn Sie hacken, programmieren Sie nicht wirklich, sondern regurigieren wahrscheinlich nur etwas, was bereits getan wurde.

Wie interessant ist das

Jordanien
quelle
1
Die ursprüngliche Frage betraf C, nicht C ++, erwähnte oder implizierte keine On-Stack-Variablen und ging nicht um interessante / neue / einzigartige Dinge.
Alexey Frunze
Außerdem führen malloc-ähnliche Funktionen nicht unbedingt eine Aktion aus, die zur Heap-Erweiterung führt. C ++ hat eine völlig neue Art der Speicherzuweisung und jeder weiß, dass die Verwendung der vordefinierten Funktionen von C keine gute Idee ist.
Imobilis
Nur zu wissen, dass Ihnen ein Zeiger übergeben wurde, der irgendwo in den Stapel zeigt, ist nicht sinnvoll. Sie müssen noch das Problem von OP lösen, nämlich zu wissen, wie groß der Puffer ist, auf den gezeigt wird.
Melpomene