C 的智能指针/安全内存管理?
我,以及我认为许多其他人,使用智能指针来包装 C++ 中的不安全内存操作(使用 RAII 等)取得了巨大成功。 然而,当您有析构函数、类、运算符重载等时,包装内存管理更容易实现。
对于用原始 C99 编写的人,您可以指出哪里(没有双关语)来帮助安全内存管理?
谢谢。
I, and I think many others, have had great success using smart pointers to wrap up unsafe memory operations in C++, using things like RAII, et cetera. However, wrapping memory management is easier to implement when you have destructors, classes, operator overloading, et cetera.
For someone writing in raw C99, where could you point (no pun intended) to help with safe memory management?
Thanks.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
这个问题有点老了,但我想我会花时间链接到我的 智能指针库 适用于 GNU 编译器(GCC、Clang、ICC、MinGW,...)。
此实现依赖于清理变量属性(GNU 扩展),在超出范围时自动释放内存,因此,不是 ISO C99,而是具有 GNU 扩展的 C99。
示例:
simple1.c:
编译& Valgrind 会议:
The question is a bit old, but I figured I would take the time to link to my smart pointer library for GNU compilers (GCC, Clang, ICC, MinGW, ...).
This implementation relies on the cleanup variable attribute, a GNU extension, to automatically free the memory when going out of scope, and as such, is not ISO C99, but C99 with GNU extensions.
Example:
simple1.c:
Compilation & Valgrind session:
在原始 C 中处理智能指针很困难,因为您没有语言语法来支持用法。 我见过的大多数尝试都不起作用,因为当对象离开作用域时,您没有运行析构函数的优势,而这正是智能指针发挥作用的真正原因。
如果您真的担心这一点,您可能需要考虑直接使用 垃圾收集器,并完全绕过智能指针要求。
It's difficult to handle smart pointers in raw C, since you don't have the language syntax to back up the usage. Most of the attempts I've seen don't really work, since you don't have the advantages of destructors running when objects leave scope, which is really what makes smart pointers work.
If you're really worried about this, you might want to consider just directly using a garbage collector, and bypassing the smart pointer requirement altogether.
您可能需要考虑的另一种方法是 Apache 使用的池化内存方法。 如果您具有与请求或其他短期对象关联的动态内存使用情况,则此方法非常有效。 您可以在请求结构中创建一个池,并确保始终从池中分配内存,然后在处理完请求后释放池。 一旦你使用过它,它听起来就不再那么强大了。 它几乎和 RAII 一样好。
Another approach that you might want to consider is the pooled memory approach that Apache uses. This works exceptionally well if you have dynamic memory usage that is associated with a request or other short-lived object. You can create a pool in your request structure and make sure that you always allocate memory from the pool and then free the pool when you are done processing the request. It doesn't sound nearly as powerful as it is once you have used it a little. It is almost as nice as RAII.
你不能在 C 中使用智能指针,因为它没有提供必要的语法,但你可以通过练习避免泄漏。 分配完资源后立即编写资源释放代码。 因此,每当您编写
malloc
时,您应该立即在清理部分中编写相应的free
。在 CI 中,我们经常看到“GOTO 清理”模式:
在 C 中,我们还使用很多分配内容的上下文,同样的规则也适用于此:
这类似于对象构造函数和析构函数。 只要您不将分配的资源交给其他对象,它就不会泄漏,指针也不会悬空。
编写一个跟踪分配并写入泄漏块
atexit
的自定义分配器并不困难。如果您需要放弃指向已分配资源的指针,您可以为其创建包装器上下文,并且每个对象都拥有一个包装器上下文而不是资源。 这些包装器共享资源和一个计数器对象,该对象跟踪使用情况并在无人使用时释放对象。 这就是 C++11 的
shared_ptr
和weak_ptr
的工作原理。 这里写得更详细:weak_ptr 是如何工作的?You cannot do smart pointers in C because it does not provide necessary syntax, but you can avoid leaks with practice. Write the resource release code immediately after you allocated it. So whenever you write a
malloc
, you should write the correspondingfree
immediately in a cleanup section.In C I see the 'GOTO cleanup' pattern a lot:
In C we also use a lot of contexts which allocate stuff, the same rule can be applied for that too:
This is analogous to the object constructors and destructors. As long as you don't give away the allocated resources to other objects it won't leak and pointers won't dangle.
It's not difficult to write a custom allocator that tracks allocations and writes leaking blocks
atexit
.If you need to give away pointers to the allocated resources you can create wrapper contexts for it and each object owns a wrapper context instead of the resource. These wrappers share the resource and a counter object, which tracks the usage and frees the objects when no one uses it. This is how C++11's
shared_ptr
andweak_ptr
works. It's written in more detail here: How does weak_ptr work?静态代码分析工具,例如 splint 或 Gimpel PC-Lint 可能会在这里提供帮助 - 您甚至可以通过将它们连接到自动“持续集成”样式构建服务器来使它们具有适度的“预防性”。 (你确实有其中之一,对吧?:grin:)
这个主题还有其他(一些更昂贵的)变体......
Static code analysis tools like splint or Gimpel PC-Lint may help here -- you can even make these moderately "preventative" by wiring them into your automatic "continuous-integration" style build server. (You do have one of those, right? :grin:)
There are other (some more expensive) variants on this theme too...
好的,这是您的选择。 理想情况下,您将它们结合起来以获得更好的结果。 如果是C,偏执是可以的。
编译时:
运行时:
Ok, so here are your options. Ideally, you combine them to get better result. In case of C, paranoia is fine.
Compile-time:
Runtime:
您可以定义宏(例如 BEGIN 和 END)来代替大括号并触发自动销毁正在退出其范围的资源。 这要求所有此类资源都由智能指针指向,该指针还包含指向对象析构函数的指针。 在我的实现中,我在堆内存中保留了一个智能指针堆栈,记住作用域入口处的堆栈指针,并在作用域出口处调用记忆的堆栈指针上方的所有资源的析构函数(END或用于返回的宏替换)。 即使使用 setjmp/longjmp 异常机制,这也能很好地工作,并且还清除 catch 块和引发异常的范围之间的所有中间范围。 请参阅 https://github.com/psevon/exceptions-and-raii- in-c.git 进行实施。
You can define macros, for example BEGIN and END, to be used in place of braces and trigger automatic destruction of resources that are exiting their scope. This requires that all such resources are pointed to by smart pointers that also contain pointer to the destructor of the object. In my implementation I keep a stack of smart pointers in heap memory, memorize the stack pointer at entry to a scope, and call destructors of all resources above the memorized stack pointer at scope exit (END or macro replacement for return). This works nicely even if setjmp/longjmp exception mechanism is used, and cleans up all the intermediate scopes between the catch-block and the scope where the exception was thrown, too. See https://github.com/psevon/exceptions-and-raii-in-c.git for the implementation.
如果您使用 Win32 进行编码,则可以使用 结构化异常处理来完成类似的事情。 您可以这样做:
虽然不像 RAII 那么容易,但您可以将所有清理代码收集在一个地方并保证它被执行。
If you are coding in Win32 you might be able to use structured exception handling to accomplish something similar. You could do something like this:
While not quite as easy as RAII, you can collect all of your cleanup code in one place and guarantee that it is executed.