将 char** 传递给 void** 函数参数时,指针到指针类型的不兼容指针类型警告
Incompatible pointer type warning with pointer-to-pointer types when passing char** to void** function parameter
我正在尝试实现一个安全释放函数,该函数擦除分配的内存,释放它,然后还将指向分配区域的指针设置为 NULL,这样指针在释放后不能被重用,也不能被双重使用使用相同的功能释放。为此,我使用了一个指向指针的指针参数,它允许我覆盖指向已分配内存的指针。
问题是 GCC 抱怨不兼容的指针类型(“但它在我的机器上工作”);我没想到会有这样的警告。我的理解是任何指针都可以隐式转换为 void*
,因此我猜测指针的 地址 也可以转换为 void**
.
同时我将secure_free()
重写为宏,解决了警告,但我想知道为什么编译器会报错。
文件securefree.c
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#define STRING_BUFFER_LEN 10
/**
* Securely erases a heap-allocated memory section, frees it and sets its
* pointer to NULL to avoid use-after-free and double-free.
*/
static void secure_free(void** p_p_data, size_t length_in_bytes)
{
if (p_p_data == NULL || *p_p_data == NULL)
{ return; }
memset(*p_p_data, 0, length_in_bytes);
free(*p_p_data);
*p_p_data = NULL;
}
int main(void)
{
// Allocate some data
char* my_string = calloc(STRING_BUFFER_LEN, sizeof(char));
if (my_string == NULL) { return 1; }
// Use the allocated space in some way
my_string[0] = 'a';
my_string[1] = 'b';
// Free using the dedicated function
secure_free(&my_string, STRING_BUFFER_LEN);
return 0;
}
使用 GCC 编译(Rev6,由 MSYS2 项目构建,10.2.0):
$ gcc securefree.c -o securefree
securefree.c: In function 'main':
securefree.c:29:17: warning: passing argument 1 of 'secure_free' from incompatible pointer type [-Wincompatible-pointer-types]
29 | secure_free(&my_string, STRING_BUFFER_LEN);
| ^~~~~~~~~~
| |
| char **
securefree.c:11:32: note: expected 'void **' but argument is of type 'char **'
11 | static void secure_free(void** p_p_data, size_t length_in_bytes)
| ~~~~~~~^~~~~~~~
编辑:宏版本看起来像这样
#define secure_free_macro(ptr, len) if ((ptr) != NULL) { \
memset((ptr), 0, (len)); free(ptr); (ptr) = NULL; }
C 允许任何指针 隐式 转换为 void*
作为显式异常。请注意,void
和 char
是 不 兼容的类型。因此 void*
、char*
、void**
和 char**
也不兼容。这就是编译器发出警告的原因。
要绕过此问题,请更改函数签名以使用 void*
:
void secure_free(void* ptr, size_t length_in_bytes) {
void **p_p_data = (void**)ptr;
...
}
要增加参数是指向指针的指针的保护,可以使用宏:
#define secure_free(x,s) ((void)sizeof **(x), secure_free((x), (s)))
- 表达式
**(x)
不会编译是 x 不是指向指针的指针。
sizeof
防止在 **(x)
中计算 x
以避免副作用
(void)
让编译器停止抱怨未使用的值
- 逗号运算符
(X,Y)
,return只有Y
的值,也就是returnsecure_free(...)
的值
- 对宏使用与函数相同的名称允许将
secure_free
扩展为仅当它用作函数时的宏。这允许使用 secure_free
作为指向函数的指针
补充说明。在代码中
memset(*p_p_data, 0, length_in_bytes);
free(*p_p_data);
编译器可能会优化 memset()
。我建议强制转换为 volatile void *
以强制编译器生成清除代码。
编辑
此外,由于 memset
丢弃了 volatile
限定符,可能会用循环清除内容。
您尝试做的事情无法移植,因为不同的指针类型可以有不同的表示;并且要将空指针分配给该值,您必须首先将指向指针的指针 转换为指向实际的 有效类型 的指针指针变量 - 这是不可能的。
然而你可以做的是使用宏,它和任何宏一样好,而且使用起来更简单:
#define secure_free(x) (free(x), (x) = 0)
这在没有 &
的情况下有效。
我正在尝试实现一个安全释放函数,该函数擦除分配的内存,释放它,然后还将指向分配区域的指针设置为 NULL,这样指针在释放后不能被重用,也不能被双重使用使用相同的功能释放。为此,我使用了一个指向指针的指针参数,它允许我覆盖指向已分配内存的指针。
问题是 GCC 抱怨不兼容的指针类型(“但它在我的机器上工作”);我没想到会有这样的警告。我的理解是任何指针都可以隐式转换为 void*
,因此我猜测指针的 地址 也可以转换为 void**
.
同时我将secure_free()
重写为宏,解决了警告,但我想知道为什么编译器会报错。
文件securefree.c
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#define STRING_BUFFER_LEN 10
/**
* Securely erases a heap-allocated memory section, frees it and sets its
* pointer to NULL to avoid use-after-free and double-free.
*/
static void secure_free(void** p_p_data, size_t length_in_bytes)
{
if (p_p_data == NULL || *p_p_data == NULL)
{ return; }
memset(*p_p_data, 0, length_in_bytes);
free(*p_p_data);
*p_p_data = NULL;
}
int main(void)
{
// Allocate some data
char* my_string = calloc(STRING_BUFFER_LEN, sizeof(char));
if (my_string == NULL) { return 1; }
// Use the allocated space in some way
my_string[0] = 'a';
my_string[1] = 'b';
// Free using the dedicated function
secure_free(&my_string, STRING_BUFFER_LEN);
return 0;
}
使用 GCC 编译(Rev6,由 MSYS2 项目构建,10.2.0):
$ gcc securefree.c -o securefree
securefree.c: In function 'main':
securefree.c:29:17: warning: passing argument 1 of 'secure_free' from incompatible pointer type [-Wincompatible-pointer-types]
29 | secure_free(&my_string, STRING_BUFFER_LEN);
| ^~~~~~~~~~
| |
| char **
securefree.c:11:32: note: expected 'void **' but argument is of type 'char **'
11 | static void secure_free(void** p_p_data, size_t length_in_bytes)
| ~~~~~~~^~~~~~~~
编辑:宏版本看起来像这样
#define secure_free_macro(ptr, len) if ((ptr) != NULL) { \
memset((ptr), 0, (len)); free(ptr); (ptr) = NULL; }
C 允许任何指针 隐式 转换为 void*
作为显式异常。请注意,void
和 char
是 不 兼容的类型。因此 void*
、char*
、void**
和 char**
也不兼容。这就是编译器发出警告的原因。
要绕过此问题,请更改函数签名以使用 void*
:
void secure_free(void* ptr, size_t length_in_bytes) {
void **p_p_data = (void**)ptr;
...
}
要增加参数是指向指针的指针的保护,可以使用宏:
#define secure_free(x,s) ((void)sizeof **(x), secure_free((x), (s)))
- 表达式
**(x)
不会编译是 x 不是指向指针的指针。 sizeof
防止在**(x)
中计算x
以避免副作用(void)
让编译器停止抱怨未使用的值- 逗号运算符
(X,Y)
,return只有Y
的值,也就是returnsecure_free(...)
的值
- 对宏使用与函数相同的名称允许将
secure_free
扩展为仅当它用作函数时的宏。这允许使用secure_free
作为指向函数的指针
补充说明。在代码中
memset(*p_p_data, 0, length_in_bytes);
free(*p_p_data);
编译器可能会优化 memset()
。我建议强制转换为 volatile void *
以强制编译器生成清除代码。
编辑
此外,由于 memset
丢弃了 volatile
限定符,可能会用循环清除内容。
您尝试做的事情无法移植,因为不同的指针类型可以有不同的表示;并且要将空指针分配给该值,您必须首先将指向指针的指针 转换为指向实际的 有效类型 的指针指针变量 - 这是不可能的。
然而你可以做的是使用宏,它和任何宏一样好,而且使用起来更简单:
#define secure_free(x) (free(x), (x) = 0)
这在没有 &
的情况下有效。