【问题标题】:ARRAYSIZE C++ macro: how does it work?ARRAYSIZE C++ 宏:它是如何工作的?
【发布时间】:2010-10-31 17:17:43
【问题描述】:

好的,我不是完全新手,但我不能说我理解以下宏。最令人困惑的部分是将 value 转换为 size_t 的除法:这到底完成了什么?特别是,因为我看到一个否定运算符,据我所知,它可能会导致零值。这是否意味着它会导致被零除的错误? (顺便说一句,宏 是正确的,而且效果很好。)

#define ARRAYSIZE(a) \
  ((sizeof(a) / sizeof(*(a))) / \
  static_cast<size_t>(!(sizeof(a) % sizeof(*(a)))))

【问题讨论】:

    标签: c++ c-preprocessor


    【解决方案1】:

    第一部分(sizeof(a) / sizeof(*(a))) 相当简单;它将整个数组的大小(假设您将宏传递给数组类型的对象,而不是指针)除以第一个元素的大小。这给出了数组中元素的数量。

    第二部分不是那么简单。我认为潜在的除零是故意的。如果由于某种原因数组的大小不是其元素之一的整数倍,则会导致编译时错误。换句话说,它是某种编译时完整性检查。

    但是,我不知道在什么情况下会发生这种情况...正如人们在下面的 cmets 中所建议的那样,它会捕获 一些 误用(例如在指针上使用 ARRAYSIZE())。但是,它不会捕获所有这样的错误。

    【讨论】:

    • 我认为它可以捕捉到偶尔滥用宏的情况,比如在指针而不是数组上调用它。不幸的是,它无法捕获诸如在指向与指针大小相同的对象的指针上调用它。
    • 我能想到的主要情况是a 是一个指针,指向大小不是sizeof (a) 的因素。所以大多数类,你会期望的,但如果achar*,你将不会得到所需的除零。有更好的方法可以在 C++ 中编写 ARRAYSIZE 帮助程序,以便在与指针一起使用时获得警告/错误。这只是 C++,而不是双重用途的 C++ 和 C,因为 static_cast
    【解决方案2】:

    我写了这个宏的这个版本。考虑旧版本:

    #include <sys/stat.h>
    #define ARRAYSIZE(a) (sizeof(a) / sizeof(*(a)))
    
    int main(int argc, char *argv[]) {
      struct stat stats[32];
      std::cout << "sizeof stats = " << (sizeof stats) << "\n";
      std::cout << "sizeof *stats = " << (sizeof *stats) << "\n";
      std::cout << "ARRAYSIZE=" << ARRAYSIZE(stats) << "\n";
    
      foo(stats);
    }
    
    void foo(struct stat stats[32]) {
      std::cout << "sizeof stats = " << (sizeof stats) << "\n";
      std::cout << "sizeof *stats = " << (sizeof *stats) << "\n";
      std::cout << "ARRAYSIZE=" << ARRAYSIZE(stats) << "\n";
    }
    

    在 64 位机器上,此代码产生以下输出:

    sizeof stats = 4608
    sizeof *stats = 144
    ARRAYSIZE=32
    sizeof stats = 8
    sizeof *stats = 144
    ARRAYSIZE=0
    

    发生了什么事? ARRAYSIZE 是如何从 32 变为 0 的?好吧,问题是函数参数实际上是一个指针,尽管它看起来像一个数组。所以在 foo 里面,"sizeof(stats)" 是 8 个字节,而 "sizeof(*stats)" 仍然是 144。

    使用新宏:

    #define ARRAYSIZE(a) \
      ((sizeof(a) / sizeof(*(a))) / \
      static_cast<size_t>(!(sizeof(a) % sizeof(*(a)))))
    

    当 sizeof(a) 不是 sizeof(* (a)) 的倍数时,% 不为零,即 !反转,然后 static_cast 计算为零,导致编译时除以零。因此,尽可能在宏中,这种奇怪的划分在编译时发现了问题。

    PS:在 C++17 中,只需使用 std::size,参见http://en.cppreference.com/w/cpp/iterator/size

    【讨论】:

    • std::size(a) !最佳建议。
    【解决方案3】:

    最后的除法似乎是尝试检测非数组参数(例如指针)。

    它无法检测到,例如char*,但可以用于T*,其中sizeof(T) 大于指针的大小。

    在 C++ 中,人们通常更喜欢以下函数模板:

    typedef ptrdiff_t Size;
    
    template< class Type, Size n >
    Size countOf( Type (&)[n] ) { return n; }
    

    这个函数模板不能用指针参数实例化,只能用数组。在 C++11 中,它也可以用 std::beginstd::end 来表示,这会自动使其也适用于具有随机访问迭代器的标准容器。

    限制:不适用于 C++03 中的本地类型数组,并且不会产生编译时大小。

    对于编译时间大小,您可以改为这样做

    template< Size n > struct Sizer { char elems[n]; };
    
    template< class Type, size n >
    Sizer<n> countOf_( Type (&)[n] );
    
    #define COUNT_OF( a ) sizeof( countOf_( a ).elems )
    

    免责声明:所有代码均未经编译器处理。

    但一般情况下,只需使用第一个函数模板,countOf

    干杯&hth。

    【讨论】:

      【解决方案4】:

      假设我们有

      T arr[42];
      

      ARRAYSIZE(arr) 将扩展为(粗略)

      sizeof (arr) / sizeof(*arr) / !(sizeof(arr) % sizeof(*arr))
      

      在这种情况下给出 42/!0 即 42

      如果由于某种原因 sizeof 数组不能被其元素的 sizeof 整除,则会发生被零除。什么时候可以发生?例如,当您传递动态分配的数组而不是静态数组时!

      【讨论】:

        【解决方案5】:

        它确实会导致除零错误(故意)。这个宏的工作方式是将数组的大小(以字节为单位)除以单个数组元素的大小(以字节为单位)。因此,如果您有一个 int 值数组,其中 int 是 4 个字节(在大多数 32 位机器上),那么一个包含 4 个 int 值的数组将是 16 个字节。

        因此,当您在这样的数组上调用此宏时,它会执行sizeof(array) / sizeof(*array)。并且由于 16 / 4 = 4,它返回数组中有 4 个元素。

        注意:*array 取消引用数组的第一个元素,等效于array[0]

        第二个除法是模除法(得到除法的余数),并且由于任何非零值都被认为是“真”,如果除法的余数,使用! 运算符将导致除以零非零(同样,除以 1)。

        【讨论】:

          【解决方案6】:

          div-by-zero 可能试图捕获由任何原因引起的对齐错误。就像,如果在某些编译器设置下,数组元素的大小为 3,但编译器会将其舍入为 4 以加快数组访问速度,那么包含 4 个条目的数组的大小将为 16,而 !(16/3) 将变为为零,在编译时除以零。但是,我不知道有任何编译器会这样做,并且它可能违反 C++ 的 sizeof 规范以返回与数组中该类型的大小不同的大小..

          【讨论】:

            【解决方案7】:

            这里的聚会迟到了……

            Google 的 C++ 代码库有恕我直言arraysize() 宏的最终 C++ 实现,其中包括一些此处未考虑的皱纹。

            我无法改进the source,它有清晰完整的cmets。

            【讨论】:

            • 链接现在失效了,这个答案没有任何信息。
            • 现在应该修复死链接。
            猜你喜欢
            • 2011-12-15
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-03-29
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多