智能指针/ C安全内存pipe理?

我和其他许多人使用智能指针在C ++中使用诸如RAII等这样的东西来包装不安全的内存操作,取得了巨大的成功。 但是,如果有析构函数,类,操作符重载等,则包装内存pipe理更容易实现。

对于使用原始C99编写的程序,您可以在哪里指出(无意双关)来帮助安全的内存pipe理?

谢谢。

在原始C中处理智能指针是很困难的,因为你没有语言语法来备份使用。 我所见过的大多数尝试都不行,因为当对象离开作用域时,你不具有运行析构函数的优点,这实际上是智能指针的工作原理。

如果你真的担心这个,你可能要考虑直接使用垃圾收集器 ,并完全绕过智能指针的要求。

这个问题有点老了,但我想我会花时间链接到我的GNU编译器(GCC,Clang,ICC,MinGW,…)的智能指针库 。

这个实现依赖于清理variables属性(一个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) 

另一种可能需要考虑的方法是Apache使用的池式内存方法。 如果您的dynamic内存使用情况与请求或其他短期对象相关联,则此方法非常有效。 您可以在请求结构中创build一个池,并确保始终从池中分配内存,然后在完成请求时释放池。 一旦你稍微使用它,它听起来不像它那么强大。 它几乎和RAII一样好。

你不能在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); } 

这类似于对象的构造函数和析构函数。 只要你不分配给其他对象的资源,它不会泄漏,指针也不会悬垂。

编写一个跟踪分配和写入泄漏块的自定义分配器并不困难。

如果您需要放弃指向已分配资源的指针,则可以为其创build包装上下文,并且每个对象都拥有包装上下文而不是资源。 这些包装共享资源和一个计数器对象,跟踪使用情况,并在没有人使用时释放对象。 这就是C ++ 11的shared_ptrweak_ptr工作原理。 这里写的更详细: weak_ptr是如何工作的?

像藤条或Gimpel PC-Lint这样的静态代码分析工具在这里可能会有所帮助,甚至可以通过将它们连接到自动的“持续集成”风格的构build服务器来使这些适度的“预防性”。 (你有其中一个,对吧?:咧嘴笑:)

还有其他的(一些更昂贵的)这个主题的变种…

如果你在Win32中编码,你可以使用结构化exception处理来完成类似的事情。 你可以做这样的事情:

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

虽然不像RAII那么简单,但是您可以将所有的清理代码收集在一个地方,并保证它被执行。

您可以定义macros(例如BEGIN和END)来替代大括号,并触发自动销毁正在退出其范围的资源。 这要求所有这些资源都由智能指针指向,这些智能指针还包含指向对象析构函数的指针。 在我的实现中,我在堆内存中保存了一堆智能指针,在进入范围时记住堆栈指针,并在范围出口(END或macrosreplace返回)调用记忆堆栈指针上方所有资源的析构函数。 即使使用了setjmp / longjmpexception机制,这也可以很好地工作,并清除catch-block和引发exception的作用域之间的所有中间作用域。 请参阅https://github.com/psevon/exceptions-and-raii-in-c.git获取实现。;

 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 ! */