编译时断言以确定指针是否为数组
Compile-time assertion to determine if pointer is an array
目前,我有以下代码块来进行安全的字符串复制(有效):
#define STRCPY(dst, src) do { assert(((void*)(dst)) == ((void*) & (dst))); \
strlcpy(dst, src, sizeof(dst)); } while (0)
所以它接受这样的构造:
const char *src = "hello";
char dest[5];
STRCPY(dest, src); //hell
并否认以下内容:
void (char *dst) {
STRCPY(dst, "heaven"); //unknown size of dst
}
问题是代码块创建了断言。有没有办法对编译时间执行此检查?
所以我希望在编译时出现错误(如 creating an array with negative size),而不是尽可能让代码崩溃。
如果你想检查它,我能想到的唯一方法是:
assert((sizeof(dst)) != (sizeof(void*)));
但只有当数组的大小与 OPs 系统上的指针大小不同时它才有效
我发现我的 post 因重复而关闭了一段时间,并遵循 the link 提到的。
该解决方案仅适用于 GCC,但对我来说很好,因为我也有基于 GCC 的夜间构建。所以代码的预稿是:
#if defined(__GNUC__)
#define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); }))
#define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
#define __must_be_array(a) BUILD_BUG_ON_ZERO(__same_type((a), &(a)[0]))
#define STRCPY(dst,src) do{__must_be_array(dst);strlcpy(dst, src, sizeof(dst));}while(0)
#else
#define STRCPY(dst,src) do{strlcpy(dst, src, sizeof(dst));}while(0)
#endif
如果标准 C 可用,那么您可以这样做:
#define STRCPY(dst, src) \
_Generic(&(dst), \
char(*)[sizeof(dst)]: strlcpy(dst,src,sizeof(dst)) )
解释:
您不能在数组类型上使用 _Generic
表达式,因为它不是不受 "array decay" 规则约束的特殊情况之一 (C17 6.3.2.1 §3) .因此,在我的示例中简单地使用 _Generic((dst), ...
,当计算表达式时,dst
最终会变成 char*
,然后我们将丢失其原始类型的信息。
但是如果我们使用 &
获取数组的地址,我们确实利用了其中一种特殊情况,并且不会发生数组衰减。相反,我们最终得到一个数组指针,这意味着 _Generic
将必须检查预期类型和大小的数组指针:char(*)[sizeof(dst)]
.
作为一个 side-note/safety 问题,我从不使用 do-while(0)
宏并阻止它们,但那是另一回事了。
对于 dst
是否为数组的编译时断言,
如果 C11 可用,我会使用@Lundin 解决方案(或 _Static_assert
)。
否则,我会使用以下内容:
#define BUILD_BUG_ON_NON_ARRAY_TYPE(e) (sizeof(struct { int:-!!(((void*)(e)) != ((void*) & (e))); }))
这基本上采用你的编译时评估表达式 ((void*)(dst)) == ((void*) & (dst))
但不是在 运行 时间使用它 assert
只是以编译时断言方式使用它。
所以,总的来说,我会将您的宏更改为:
#define STRCPY(dst, src) do { BUILD_BUG_ON_NON_ARRAY_TYPE(dst); \
strlcpy(dst, src, sizeof(dst)); } while (0)
目前,我有以下代码块来进行安全的字符串复制(有效):
#define STRCPY(dst, src) do { assert(((void*)(dst)) == ((void*) & (dst))); \
strlcpy(dst, src, sizeof(dst)); } while (0)
所以它接受这样的构造:
const char *src = "hello";
char dest[5];
STRCPY(dest, src); //hell
并否认以下内容:
void (char *dst) {
STRCPY(dst, "heaven"); //unknown size of dst
}
问题是代码块创建了断言。有没有办法对编译时间执行此检查?
所以我希望在编译时出现错误(如 creating an array with negative size),而不是尽可能让代码崩溃。
如果你想检查它,我能想到的唯一方法是:
assert((sizeof(dst)) != (sizeof(void*)));
但只有当数组的大小与 OPs 系统上的指针大小不同时它才有效
我发现我的 post 因重复而关闭了一段时间,并遵循 the link 提到的。 该解决方案仅适用于 GCC,但对我来说很好,因为我也有基于 GCC 的夜间构建。所以代码的预稿是:
#if defined(__GNUC__)
#define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); }))
#define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
#define __must_be_array(a) BUILD_BUG_ON_ZERO(__same_type((a), &(a)[0]))
#define STRCPY(dst,src) do{__must_be_array(dst);strlcpy(dst, src, sizeof(dst));}while(0)
#else
#define STRCPY(dst,src) do{strlcpy(dst, src, sizeof(dst));}while(0)
#endif
如果标准 C 可用,那么您可以这样做:
#define STRCPY(dst, src) \
_Generic(&(dst), \
char(*)[sizeof(dst)]: strlcpy(dst,src,sizeof(dst)) )
解释:
您不能在数组类型上使用 _Generic
表达式,因为它不是不受 "array decay" 规则约束的特殊情况之一 (C17 6.3.2.1 §3) .因此,在我的示例中简单地使用 _Generic((dst), ...
,当计算表达式时,dst
最终会变成 char*
,然后我们将丢失其原始类型的信息。
但是如果我们使用 &
获取数组的地址,我们确实利用了其中一种特殊情况,并且不会发生数组衰减。相反,我们最终得到一个数组指针,这意味着 _Generic
将必须检查预期类型和大小的数组指针:char(*)[sizeof(dst)]
.
作为一个 side-note/safety 问题,我从不使用 do-while(0)
宏并阻止它们,但那是另一回事了。
对于 dst
是否为数组的编译时断言,
如果 C11 可用,我会使用@Lundin 解决方案(或 _Static_assert
)。
否则,我会使用以下内容:
#define BUILD_BUG_ON_NON_ARRAY_TYPE(e) (sizeof(struct { int:-!!(((void*)(e)) != ((void*) & (e))); }))
这基本上采用你的编译时评估表达式 ((void*)(dst)) == ((void*) & (dst))
但不是在 运行 时间使用它 assert
只是以编译时断言方式使用它。
所以,总的来说,我会将您的宏更改为:
#define STRCPY(dst, src) do { BUILD_BUG_ON_NON_ARRAY_TYPE(dst); \
strlcpy(dst, src, sizeof(dst)); } while (0)