[英]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
}
问题是代码块会产生一个断言。 有没有办法在编译时执行此检查?
所以我想在编译时遇到错误(比如创建一个负大小的数组 ),而不是在可能的情况下崩溃代码。
如果标准C可用,那么您可以这样做:
#define STRCPY(dst, src) \
_Generic(&(dst), \
char(*)[sizeof(dst)]: strlcpy(dst,src,sizeof(dst)) )
说明:
您不能在数组类型上使用_Generic
表达式,因为它不是免于“数组衰减”规则的特殊情况之一(C176.3.2.1§3)。 因此,通过在我的示例中简单地使用_Generic((dst), ...
,当评估表达式时, dst
将最终作为char*
,然后我们将丢失其原始类型的信息。
但是如果我们使用&
获取数组的地址,我们会利用其中一种特殊情况并且不会发生数组衰减。 相反,我们最终得到一个数组指针,这意味着_Generic
将必须检查预期类型和大小的数组指针: char(*)[sizeof(dst)]
。
作为侧面注意/安全问题,我从不使用do-while(0)
宏并且不鼓励它们,但这是另一个故事。
我发现我的帖子已经关闭了一段时间,并按照提到的链接进行了操作 。 解决方案仅适用于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
对于dst
是否是数组的编译时断言,我会使用@Lundin解决方案(或_Static_assert
)以防C11可用。
否则,我会使用以下内容:
#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)
如果你想检查它我能想到的唯一方法是:
assert((sizeof(dst)) != (sizeof(void*)));
但它只有在数组的大小与OPs系统上的指针大小不同时才有效
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.