fre*_*low 16 c arrays assert pointers sizeof
每个C程序员都可以使用这个众所周知的宏来确定数组中元素的数量:
#define NUM_ELEMS(a) (sizeof(a)/sizeof 0[a])
Run Code Online (Sandbox Code Playgroud)
这是一个典型的用例:
int numbers[] = {2, 3, 5, 7, 11, 13, 17, 19};
printf("%lu\n", NUM_ELEMS(numbers)); // 8, as expected
Run Code Online (Sandbox Code Playgroud)
但是,没有什么能阻止程序员意外地传递指针而不是数组:
int * pointer = numbers;
printf("%lu\n", NUM_ELEMS(pointer));
Run Code Online (Sandbox Code Playgroud)
在我的系统上,这打印2,因为显然,指针是整数的两倍.我想过如何防止程序员错误地传递指针,我找到了一个解决方案:
#define NUM_ELEMS(a) (assert((void*)&(a) == (void*)(a)), (sizeof(a)/sizeof 0[a]))
Run Code Online (Sandbox Code Playgroud)
这是有效的,因为指向数组的指针与指向其第一个元素的指针具有相同的值.如果改为传递指针,指针将与指向自身的指针进行比较,这几乎总是假的.(唯一的例外是递归的void指针,也就是指向自身的void指针.我可以忍受它.)
意外地传递指针而不是数组现在在运行时触发错误:
Assertion `(void*)&(pointer) == (void*)(pointer)' failed.
Run Code Online (Sandbox Code Playgroud)
太好了!现在我有几个问题:
我的用法是assert
逗号表达式的左操作数有效标准C吗?也就是说,标准是否允许我assert
用作表达式?对不起,如果这是一个愚蠢的问题:)
可以在编译时以某种方式完成检查吗?
我的C编译器认为这int b[NUM_ELEMS(a)];
是一个VLA.有没有办法说服他呢?
我是第一个想到这个吗?如果是这样,我可以期待在天堂等待多少处女?:)
oua*_*uah 10
我使用assert作为逗号表达式有效标准C的左操作数吗?也就是说,标准是否允许我使用assert作为表达式?
是的,它是有效的,因为逗号运算符的左操作数可以是类型的表达式void
.并且assert
函数具有void
返回类型.
我的C编译器认为int b [NUM_ELEMS(a)]; 是一个VLA.有没有办法说服他呢?
它认为是因为逗号表达式的结果永远不是常量表达式(例如,1,2不是常量表达式).
EDIT1:添加以下更新.
我有另一个版本的宏在编译时工作:
#define NUM_ELEMS(arr) \
(sizeof (struct {int not_an_array:((void*)&(arr) == &(arr)[0]);}) * 0 \
+ sizeof (arr) / sizeof (*(arr)))
Run Code Online (Sandbox Code Playgroud)
这似乎也适用于具有静态存储持续时间的对象的初始化程序.并且它也适用于你的例子int b[NUM_ELEMS(a)]
EDIT2:
解决@DanielFischer评论.宏上面工作gcc
,而不 -pedantic
只是因为gcc
接受:
(void *) &arr == arr
Run Code Online (Sandbox Code Playgroud)
作为一个整数常量表达式,它考虑
(void *) &ptr == ptr
Run Code Online (Sandbox Code Playgroud)
不是整数常量表达式.据到C他们都没有整型常量表达式,并用-pedantic
,gcc
正确的问题在这两种情况下的诊断.
据我所知,没有100%可移植的方式来编写这个NUM_ELEM
宏.C具有更灵活的规则和初始化程序常量表达式(参见C99中的6.6p7),可以利用它来编写这个宏(例如使用sizeof
和复合文字),但是在块作用域C不要求初始化器是常量表达式所以它不会可以有一个宏,它适用于所有情况.
EDIT3:
我认为值得一提的是,Linux内核有一个ARRAY_SIZE
宏(in include/linux/kernel.h
),在执行稀疏(内核静态分析检查器)时实现这样的检查.
他们的解决方案不可移植,并使用两个GNU扩展:
typeof
操作者__builtin_types_compatible_p
内置功能基本上它看起来像这样:
#define NUM_ELEMS(arr) \
(sizeof(struct {int :-!!(__builtin_types_compatible_p(typeof(arr), typeof(&(arr)[0])));}) \
+ sizeof (arr) / sizeof (*(arr)))
Run Code Online (Sandbox Code Playgroud)
assert()
是一个 void 表达式,所以一开始就没有问题。