为了账号安全,请及时绑定邮箱和手机立即绑定

拒绝指针的数组大小的宏

拒绝指针的数组大小的宏

C
RISEBY 2019-10-18 09:53:14
通常讲授的标准数组大小宏是#define ARRAYSIZE(arr) (sizeof(arr) / sizeof(arr[0]))或某些等效形式。但是,当传入一个指针时,这种事情会悄无声息地成功,并且给出的结果在运行时看起来似乎是合理的,直到事情神秘地消失。犯这样的错误太容易了:重构具有局部数组变量的函数,将数组操作的一部分移到以数组为参数的新函数中。因此,问题是:是否存在“卫生的”宏来检测ARRAYSIZEC中宏的滥用,最好是在编译时?在C ++中,我们将仅使用专门用于数组参数的模板。在C语言中,似乎我们需要某种方式来区分数组和指针。(例如,如果我想拒绝数组,我会做例如(arr=arr, ...)因为数组分配是非法的)。
查看完整描述

3 回答

?
ITMISS

TA贡献1871条经验 获得超8个赞

Linux内核使用了一个不错的实现ARRAY_SIZE来解决此问题:


#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + __must_be_array(arr))


#define __must_be_array(a) BUILD_BUG_ON_ZERO(__same_type((a), &(a)[0]))


#define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))

当然,这仅在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)))


#define IS_ARRAY(arr)  ((void*)&(arr) == &(arr)[0])

#define STATIC_EXP(e)  \

    (0 * sizeof (struct { int ARRAY_SIZE_FAILED:(2 * (e) - 1);}))

随着gcc如果参数是一个数组这个没有给出警告,-std=c99 -Wall但-pedantic会发出警告。原因是IS_ARRAYexpression不是整数常量表达式(整数常量表达式中不允许转换为指针类型和下标运算符),并且in的位字段宽度STATIC_EXP需要整数常量表达式。


查看完整回答
反对 回复 2019-10-18
?
PIPIONE

TA贡献1829条经验 获得超9个赞

此版本的ARRAYSIZE()return 0when 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;

}

输出:


5

0

10

正如本杰克逊(Ben Jackson)指出的那样,您可以强制运行时异常(除以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))

可悲的是,您不能强制执行编译时错误(arg必须在运行时比较的地址)


查看完整回答
反对 回复 2019-10-18
  • 3 回答
  • 0 关注
  • 434 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信