【问题标题】:C find static array size (preventing mistakes) [duplicate]C查找静态数组大小(防止错误)[重复]
【发布时间】:2015-07-07 14:47:09
【问题描述】:

查找静态数组的大小是一种常见操作。见:C find static array size - sizeof(a) / sizeof((a)[0])

这可以包装成一个宏,例如:

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

但是可能会意外传入常规指针。

例如:void func(SomeArray **foo) { int i = ARRAY_SIZE(foo); }

虽然它的 C 是有效的,但通常以逻辑错误告终。

有可能防止这个错误(利用每个处理器在零长度位字段上失败)


#define ARRAY_SIZE(a) \
    ((sizeof(struct { int isnt_array : \
     ((const void *)&(a) == &(a)[0]); }) * 0) + \
     (sizeof(a) / sizeof(*(a))))

我发现此宏适用于 GCC,但不适用于 Clang,用于间接引用的成员。 error: expression is not an integer constant expression

例如:

  • char word[8]; int i = ARRAY_SIZE(word);好的。
  • struct Bar { word[8]; }
    void func(struct Bar *foo) { int i = ARRAY_SIZE(foo->word); } 失败。

有没有更便携的方法来实现这一点? (虽然我对通用的可移植性很感兴趣......其他编译器也是)。

这似乎是一项常见的任务,最好有一个可重复使用的便携式宏。

【问题讨论】:

  • 1.这就是宏的问题,也是很多人讨厌它们的原因:它们会导致难以发现的错误。 2. clang 对你的例子有什么看法?
  • 我不认为这是宏的具体问题,sizeof(a) / sizeof((a)[0]) 的意外误用也可能在没有宏的情况下发生。如果您使用例如参数回复本地固定宽度字符串,则可能会发生这种情况。 (已编辑以包含 Clang 的错误消息)
  • @ideasman42:你确定位域长度可以是非常量表达式吗?还是我误读了您的代码?...
  • @Steve Fallows,^^^ 不是重复的,因为它是一种不同的语言,答案涉及使用 C++ 特定功能。
  • @ideasman42 - 哇! - 不仔细阅读。如果未来的读者正在寻找 C++ 解决方案,我会留下评论。

标签: c arrays c-preprocessor


【解决方案1】:

这个宏对 clang 和 gcc 有效(无论如何在我的测试中)。我几乎可以肯定没有便携式解决方案。

#define ARRAY_SIZE(a) \
    (({ static __typeof__(a) _aa; \
        static __typeof__(&(a)[0]) _pa = _aa; (void)_pa; }), \
           sizeof(a)/sizeof((a)[0]))

【讨论】:

  • 这些是静态的有什么理由吗?我得到这个,error: initializer element is not constant,用于_pa 分配(GCC 5.1)
  • 我无法让它工作(在通过删除 static 使其编译后)。 char *foo 的函数参数成功,(当它不应该有的时候)和本地定义的 char foo[10] 一样。
  • 我用 gcc-4.9 和 clang-3.5 测试过,很快就会看到更新的版本。
  • 我收到多个警告,尤其是:unused variable '_pa' [-Wunused-variable]
  • 可以抑制未使用的变量警告,而不是on语句表达式。恐怕您的版本确实会为宏的所有实例创建静态数据,而不是理想的副作用。
【解决方案2】:

试试这个:

#define ASSERT_ARRAY(a) \
    sizeof(char[1-2*__builtin_types_compatible_p(__typeof__(a), __typeof__(&(a)[0]))])

#define ARRAY_SIZE(a) \
    (ASSERT_ARRAY(a)*0 + sizeof(a)/sizeof((a)[0]))

它不可移植,但可与gccclang 一起使用,而且副作用比n.m. 的提议要少。

【讨论】:

    【解决方案3】:

    这是我使用的,同时适用于 C 和 C++ 的解决方案。

    这对我来说是一个要求,即使在 VLA 上也能工作。

    #ifndef __cplusplus
    int _ptr_used_(void) __attribute__((error("Pointer used in place of array") ));
    #define ARRAY_SIZEOF(arr) ( \
    __builtin_types_compatible_p(typeof(arr), typeof((arr)[0])*) \
    ? _ptr_used_() \
    : sizeof(arr)/sizeof((arr)[0]) \
    )
    #else
    /// A type that exists
    struct _true_ {};
    
    template <bool constant>
    struct is_an_array
    {
        /// Used when a constant sized (non-VLA) object is passed in
        /// only allow arrays past
        template<class B, size_t n>
        static _true_ test( B(&)[n] );
    };
    
    template <>
    struct is_an_array<false>
    {
        /// This happens only for VLAs; force decay to a pointer to let it work with templates
        template <class B>
        static _true_ test(B *n);
    };
    
    # define ARRAY_SIZEOF(arr) ({ typedef decltype(is_an_array<static_cast<bool>(__builtin_constant_p(sizeof(arr)))>::test(arr)) type; sizeof(arr) / sizeof((arr)[0]); })
    #endif
    

    【讨论】:

    • clang 似乎不支持属性错误。我在 MacOS 上收到此警告:warning: unknown attribute 'error' ignored [-Wunknown-attributes]ARRAY_SIZEOF(p) 上的链接器失败。
    • @chqrlie 那部分只是静态断言的实现。您可以放弃任何其他实现,例如 sizeof(char[1-2*b]),但代价是放弃漂亮的错误消息。
    【解决方案4】:

    真的需要编译时断言吗?如果是,恐怕没有可移植的方式,您只能通过特定于实现的技巧使其在 Clang 和 GCC 或其他编译器上工作。

    但是,如果您决定追求可移植性,则可以使用运行时错误(这可能同样有效,具体取决于您的测试策略)。假设你有一个错误报告函数void error(char *errorText)。宏可能看起来像这样(未经测试,但我希望你明白):

    #ifdef DEBUG /* Place your debug-mode-flag macro here.
     You won't want the extra branch in the release build */
    #define ARRAY_SIZE(a) \
        ((const void *)&(a) == &(a)[0]) ? \
            (sizeof(a) / sizeof(*(a))) : (error("Tried to treat pointer as array!"), 0)
    #else
    #define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
    #endif
    

    【讨论】:

      猜你喜欢
      • 2012-05-04
      • 2013-08-24
      • 2016-12-20
      • 2014-03-05
      • 2018-01-13
      • 2022-12-06
      • 2011-09-11
      • 1970-01-01
      • 2014-10-06
      相关资源
      最近更新 更多