Ira*_*d K 32 c variables macros
不幸的是,在C中没有任何智能指针..但是有可能构建一个宏来包装变量声明并在离开声明变量的范围时使用该变量作为输入变量调用函数调用吗?
很抱歉这个长短语,但我正在使用xnu内核,你有许多内置引用计数器的元素,并且一定不要忘记在使用它时不用这个元素来避免内存泄漏.
例如,如果我有以下类型proc_t:
struct proc;
typedef struct proc * proc_t;
Run Code Online (Sandbox Code Playgroud)
我想在范围内基于此类型声明堆栈变量,例如:
{
proc_t_release_upon_exit proc_t proc_iter = proc_find(mypid);
//the rest of the code in this scope
}
Run Code Online (Sandbox Code Playgroud)
在预处理器分析宏之后和编译之前,我希望生成以下代码:
{
proc_t myproc = proc_find(mypid)
//the rest of the code in scope
proc_rele(myproc);
}
Run Code Online (Sandbox Code Playgroud)
有没有办法在C中定义这样的宏?
小智 39
您可以在GCC中使用清理变量属性.请看一下:http: //echorand.me/site/notes/articles/c_cleanup/cleanup_attribute_c.html
示例代码:
#include <stdio.h>
#include <stdlib.h>
void free_memory(void **ptr)
{
printf("Free memory: %p\n", *ptr);
free(*ptr);
}
int main(void)
{
// Define variable and allocate 1 byte, the memory will be free at
// the end of the scope by the free_memory function. The free_memory
// function will get the pointer to the variable *ptr (double pointer
// **ptr).
void *ptr __attribute__ ((__cleanup__(free_memory))) = malloc(1);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
如果将源代码保存在名为main.c的文件中,则可以使用以下命令对其进行编译:
gcc main.c -o main
Run Code Online (Sandbox Code Playgroud)
并通过以下方式验证是否存在任何内存泄漏:
valgrind ./main
Run Code Online (Sandbox Code Playgroud)
valgrind的输出示例:
==1026== Memcheck, a memory error detector
==1026== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==1026== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==1026== Command: ./main
==1026==
Free memory: 0x51ff040
==1026==
==1026== HEAP SUMMARY:
==1026== in use at exit: 0 bytes in 0 blocks
==1026== total heap usage: 1 allocs, 1 frees, 1 bytes allocated
==1026==
==1026== All heap blocks were freed -- no leaks are possible
==1026==
==1026== For counts of detected and suppressed errors, rerun with: -v
==1026== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Run Code Online (Sandbox Code Playgroud)
Leu*_*nko 18
C确实提供了一种在语法上将代码放在首先执行的其他代码之前的方法:for块.请记住,for结构的第3节可以包含任意表达式,并且总是在执行主块之后运行.
因此,您可以通过在宏中包装块来创建一个宏,该宏在给定的以下代码块之后进行预定调用for:
#define M_GEN_DONE_FLAG() _done_ ## __LINE__
#define M_AROUND_BLOCK2(FLAG, DECL, BEFORE, AFTER) \
for (int FLAG = (BEFORE, 0); !FLAG; ) \
for (DECL; !FLAG; FLAG = (AFTER, 1))
#define M_AROUND_BLOCK(DECL, BEFORE, AFTER) M_AROUND_BLOCK2(M_GEN_DONE_FLAG(), DECL, BEFORE, AFTER)
#define M_CLEANUP_VAR(DECL, CLEANUP_CALL) M_AROUND_BLOCK(DECL, (void)0, CLEANUP_CALL)
Run Code Online (Sandbox Code Playgroud)
......你可以像这样使用它:
#include <stdio.h>
struct proc;
typedef struct proc * proc_t;
proc_t proc_find(int);
void proc_rele(proc_t);
void fun(int mypid) {
M_CLEANUP_VAR (proc_t myproc = proc_find(mypid), proc_rele(myproc))
{
printf("%p\n", &myproc); // just to prove it's in scope
}
}
Run Code Online (Sandbox Code Playgroud)
这里的技巧是for块接受以下语句,但是如果我们实际上没有将该语句放在宏定义中,我们可以使用普通代码块跟随宏调用,它将"神奇地"属于我们的新范围 -控制结构语法,只需通过以下扩展for.
任何值得使用的优化器都会在最低优化设置下删除循环标志.请注意,与标志冲突的名称不是一个大问题(即你真的不需要gensym这个),因为标志的范围是循环体,如果它们使用相同的标志名称,任何嵌套循环都将安全地隐藏它.
这里的好处是清理变量的范围是有限的(它不能在声明之后立即在化合物之外使用)和视觉上明确(因为所述化合物).
优点:
__attribute__ __cleanup__缺点:
goto或C++异常:__cleanup__通常使用C++机制实现,因此它更完整).更严重的是,它不能提前防范return(感谢@Voo).(你至少可以防止错位break- 如果你想 - 通过添加第三行,switch (0) default:到最后M_AROUND_BLOCK2.)