编译时断言以确定指针是否为数组

时间:2018-12-18 10:56:41

标签: c assertion

当前,我有以下代码块可以进行安全的字符串复制(有效):

#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),而不是使代码崩溃。

4 个答案:

答案 0 :(得分:5)

如果可以使用标准C,则可以执行以下操作:

#define STRCPY(dst, src)                                        \
  _Generic(&(dst),                                              \
           char(*)[sizeof(dst)]: strlcpy(dst,src,sizeof(dst)) )

说明:

您不能在数组类型上使用_Generic表达式,因为它不是不受“数组衰减”规则(C17 6.3.2.1§3)约束的特殊情况之一。因此,在我的示例中,仅使用_Generic((dst), ...,在对表达式求值时,dst最终将以char*的形式出现,然后我们将丢失其原始类型的信息。

但是,如果我们使用&来获取数组的地址,那么我们会利用其中一种特殊情况,并且不会发生数组衰减。相反,我们以数组指针结尾,这意味着_Generic将必须检查预期类型和大小的数组指针:char(*)[sizeof(dst)]


作为附带说明/安全问题,我从不使用do-while(0)宏并不鼓励使用它们,但这是另一回事了。

答案 1 :(得分:1)

我发现我的帖子被关闭了一段时间(重复了一段时间),并遵循了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

答案 2 :(得分:1)

要在编译时声明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)

答案 3 :(得分:0)

如果您想检查一下,我想到的唯一方法是:

assert((sizeof(dst)) != (sizeof(void*)));

但是只有在数组的大小与OPs系统上的指针的大小不同时,它才起作用