我尝试阅读了在SO上与标题类似的其他问题,但它们都稍微有点太复杂,以至于我无法将解决方案(甚至是解释)应用到自己的问题上,而我的问题似乎更简单。
在我的情况下,我有一个包装器围绕free()
,在释放后将指针设置为NULL
:
void myfree(void **ptr)
{
free(*ptr);
*ptr = NULL;
}
在我正在工作的项目中,它被称为这样:
myfree((void **)&a);
这会使得
gcc
(在OpenBSD上的4.2.1版本)如果我将优化级别提高到-O3
并添加-Wall
(否则不会)时,发出警告"dereferencing type-punned pointer will break strict-aliasing rules"。调用以下方式的
myfree()
不会使编译器发出该警告:myfree((void *)&a);
因此,我在想我们是否应该改变调用myfree()
的方式。
我认为第一种调用myfree()
的方式存在未定义行为,但我还无法理解为什么会这样。此外,在我可以访问的所有编译器(clang
和gcc
)和系统(OpenBSD、Mac OS X和Linux)上,这是唯一一个实际给出该警告的编译器和系统(我知道发出警告是一个很好的选择)。
使用两种调用方式之前、之中和之后打印指针的值,结果相同(但如果存在未定义行为,这可能意味着什么):
#include <stdio.h>
#include <stdlib.h>
void myfree(void **ptr)
{
printf("(in myfree) ptr = %p\n", *ptr);
free(*ptr);
*ptr = NULL;
}
int main(void)
{
int *a, *b;
a = malloc(100 * sizeof *a);
b = malloc(100 * sizeof *b);
printf("(before myfree) a = %p\n", (void *)a);
printf("(before myfree) b = %p\n", (void *)b);
myfree((void **)&a); /* line 21 */
myfree((void *)&b);
printf("(after myfree) a = %p\n", (void *)a);
printf("(after myfree) b = %p\n", (void *)b);
return EXIT_SUCCESS;
}
编译和运行它:
$ cc -O3 -Wall free-test.c
free-test.c: In function 'main':
free-test.c:21: warning: dereferencing type-punned pointer will break strict-aliasing rules
$ ./a.out
(before myfree) a = 0x15f8fcf1d600
(before myfree) b = 0x15f876b27200
(in myfree) ptr = 0x15f8fcf1d600
(in myfree) ptr = 0x15f876b27200
(after myfree) a = 0x0
(after myfree) b = 0x0
我希望了解第一个对myfree()
的调用有何问题,并想知道第二个调用是否正确。谢谢。
free(NULL)
是无害的,而解引用NULL
则不是。因此我认为,(这是一个运行了多年的协作项目)被告知引用已释放内存的情况比捕获双重free()
调用更好。我一直在考虑使用 Boehm GC 进行内存泄漏检测和/或 Valgrind,但由于我在项目中还比较新,所以还没有开始做这些事情。 - Kusalananda