2009-04-28 3 views
34

Я, и многие другие, имел большой успех, используя интеллектуальные указатели, чтобы обернуть небезопасные операции с памятью на C++, используя такие вещи, как RAII и т. Д. Однако управление оболочкой памяти проще реализовать, когда у вас есть деструкторы, классы, перегрузка операторов и т. Д.Умные указатели/безопасное управление памятью для C?

Для кого-то, пишущего на необработанном C99, где вы могли бы указать (не каламбур), чтобы помочь с безопасным управлением памятью?

Спасибо.

ответ

12

Сложно обрабатывать интеллектуальные указатели в raw C, так как у вас нет синтаксиса языка для резервного копирования. Большинство попыток, которые я видел, на самом деле не работают, поскольку у вас нет преимуществ деструкторов, выполняющихся, когда объекты покидают область действия, что действительно делает умные указатели.

Если вы действительно беспокоитесь об этом, вам может потребоваться прямое использование garbage collector и обход требования интеллектуального указателя.

+0

Как это работает? Как он может отслеживать назначения указателя? – Calmarius

+0

@Calmarius Существуют различные способы их работы. См .: http://en.wikipedia.org/wiki/Garbage_collection_(computer_science) –

+0

Я вижу. Я спросил о GC, с которой вы связались. Он утверждает, что он работает с немодифицированными программами C, заменяя только malloc и realloc. Но как он находит указатели, указывающие на выделенный блок? Они могут быть скопированы в программе. – Calmarius

3

Инструменты анализа статического кода, такие как splint или Gimpel PC-Lint может помочь здесь - вы можете даже сделать эти умеренно «превентивные», подключив их к вашему автоматическому серверу построения «непрерывной интеграции». (У вас есть один из тех, верно? Оскал :)

Есть другие (некоторые более дорогие) варианты на эту тему тоже ...

+0

+1 Хороший вызов статическим средствам проверки кода. –

2

Если вы кодирования в Win32, вы можете быть в состоянии использовать structured exception handling выполнить нечто подобное. Вы могли бы сделать что-то вроде этого:

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

Хотя не совсем так просто, как RAII, вы можете собрать весь код очистки в одном месте и гарантировать, что она будет выполнена.

8

Другим подходом, который вы можете рассмотреть, является подход к объединенной памяти, который Apache uses. Это работает исключительно хорошо, если у вас динамическое использование памяти, связанное с запросом или другим недолговечным объектом. Вы можете создать пул в структуре запроса и убедиться, что вы всегда выделяете память из пула, а затем освобождаете пул, когда вы закончите обработку запроса. Это звучит не так сильно, как только вы его немного использовали. Это почти так же хорошо, как RAII.

4

Вы не можете делать интеллектуальные указатели в C, потому что он не обеспечивает необходимый синтаксис, но вы можете избежать утечек с практикой. Напишите код выпуска ресурса сразу после его выделения. Поэтому всякий раз, когда вы пишете malloc, вы должны написать соответствующий free непосредственно в разделе очистки.

В CI увидеть «GOTO очистки» модель много:

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

В C мы также используем много контекстов, которые выделяют вещество, такое же правило может быть применено для этого тоже:

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

Это аналогично конструкторам объектов и деструкторам. Пока вы не отдаете выделенные ресурсы другим объектам, они не будут течь, а указатели не будут болтаться.

Нетрудно написать пользовательский распределитель, который отслеживает распределения и записывает утечки блоков atexit.

Если вам нужно указать указатели на выделенные ресурсы, вы можете создать для него контексты-оболочки, и каждый объект будет обладать контекстом-оболочкой вместо ресурса. Эти оболочки обмениваются ресурсом и объектом-счетчиком, который отслеживает использование и освобождает объекты, когда никто его не использует. Вот как работает C++ 11's shared_ptr и weak_ptr. Здесь написано более подробно: How does weak_ptr work?

2

Вы можете определить макросы, например BEGIN и END, которые будут использоваться вместо брекетов и инициировать автоматическое уничтожение ресурсов, выходящих за пределы их объема. Это требует, чтобы на все такие ресурсы указывались интеллектуальные указатели, которые также содержат указатель на деструктор объекта. В моей реализации я сохраняю стек интеллектуальных указателей в кучевой памяти, запоминаю указатель стека при входе в область действия и вызываю деструкторы всех ресурсов над запомненным указателем стека при выходе области (END или замена макроса для возврата). Это хорошо работает, даже если используется механизм исключения setjmp/longjmp и очищает все промежуточные области между блоком catch и областью, в которой было выбрано исключение. См. https://github.com/psevon/exceptions-and-raii-in-c.git для реализации.

12

Вопрос немного устарел, но я решил, что потрачу время на ссылку на мой smart pointer library для компиляторов GNU (GCC, Clang, ICC, MinGW, ...).

Эта реализация основана на атрибуте переменной очистки, расширении GNU, чтобы автоматически освобождать память при выходе из области видимости и, как таковой, является не ISO C99, но C99 с расширениями GNU.

Пример:

simple1.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; 
} 

Компиляция & Valgrind сессия:

$ 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) 
0
Sometimes i use this approach and it seems good :) 

Object *construct(type arg, ...){ 

    Object *__local = malloc(sizeof(Object)); 
    if(!__local) 
     return NULL; 
    __local->prop_a = arg; 
    /* blah blah */ 


} // constructor 

void destruct(Object *__this){ 

    if(__this->prop_a)free(this->prop_a); 
    if(__this->prop_b)free(this->prop_b); 

} // destructor 

Object *o = __construct(200); 
if(o != NULL) 
    ;; 

// use 

destruct(o); 

/* 
    done ! 
*/ 
Смежные вопросы