C言語では、ジャグ配列は構文としてサポートされていない。
Wikipedia の例のように、ポインタ型の配列に各配列を格納しなければならない。
扱いにくいため、個人的にはあまり使用しないが、「初期化された静的な可変長配列のリスト」が欲しいときがある。
(ROM持ちバイナリデータをリストとして扱いたい、とか。)
探せば解説しているページもあるだろうが、Qiita には無さそうなので実装してみた。
注
規格に精通してはいないので、用語に関しては誤りがあるかもしれません。
編集リクエストをお待ちしています。
要件
- C99(相当)レベルの主要なコンパイラ(というかVisual C++ 2008)でコンパイルできること。
(規格準拠はあまり気にしていない。) - 内部の可変長配列は静的領域に確保されること。
- 要素はイニシャライザで初期化できること。
- できればインライン展開可能であること。
サンプルコード
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#define DEFINE_INNER_ARRAY_INFO(T) \
typedef struct { \
size_t length; \
const T* data; \
} InnerArrayInfo_##T;
#define INNER_ARRAY_BEGIN \
enum { base = __COUNTER__ + 1 }; \
switch (index) {
#define INNER_ARRAY(T, ...) \
case (__COUNTER__ - base): { \
static const T data[] = {__VA_ARGS__}; \
const InnerArrayInfo_##T a = {sizeof(data) / sizeof(data[0]), data}; \
return a; \
}
#define INNER_ARRAY_END(T) \
default: { \
const InnerArrayInfo_##T a = {0}; \
return a; \
} \
}
DEFINE_INNER_ARRAY_INFO(uint8_t);
InnerArrayInfo_uint8_t numArr(size_t index) {
INNER_ARRAY_BEGIN
INNER_ARRAY(uint8_t, 1, 2, 3);
INNER_ARRAY(uint8_t, 4, 5, 6, 7);
INNER_ARRAY(uint8_t, 8, 9);
INNER_ARRAY_END(uint8_t);
}
int main() {
size_t i = 0;
for (InnerArrayInfo_uint8_t array = numArr(i); array.length > 0;
i++, array = numArr(i)) {
ioctl(0, 0xDEAD, array.data, array.length);
}
}
解説(というかメモ)
- ジャグ配列となるオブジェクト自体は作らず、「インデックスを渡すことでジャグ配列のように振る舞う関数」で実装している。
- サンプルコードを見ての通り、
case
内のブロック内に静的な可変長配列を用意している。 - 引数の処理が面倒で、翻訳単位に複数のジャグ配列を書けるようにするため、
__COUNTER__
を使用している。
主要なコンパイラ では対応しているようなので、問題ないだろう。 - 長さ0を番兵にしているので、内部の配列では長さ0の配列はサポートしていない。
- ジャグ配列の長さは直接取得できない。
C++であれば、クラス化すれば可能であるが、 そもそも C++11であればstd::vector<std::vector<T>>
でイニシャライザーリストを用いればいいので、この手法の出番ではない。
インライン展開
サイズによると思うが、できることもある。