Intelligente Zeiger/sichere Speicherverwaltung für C?

Lesezeit: 5 Minuten

Benutzeravatar von Mannicken
mannicken

Ich, und ich denke, viele andere hatten großen Erfolg mit intelligenten Zeigern, um unsichere Speicheroperationen in C++ zu verpacken, indem sie Dinge wie RAII und so weiter verwendeten. Das Wrapping der Speicherverwaltung ist jedoch einfacher zu implementieren, wenn Sie Destruktoren, Klassen, Operatorüberladungen usw. haben.

Wo könnten Sie für jemanden, der in rohem C99 schreibt, zeigen (kein Wortspiel beabsichtigt), um bei der sicheren Speicherverwaltung zu helfen?

Vielen Dank.

  • Wo könntest du hinzeigen. Mann, ich wünschte, das Wortspiel wäre beabsichtigt.

    – Balázs Börcsök

    5. Juni um 20:12 Uhr

Benutzeravatar von Snaipe
Schnaipe

Die Frage ist ein bisschen alt, aber ich dachte, ich würde mir die Zeit nehmen, auf meine zu verlinken Smart-Pointer-Bibliothek für GNU-Compiler (GCC, Clang, ICC, MinGW, …).

Diese Implementierung stützt sich auf das cleanup-Variablenattribut, eine GNU-Erweiterung, um den Speicher automatisch freizugeben, wenn der Gültigkeitsbereich überschritten wird, und ist es daher auch nicht ISO C99, aber C99 mit GNU-Erweiterungen.

Beispiel:

einfach1.c:

#include <stdio.h>
#include <csptr/smart_ptr.h>

int main(void) {
    smart int *some_int = unique_ptr(int, 1);

    printf("%p = %d\n", some_int, *some_int);

    // some_int is destroyed here
    return 0;
}

Zusammenstellung & Valgrind-Session:

$ gcc -std=gnu99 -o simple1 simple1.c -lcsptr
$ valgrind ./simple1
==3407== Memcheck, a memory error detector
==3407== Copyright (C) 2002-2013, and GNU GPL\'d, by Julian Seward et al.
==3407== Using Valgrind-3.10.0 and LibVEX; rerun with -h for copyright info
==3407== Command: ./simple1 
==3407==
0x53db068 = 1
==3407==
==3407== HEAP SUMMARY:
==3407==     in use at exit: 0 bytes in 0 blocks
==3407==   total heap usage: 1 allocs, 1 frees, 48 bytes allocated
==3407==
==3407== All heap blocks were freed -- no leaks are possible
==3407==
==3407== For counts of detected and suppressed errors, rerun with: -v
==3407== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

Benutzeravatar von Reed Copsey
Reed Copsey

Es ist schwierig, intelligente Zeiger in rohem C zu handhaben, da Sie nicht über die Sprachsyntax verfügen, um die Verwendung zu sichern. Die meisten Versuche, die ich gesehen habe, funktionieren nicht wirklich, da Sie nicht die Vorteile von Destruktoren haben, die ausgeführt werden, wenn Objekte den Gültigkeitsbereich verlassen, was wirklich dazu führt, dass intelligente Zeiger funktionieren.

Wenn Sie sich darüber wirklich Sorgen machen, sollten Sie erwägen, einfach direkt a zu verwenden Müllsammlerund Umgehung der Smart-Pointer-Anforderung insgesamt.

  • @Calmarius Es gibt verschiedene Möglichkeiten, wie sie funktionieren. Sehen: en.wikipedia.org/wiki/Garbage_collection_(Informatik)

    – Reed Copsey

    11. August 2012 um 18:55 Uhr

  • Aha. Ich habe nach dem verlinkten GC gefragt. Es behauptet, dass es mit unmodifizierten C-Programmen funktioniert, indem es nur malloc und realloc ersetzt. Aber wie findet es die Zeiger, die auf den zugewiesenen Block zeigen? Sie können im Programm herumkopiert werden.

    – Kalmarius

    11. August 2012 um 22:47 Uhr


  • Es scheint, dass es die zugewiesenen Blöcke durchsucht und versucht, alle ganzzahligen Werte als Zeiger zu interpretieren. Dann kann die Effizienz für große Blöcke schrecklich sein.

    – Kalmarius

    12. August 2012 um 7:03 Uhr

  • Garbage Collector-Link ist defekt.

    – Robert Harvey

    24. Juni 2016 um 16:18 Uhr

