LoginSignup
0
1

More than 5 years have passed since last update.

C言語で静的なジャグ配列(っぽいもの)を実現する方法

Last updated at Posted at 2018-11-18

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>> でイニシャライザーリストを用いればいいので、この手法の出番ではない。

インライン展開

サイズによると思うが、できることもある。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1