ARRAYSIZE C++宏:它是如何工作的?

Laj*_*agy 15 c++ c-preprocessor

OK,我不完全是一个新手,但我不能说我理解下面的宏.最令人困惑的部分是将值转换为size_t:这实际上是什么?特别是,因为我看到一个否定运算符,据我所知,它可能导致零值.这是不是意味着它会导致被零除错误?(顺便说一句,宏正确的,并且工作得很漂亮.)

#define ARRAYSIZE(a) \
  ((sizeof(a) / sizeof(*(a))) / \
  static_cast<size_t>(!(sizeof(a) % sizeof(*(a)))))
Run Code Online (Sandbox Code Playgroud)

Oli*_*rth 13

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

第二部分不是那么简单.我认为潜在的归零是有意的; 如果由于某种原因,数组的大小不是其中一个元素的整数倍,它将导致编译时错误.换句话说,它是某种编译时健全性检查.

但是,我无法看到在什么情况下可能发生这种情况......正如人们在下面的评论中提出的那样,它会捕获一些误用(比如ARRAYSIZE()在指针上使用).但是,它不会捕获所有这样的错误.

  • 我能想到的主要情况是“ a”是一个指针,指向大小不是“ sizeof(a)”的因素的东西。因此,您可以期望大多数类,但是如果`a`是`char *`,您将无法得到期望的零除。有更好的方法用C ++编写ARRAYSIZE帮助程序,以便在与指针一起使用时获得警告/错误。由于`static_cast`,这仅是C ++,而不是两用C ++和C。 (2认同)

Che*_*Alf 6

最后的划分似乎是尝试检测非数组参数(例如指针).

例如,它无法检测到char*,但是对于大于指针大小的T*位置会起作用sizeof(T).

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

typedef ptrdiff_t Size;

template< class Type, Size n >
Size countOf( Type (&)[n] ) { return n; }
Run Code Online (Sandbox Code Playgroud)

此函数模板无法使用指针参数进行实例化,只能使用数组.在C++ 11中,它也可以用std::begin和表示std::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 )
Run Code Online (Sandbox Code Playgroud)

免责声明:所有代码都不受编译器的影响.

但一般来说,只需使用第一个功能模板,countOf.

干杯和hth.


jor*_*own 5

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

#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";
}
Run Code Online (Sandbox Code Playgroud)

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

sizeof stats = 4608
sizeof *stats = 144
ARRAYSIZE=32
sizeof stats = 8
sizeof *stats = 144
ARRAYSIZE=0
Run Code Online (Sandbox Code Playgroud)

这是怎么回事?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)))))
Run Code Online (Sandbox Code Playgroud)

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

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