可靠地确定数组元素数 [英] Reliably determine the number of elements in an array

查看:127
本文介绍了可靠地确定数组元素数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

每一个C程序员能够确定在这个著名的宏数组中元素的个数:

 的#define NUM_ELEMS(一)(的sizeof(A)/ sizeof的0 [A])

下面是一个典型的用例:

  INT号码[] = {2,3,5,7,11,13,17,19};
的printf(%鲁\\ n,NUM_ELEMS(数字)); // 8,符合市场预期

然而,没有任何$ P $偶然路过而不是数组的指针pvents程序员:

 为int *指针=数;
的printf(%鲁\\ n,NUM_ELEMS(指针));

在我的系统,其打印2,因为很明显,一个指针是两倍整数一样大。我想过如何prevent错误地传递一个指针程序员,我发现了一个解决方案:

 的#define NUM_ELEMS(一)(断言((无效*)及(一)==(无效*)(a)条),(的sizeof(A)/ sizeof的0一个]))

此工作,因为一个指针阵列具有相同的值作为一个指针,它的第一个元素。如果你传递一个指针代替,指针将指向自己的指针,它几乎总是错误的进行比较。 (唯一的例外是一个递归void指针,也就是说,一个空隙的指针指向自身。我可以忍受。)

无意中传递一个指针,而不是一个数组现在在运行时触发一个错误:

 断言'(无效*)及(指针)==(无效*)(指针)'失败。

不错!现在我有几个问题:


  1. 是我的的使用断言作为逗号前pression有效的标准C的左操作数?也就是说,它的标准允许我使用断言作为前pression?很抱歉,如果这是一个愚蠢的问题:)


  2. 可以检查以某种方式在编译时怎么办?


  3. 我的C语言编译器认为 INT B〔NUM_ELEMS(一); 是一个VLA。任何方式,否则说服他?


  4. 我是第一个想到这个?如果是这样,有多少处女可以期待我在天堂等我? :)



解决方案

  

是我的断言的用法逗号前pression有效的标准C的左操作数?也就是说,它的标准允许我使用断言作为前pression?


是的,它是有效的逗号运算符的左操作数的类型可以是无效的前pression。和断言函数无效作为它的返回类型。


  

我的C语言编译器认为,INT B〔NUM_ELEMS(一)];是VLA。任何方式,否则说服他?


据认为是因为逗号前pression的结果是永远恒定的前pression(e..g,1,2不是一个常量前pression)。

EDIT1:添加下面的更新

我有宏的另一个版本,在编译时的作品:

 的#define NUM_ELEMS(ARR)\\
 (sizeof的(结构{INT not_an_array:((无效*)及(ARR)==及(ARR)[0]);})* 0 \\
  +的sizeof(ARR)/ sizeof的(*(ARR)))

和这似乎与初始化与静态存储持续时间对象,甚至也行。
而且这也与你的为int的例子正常运行B〔NUM_ELEMS(A)]

EDIT2:

解决 @DanielFischer 评论。上述工程宏与 GCC 没有 -pedantic 不仅是因为海湾合作委员会接受

 (无效*)及编曲改编==

作为整型常量前pression,而它认为

 (无效*)及PTR PTR ==

是不是一个整数常量前pression。据到C他们都没有整型常量前pressions和 -pedantic GCC 发出正确的诊断在这两种情况下

据我所知,有写这个 NUM_ELEM 宏没有100%的可移植的方法。 C有初始恒前pressions(见C99 6.6p7),它可能被利用来写这个宏更灵活的规则(例如,使用的sizeof 和复合文字)但在框域C不需要初始化为恒定前pressions所以不会有可能有一个单独的宏这在所有的情况下工作的。

EDIT3:

我认为这是值得一提的是,Linux内核有一个 ARRAY_SIZE 宏(在在include / linux / kernel.h )实现稀疏(内核静态分析检查)被执行时,这样的检查。

他们的解决方案是不可移植和利用两个GNU扩展:


  • 的typeof 运营商

  • __ builtin_types_compatible_p 内置函数

基本上它看起来像这样的事情:

 的#define NUM_ELEMS(ARR)\\
 (sizeof的(结构{INT: - !!(__ builtin_types_compatible_p(typeof运算(ARR)的typeof(及(ARR)[0])));})\\
  +的sizeof(ARR)/ sizeof的(*(ARR)))

Every C programmer can determine the number of elements in an array with this well-known macro:

#define NUM_ELEMS(a) (sizeof(a)/sizeof 0[a])

Here is a typical use case:

int numbers[] = {2, 3, 5, 7, 11, 13, 17, 19};
printf("%lu\n", NUM_ELEMS(numbers));          // 8, as expected

However, nothing prevents the programmer from accidentally passing a pointer instead of an array:

int * pointer = numbers;
printf("%lu\n", NUM_ELEMS(pointer));

On my system, this prints 2, because apparently, a pointer is twice as large as an integer. I thought about how to prevent the programmer from passing a pointer by mistake, and I found a solution:

#define NUM_ELEMS(a) (assert((void*)&(a) == (void*)(a)), (sizeof(a)/sizeof 0[a]))

This works because a pointer to an array has the same value as a pointer to its first element. If you pass a pointer instead, the pointer will be compared with a pointer to itself, which is almost always false. (The only exception is a recursive void pointer, that is, a void pointer that points to itself. I can live with that.)

Accidentally passing a pointer instead of an array now triggers an error at runtime:

Assertion `(void*)&(pointer) == (void*)(pointer)' failed.

Nice! Now I have a couple of questions:

  1. Is my usage of assert as the left operand of the comma expression valid standard C? That is, does the standard allow me to use assert as an expression? Sorry if this is a dumb question :)

  2. Can the check somehow be done at compile-time?

  3. My C compiler thinks that int b[NUM_ELEMS(a)]; is a VLA. Any way to convince him otherwise?

  4. Am I the first to think of this? If so, how many virgins can I expect to be waiting for me in heaven? :)

解决方案

Is my usage of assert as the left operand of the comma expression valid standard C? That is, does the standard allow me to use assert as an expression?

Yes, it is valid as the left operand of the comma operator can be an expression of type void. And assert function has void as its return type.

My C compiler thinks that int b[NUM_ELEMS(a)]; is a VLA. Any way to convince him otherwise?

It believes so because the result of a comma expression is never a constant expression (e..g, 1, 2 is not a constant expression).

EDIT1: add the update below.

I have another version of your macro which works at compile time:

#define NUM_ELEMS(arr)                                                 \
 (sizeof (struct {int not_an_array:((void*)&(arr) == &(arr)[0]);}) * 0 \
  + sizeof (arr) / sizeof (*(arr)))

and which seems to work even also with initializer for object with static storage duration. And it also work correctly with your example of int b[NUM_ELEMS(a)]

EDIT2:

to address @DanielFischer comment. The macro above works with gcc without -pedantic only because gcc accepts :

(void *) &arr == arr

as an integer constant expression, while it considers

(void *) &ptr == ptr

is not an integer constant expression. According to C they are both not integer constant expressions and with -pedantic, gcc correctly issues a diagnostic in both cases.

To my knowledge there is no 100% portable way to write this NUM_ELEM macro. C has more flexible rules with initializer constant expressions (see 6.6p7 in C99) which could be exploited to write this macro (for example with sizeof and compound literals) but at block-scope C does not require initializers to be constant expressions so it will not be possible to have a single macro which works in all cases.

EDIT3:

I think it is worth mentioning that the Linux kernel has an ARRAY_SIZE macro (in include/linux/kernel.h) that implements such a check when sparse (the kernel static analysis checker) is executed.

Their solution is not portable and make use of two GNU extensions:

  • typeof operator
  • __builtin_types_compatible_p builtin function

Basically it looks like something like that:

#define NUM_ELEMS(arr)  \
 (sizeof(struct {int :-!!(__builtin_types_compatible_p(typeof(arr), typeof(&(arr)[0])));})  \
  + sizeof (arr) / sizeof (*(arr)))

这篇关于可靠地确定数组元素数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