nne*_*neo 49 c arrays macros c-preprocessor
经常教授的标准数组大小的宏是
#define ARRAYSIZE(arr) (sizeof(arr) / sizeof(arr[0]))
Run Code Online (Sandbox Code Playgroud)
或一些等效的形成.然而,当传入指针时,这种事情会默默地成功,并且在运行时看起来似乎有道理,直到事情神秘地分崩离析.
犯这个错误太容易了:一个具有局部数组变量的函数被重构,将一些数组操作移动到一个以数组作为参数调用的新函数中.
所以,问题是:是否有一个"卫生"宏来检测ARRAYSIZE
C中宏的滥用,最好是在编译时?在C++中,我们只使用专门用于数组参数的模板; 在C中,似乎我们需要一些方法来区分数组和指针.(例如,如果我想拒绝数组,我只是(arr=arr, ...)
因为数组赋值是非法的).
oua*_*uah 31
Linux内核使用一个很好的实现ARRAY_SIZE
来处理这个问题:
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + __must_be_array(arr))
Run Code Online (Sandbox Code Playgroud)
同
#define __must_be_array(a) BUILD_BUG_ON_ZERO(__same_type((a), &(a)[0]))
Run Code Online (Sandbox Code Playgroud)
和
#define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
Run Code Online (Sandbox Code Playgroud)
当然,这只能在GNU C中移植,因为它使用了两个内在函数:
typeof
运算符和__builtin_types_compatible_p
函数.它还使用了他们的"着名" BUILD_BUG_ON_ZERO
宏,它只在GNU C中有效.
假设编译时评估要求(这是我们想要的),我不知道这个宏的任何可移植实现.
"半便携式"实施(并不涵盖所有情况)是:
#define ARRAY_SIZE(arr) \
(sizeof(arr) / sizeof((arr)[0]) + STATIC_EXP(IS_ARRAY(arr)))
Run Code Online (Sandbox Code Playgroud)
同
#define IS_ARRAY(arr) ((void*)&(arr) == &(arr)[0])
#define STATIC_EXP(e) \
(0 * sizeof (struct { int ARRAY_SIZE_FAILED:(2 * (e) - 1);}))
Run Code Online (Sandbox Code Playgroud)
随着gcc
如果参数是一个数组这个没有给出警告,-std=c99 -Wall
但-pedantic
会发出警告.原因是IS_ARRAY
expression不是整型常量表达式(在整型常量表达式中不允许转换为指针类型和下标运算符),并且位域宽度in STATIC_EXP
需要整数常量表达式.
Dav*_*eri 17
这个版本ARRAYSIZE()
返回0
时arr
是一个指针和尺寸当其纯阵列
#include <stdio.h>
#define IS_INDEXABLE(arg) (sizeof(arg[0]))
#define IS_ARRAY(arg) (IS_INDEXABLE(arg) && (((void *) &arg) == ((void *) arg)))
#define ARRAYSIZE(arr) (IS_ARRAY(arr) ? (sizeof(arr) / sizeof(arr[0])) : 0)
int main(void)
{
int a[5];
int *b = a;
int n = 10;
int c[n]; /* a VLA */
printf("%zu\n", ARRAYSIZE(a));
printf("%zu\n", ARRAYSIZE(b));
printf("%zu\n", ARRAYSIZE(c));
return 0;
}
Run Code Online (Sandbox Code Playgroud)
输出:
5
0
10
Run Code Online (Sandbox Code Playgroud)
正如本杰克逊所指出的,你可以强制运行时异常(除以0)
#define IS_INDEXABLE(arg) (sizeof(arg[0]))
#define IS_ARRAY(arg) (IS_INDEXABLE(arg) && (((void *) &arg) == ((void *) arg)))
#define ARRAYSIZE(arr) (sizeof(arr) / (IS_ARRAY(arr) ? sizeof(arr[0]) : 0))
Run Code Online (Sandbox Code Playgroud)
遗憾的是,你不能强制编译时错误(arg
必须在运行时比较地址)
使用C11,我们可以使用区分数组和指针_Generic
,但是如果您提供元素类型,我只找到了一种方法:
#define ARRAY_SIZE(A, T) \
_Generic(&(A), \
T **: (void)0, \
default: _Generic(&(A)[0], T *: sizeof(A) / sizeof((A)[0])))
int a[2];
printf("%zu\n", ARRAY_SIZE(a, int));
Run Code Online (Sandbox Code Playgroud)
宏检查:1)指向A的指针不是指向指针的指针.2)指向elem的指针指向T.它(void)0
使用指针进行静态求值和失败.
这是一个不完美的答案,但也许读者可以改进它并摆脱该类型参数!
使用typeof而不是类型参数修改bluss的答案:
#define ARRAY_SIZE(A) \
_Generic(&(A), \
typeof((A)[0]) **: (void)0, \
default: sizeof(A) / sizeof((A)[0]))
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
12293 次 |
最近记录: |