Ein weiterer Ansatz, den Sie vielleicht in Betracht ziehen sollten, ist der Pooled-Memory-Ansatz, der Apache verwendet. Dies funktioniert besonders gut, wenn Sie eine dynamische Speichernutzung haben, die einer Anfrage oder einem anderen kurzlebigen Objekt zugeordnet ist. Sie können einen Pool in Ihrer Anforderungsstruktur erstellen und sicherstellen, dass Sie immer Speicher aus dem Pool zuweisen und den Pool dann freigeben, wenn Sie mit der Verarbeitung der Anforderung fertig sind. Es klingt nicht annähernd so kraftvoll, wie es ist, wenn Sie es ein wenig benutzt haben. Es ist fast so schön wie RAII.

Benutzeravatar von Calmarius
Calmarius

Sie können keine intelligenten Zeiger in C ausführen, da es die erforderliche Syntax nicht bereitstellt, aber Sie können Lecks mit Übung vermeiden. Schreiben Sie den Freigabecode der Ressource unmittelbar nach der Zuweisung. Also immer wenn du a schreibst mallocsollten Sie das entsprechende schreiben free sofort in einem Reinigungsabschnitt.

In CI sehen Sie häufig das Muster ‘GOTO cleanup’:

int foo()
{
    int *resource = malloc(1000);
    int retVal = 0;
    //...
    if (time_to_exit())
    {
        retVal = 123;
        goto cleanup;
    }
cleanup:
    free(resource);
    return retVal;
}

In C verwenden wir auch viele Kontexte, die Dinge zuweisen, die gleiche Regel kann auch dafür angewendet werden:

int initializeStuff(Stuff *stuff)
{
    stuff->resource = malloc(sizeof(Resource));
    if (!stuff->resource) 
    {
        return -1; ///< Fail.
    }
    return 0; ///< Success.
}

void cleanupStuff(Stuff *stuff)
{
    free(stuff->resource);
}

Dies ist analog zu den Objektkonstruktoren und -destruktoren. Solange Sie die zugewiesenen Ressourcen nicht an andere Objekte verschenken, wird es nicht lecken und Zeiger werden nicht baumeln.

Es ist nicht schwierig, einen benutzerdefinierten Zuordner zu schreiben, der Zuweisungen verfolgt und undichte Blöcke schreibt atexit.

Wenn Sie Zeiger auf die zugewiesenen Ressourcen weitergeben müssen, können Sie Wrapper-Kontexte dafür erstellen, und jedes Objekt besitzt einen Wrapper-Kontext anstelle der Ressource. Diese Wrapper teilen sich die Ressource und ein Zählerobjekt, das die Nutzung verfolgt und die Objekte freigibt, wenn niemand sie verwendet. So funktioniert C++11 shared_ptr und weak_ptr funktioniert. Hier ist es ausführlicher beschrieben: Wie funktioniert schwacher_ptr?

Statische Codeanalyse-Tools wie Schiene oder Gimpel PC-Lint kann hier helfen – Sie können diese sogar mäßig „präventiv“ machen, indem Sie sie in Ihren automatischen „Continuous-Integration“-Build-Server einbinden. (Du hast doch so einen, oder? :grins:)

Es gibt auch andere (teilweise teurere) Varianten zu diesem Thema …

Benutzeravatar von user3510229
Benutzer3510229

Sie können Makros definieren, z. B. BEGIN und END, die anstelle von geschweiften Klammern verwendet werden und die automatische Zerstörung von Ressourcen auslösen, die ihren Gültigkeitsbereich verlassen. Dies erfordert, dass auf alle diese Ressourcen durch intelligente Zeiger verwiesen wird, die auch einen Zeiger auf den Destruktor des Objekts enthalten. In meiner Implementierung halte ich einen Stapel intelligenter Zeiger im Heap-Speicher, speichere den Stapelzeiger beim Eintritt in einen Gültigkeitsbereich und rufe Destruktoren aller Ressourcen über dem gespeicherten Stapelzeiger beim Verlassen des Gültigkeitsbereichs auf (END oder Makroersetzung für die Rückkehr). Dies funktioniert gut, auch wenn der Ausnahmemechanismus setjmp/longjmp verwendet wird, und bereinigt auch alle Zwischenbereiche zwischen dem Catch-Block und dem Bereich, in dem die Ausnahme ausgelöst wurde. Sehen https://github.com/psevon/exceptions-and-raii-in-c.git für die Umsetzung.

Benutzeravatar von Steve Rowe
Steve Rowe

Wenn Sie in Win32 codieren, können Sie möglicherweise verwenden strukturierte Ausnahmebehandlung um etwas ähnliches zu erreichen. Du könntest so etwas machen:

foo() {
    myType pFoo = 0;
    __try
    {
        pFoo = malloc(sizeof myType);
        // do some stuff
    }
    __finally
    {
        free pFoo;
    }
}

Obwohl es nicht ganz so einfach ist wie RAII, können Sie Ihren gesamten Bereinigungscode an einem Ort sammeln und garantieren, dass er ausgeführt wird.

1411360cookie-checkIntelligente Zeiger/sichere Speicherverwaltung für C?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy