sizeof(array) / sizeof(array)なんだけど、配列名が長くなって、たとえば
sizeof(var.that_has_an_array.as_a.member) / sizeof(var.that_has_an_array.as_a.member[0])とかになるとカオス。
なので、ベンダーによっては、
#define _countof(array) (sizeof(array) / sizeof(array[0]))みたいなマクロを提供していたりするんだけど、こうやって、何も考えずに使えるようにしていくと、配列ではなくポインタを引数に渡しちゃって、サイズ計算ミスって変な動作する懸念が増してくる。
なので、Twitterで
C言語で、ある値がポインタなのか配列なのかを知る方法ってあるのかなぁ(gcc/clang拡張でも可)。意図としては countof(array) みたいなマクロで、引数arrayに渡されるものがポインタではなく配列であることをビルド時に保証したい
— Kazuho Oku (@kazuho) April 14, 2020
cf. https://t.co/izurmOdiTl
と聞いたところ、mattnさんから
gcc だとこのマクロがうまく動きそうです。
— mattn (@mattn_jp) April 14, 2020
# define IS_ARRAY(arg) __builtin_choose_expr(__builtin_types_compatible_p(typeof(arg[0]) [], typeof(arg)), 1, 0)
と教えてもらったので、
#define PTLS_BUILD_ASSERT(cond) \ ((void)sizeof(char[2 * !!(!__builtin_constant_p(cond) || (cond)) - 1])) #define PTLS_ASSERT_IS_ARRAY(a) \ PTLS_BUILD_ASSERT( __builtin_types_compatible_p(__typeof__(a[0])[], __typeof__(a))) #define PTLS_ELEMENTSOF(x) \ (PTLS_ASSERT_IS_ARRAY(x), sizeof(x) / sizeof((x)[0]))こんな感じにして取り込みました。これで、先の冗長な例も
PTLS_ELEMENTSOF(var.that_has_an_array.as_a.member)と書くことができる。便利。
完全な変更は add PTLS_ELEMENTSOF for counting the number of elements in an array by kazuho · Pull Request #301 · h2o/picotls をご覧ください。
PS. 配列かポインタかの確認方法については、yuguiさんから下のようなコンパイラ非依存の解も教えてもらったのですが、残念ながらコンパイル時に動かすことが難しいようでした。実行時判定が必要なケースなら、この方法のほうがいいかも。
&してからintptr_tにキャストして、元の値と比較したらイケませんか?
— Yuki Yugui Sonoda (@yugui) April 14, 2020
もっと移植性のある方法もあった気はするんですが