BLOGTIMES
2020/10/07

int arr[n]; はいつからできるようになったのか

  c  programming 
このエントリーをはてなブックマークに追加

C プログラミングを教えていときに「int arr[n]; のような感じで、配列の宣言時に変数が使えるのか?」と質問を受けたので、昔習ったとおり「できない」と答えたのですが、「実はこの書き方でもコンパイルが通ってしまうのですが・・・」と追い打ちをかけられたので、その理由を調べてみたら意外な結末を迎えてしまったのでその顛末をメモ。
C 言語も日々進化しているので、自分の知識も定期的にアップデートしなければならないことを実感しました。

今回のサンプルプログラム

単純な例だと以下のようなコードでしょうか。

var.c

#include <stdio.h> int main(void){ int len = 5, i; int arr[len]; for(i = 0; i < len ; i++ ){ scanf("%d", &arr[i]); } for(i = 0; i < len ; i++ ){ printf("%d\n", arr[i]); } return 0; }

コンパイルしてみると・・・・

確かに gcc で何もオプションを付けないとコンパイルが通ってしまいます

$ gcc --version gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-39) Copyright (C) 2015 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. $ gcc var.c

おそらく言語仕様の問題だという勘所は掴めたので std と pedantic を付けて順番に試してみます。
そうすると、c99 や c11 ではエラーが出ず、c90 だとエラーが出ることが分かりました。

$ gcc --std=c90 -pedantic -Wall var.c var.c: In function ‘main’: var.c:4:2: warning: ISO C90 forbids variable length array ‘arr’ [-Wvla] int arr[len]; ^ $ gcc --std=c99 -pedantic -Wall var.c $ gcc --std=c11 -pedantic -Wall var.c

エラーメッセージにvariable length array と書いてあるんで、これ variable length array と呼ばれている機能なんですね。
それはともかく、c99 と 20 年前の仕様から許されていたことにびっくり
よく考えたら 20 年前は僕も大学生でしたので、そのときに習っていた C は c90 相当だったわけですね。

仕様書を調べて見る

というわけで、これは仕様に当たるしかないということで ISO の C 言語仕様を見比べてみることにします。

とりあえず、仕様書で配列の宣言( Array declarators )の該当部分を調べていきます。
まず C90 を見てみると、明確に constant expression と書かれていて、[ ] 内は定数しか許されていないことが分かります。

[C90] §6.5.4.2 Array declarators

1 The expression delimited by [ and ] (which specifies the size of an array) shall be an integral constant expression that has a value greater than zero.

これに対して C99 の方は整数型 ( integer type ) とは書いてありますが、constant という表現は無くなっており、文法的にも [ ] 内に許される式が constant-expression から assignment-expression に変更されていることが確認できました。通常の整数の演算は assignment-expression に含まれているので、このことから配列の宣言に変数を使うことができることが分かります。また、第4項の後半にサイズが定数でない場合には variable length array (可変長配列)になるという記述が追加されていました。

[C99] §6.7.5.2 Array declarators

1 In addition to optional type qualifiers and the keyword static, the [ and ] may delimit an expression or *. If they delimit an expression (which specifies the size of an array), the expression shall have an integer type. If the expression is a constant expression, it shall have a value greater than zero.
4 (略) If the size is an integer constant expression and the element type has a known constant size, the array type is not a variable length array type;otherwise, the array type is a variable length array type.

さらに、C11 は C99 と同様の定義になっているのですが、先ほどの第4項がちょっと変更されていて、なぜか conditional feature になっています。
要はオプションなので、使えるかどうかはコンパイラ依存ということで、微妙に地雷のような感じがするのは僕だけでしょうか。
Visual Studio でこの機能が使えないのはこの辺りにヒミツがありそうです。

ちなみに §6.10.8.3 には _ _STDC_NO_VLA_ _ というマクロが定義されていて、これが 1 の時は VLA はサポートされてないということになるようです。

[C11] §6.7.6.2 Array declarators

4 (略) If the size is an integer constant expression and the element type has a known constant size, the array type is not a variable length array type; otherwise, the array type is a variable length array type. (Variable length arrays are a conditional feature that implementations need not support; see 6.10.8.3.)

思った以上に根が深い問題でした。。。。。。


    トラックバックについて
    Trackback URL:
    お気軽にどうぞ。トラックバック前にポリシーをお読みください。[policy]
    このエントリへのTrackbackにはこのURLが必要です→https://blog.cles.jp/item/12021
    Trackbacks
    このエントリにトラックバックはありません
    Comments
    愛のあるツッコミをお気軽にどうぞ。[policy]
    古いエントリについてはコメント制御しているため、即時に反映されないことがあります。
    コメントはありません
    Comments Form

    OpenID を使ってログインすることができます。

    Identity URL: Yahoo! JAPAN IDでログイン