質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.37%

  • C

    4832questions

    C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

c言語 配列の要素数

解決済

回答 5

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 3,026

mightyMask

score 70

静的領域やスタック領域に配列を確保する時、その要素数はコンパイル時に決められている必要があります。

個人的にはそういった用途でdefineマクロを使いたくないので、なるべくconst int といった定数で要素数を指定しています。

しかしこれだとコンパイルの時に、エラーになる時とならない時があるのですがどんな条件の元判断しているのでしょうか。

gcc version 5.3.0 (GCC)を使用しています。

なぜdefineマクロを使いたくないのかと聞かれるなら、なんとなく綺麗じゃない気がすると言えばそれまでですが、あえて理由をあげるなら、defineマクロだと数値以外も定義できてしまうし、識別子フィールドを汚さないためにundefしなくちゃいけないしといったところです。

追記

static const int N = 10;
int array[N];


これだとエラーになるようです。
同じような感じで書いた時、エラーが出なかった事が確かにあったのですが、再現する事が出来ませんでした。
申し訳ないです;;
仕事でプログラムを書いているわけではないので、バージョン管理ソフトなどは使ってないんです...
ですが、下記のような記述の場合、エラーは出ないようです。

static const int N = 10;
void func () {
    int array[N];
}

追記 該当コード

大学の先生が不正行為対策として、ネット上のソースコードと一致度合を調べて不正と判断されたら問答無用で単位を落とされてしまうのです。
大学でたまにゲームを作れみたいな課題が出されるのでいずれそれに使おうと思っているので、できれば具体的な該当コードを載せることは避けかったのです。

具体的には、メルセンヌツイスタというアルゴリズムを使った疑似乱数を実装しています。

上部4行だけソースコードを載せます。
これくらいなら不正対策には引っかからないはずです。

const static int N = 624;

static unsigned long X[N];
static int I = N+1;

エラーメッセージは以下です。

gcc test.c random.c
random.c:3:22: error: variably modified 'X' at file scope
 static unsigned long X[N];
                      ^
random.c:4:16: error: initializer element is not constant
 static int I = N+1;
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 5

checkベストアンサー

+5

>なるべくconst int といった定数で要素数を指定しています。

Cにおいてはこれは定数ではなくread onlyな変数にすぎないので、配列の要素数指定に使うのはVLAという別の機能になります。
VLAはstatic変数になれないので、自動変数領域に確保するときのみに利用できます(gccは独自拡張でVLAISとかいうクソ機能を持っているのでなんと構造体内にも宣言できる、うへぇ)。

つまりCにおいては

  1. むしろ配列の大きさは直書きして、sizeofで要素数を取る設計にする
  2. preprocessor macroないしenumで定数宣言して使う

ことになります。C++のconstexprがほしいでござる。

なお厳密な型が欲しいならマクロで

#include <stddef.h>
#define PROJECTNAME_CONSTANT_HOGE_SIZE ((size_t)(32))

みたいにするしかないでしょうね、C++と違ってenumの基底型指定できないし、constexprないし。

あるいはCではなくC++をつかう(つまりconstexprを使う)ことですかね。


追記見て

const static int N = 624;

static unsigned long X[N];
static int I = N+1;

うん、予想通りだ。もう一度言うけど、VLAはstatic変数にできない。回避策は上述の通り。これが一つ目のエラー。

2つ目のエラーは、static変数の初期化子は定数じゃないといけないけど、変数Nは上述の通り、定数ではないのでエラー。
"Initializer element is not constant" error for no reason in Linux GCC, compiling C - Stack Overflow

まあ

#ifdef _countof
#    define countof _countof
#else
#    define countof(arr) (sizeof(arr)/sizeof(*arr))
#endif

static unsigned long X[624];
static int I = countof(X)+1;

とかして使うのがいいんじゃないかな。こんな事程度にVLA使う意味がわからない。

それはそうと乱数生成にMT使いたいならそういうライブラリを使うべきかなと思います、MTの実装が課題じゃない限り。

あと、Nとかいうなんの大きさなのかわからない可読性のかけらもない名前を識別名に使うのはやめましょう。今回の場合は上述マクロ使ってcountof(X)としておくべし。同様にIとかいう謎な名前もどうにかするべし(constついていないのは意図したもの?)。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/06/12 13:18

    unsigned long の大きさは4byteで不変だと思うのですが違うのでしょうか?
    char や unsigned char は 1byteで不変。
    short や unsigned short は 2byte。
    long は 4byte。 long long は 8byte。
    int に関しては処理系によって違う。
    こんな認識をしてましたし、多少調べたところ合ってそうですが。
    最も高速の型との事ですが、「uint_fast32_t」 と 「unsigned long」 とでは速度面で違いが出る事などあるのでしょうか?

    キャンセル

  • 2017/06/12 13:34 編集

    short<=int<=long としか決まってません
    (intの最大値が少なくとも+32767というのもあるので、「しか」は語弊がありますね。)

    キャンセル

  • 2017/06/13 12:27

    ozwk氏の指摘通りで、C/C++はほとんどの型の大きさの保証がありません。そのためにC99/C++11でstdint.h/cstdintというヘッダが追加されました。
    また可読性の観点からも、型の大きさにある程度期待を持っているような今回の事例の場合、型の大きさの保証がある型を使うべきだと思います。

    キャンセル

+1

エラーになる時とならない時

の具体的なコードを見ていないので厳密には分かりかねますが、"配列の宣言部分で const
修飾のついた変数を要素数として指定している時、その該当行を示してエラーメッセージが吐かれる場合とそうでない場合がある"という質問である事を趣旨として回答します。

まずコンパイルがC99以降のモードで行われているのであれば、標準言語機能として Variable-length automatic arrays が許可されているため、配列の宣言の際に指定する要素数が、コンパイル時に決定している必要はありません。

Variable-length automatic arrays についての説明は、§6.7.5.2 "Array declarators" で述べられています。

If the size is an expression that is not an integer constant expression: if it occurs in a declaration at function prototype scope, it is treated as if it were replaced by *; otherwise, each time it is evaluated it shall have a value greater than zero. The size of each instance of a variable length array type does not change during its lifetime. Where a size expression is part of the operand of a sizeof operator and changing the value of the size expression would not affect the result of the operator, it is unspecified whether or not the size expression is evaluated.

もしそうでなければ(C99よりも前、C99を含まない、つまりC90以前、C90を含む)、GCCは独自拡張として、これを許可し、同動作をエミュレートします。この場合、標準規格に則った動作ではありません。GCCのオンラインドキュメントにて、これに関する言及がされています。コンパイル時に、オプションとして、-Wvlaもしくは-pedantic-errorsが指定されていた場合、標準規格が許可しないといった旨のメッセージが出力されます。

よって、前掲した前提の通り、該当行に関するエラーメッセージが出力される可能性として考えられるのは、

  • C90以前モードで-Wvlaもしくは-pedantic-errorsを有効にし以下のようなコードをコンパイルしている
int i=42;
const int s=i;
int ar[s];

当回答は、前掲した前提の基に成り立つ内容となっていますので、全く趣旨が異なる場合はその本質的な質問への回答として成り立たない可能性があります。可能であれば、該当コードと、そのエラーメッセージをご提示頂けると良いかと思います。


 追記

投稿のタイミングがすれ違ってしまいました。他の回答者様がおっしゃっている通り、Variable-length automatic arrays は静的領域にその機能がないため、エラーとなります。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

こんにちは。

個人的にはそういった用途でdefineマクロを使いたくないので、なるべくconst int といった定数で要素数を指定しています。

これは比較的一般的、かつ、好ましい方法です。(何かミスした時、コンパイル・エラーで検出されやすいとか、デバッガで値を表示できる等)

そして、文法通りに定義してエラーになる時はまず無いですので、エラーになる時の例を提示してみませんか?


【yumetodoさんの訂正を受けて追記】
ということで、グローバル変数(静的変数)はVLAがないので、グローバル変数に使った時エラーになりますね。

Visual C++はVLA非サポートなのでスタックの場合もエラーになります。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/06/10 22:04

    VLAってのはgccの拡張文法って事ですかね?
    ヒープから領域を取ってきてスコープを抜けたら開放するって処理をコンパイラが付け足してくれるっていうイメージで合ってますかね。

    キャンセル

  • 2017/06/10 22:24

    VLAはC99で定義されているので標準規格です。ですのでC言語用のgccのVLA対応はgcc拡張ではないと思います。
    しかし、C++の標準規格にVLAはありません。なのでC++におけるgccのVLA対応はgcc拡張です。

    gccのVLAはスタックに確保するようです。大きく取りすぎると直ぐにスタックオーバーフローがおきます。
    VLAISは良く知りませんが、yumetodoさんのリンクを見ると、スタックっぽいですね。

    キャンセル

  • 2017/06/10 23:35

    VLAはC99の標準機能、VLAISはgccの独自拡張です。
    またC++には一時期VLAを導入する動きがありましたが、結局rejectされていますからC++に於けるVLAはコンパイラの独自拡張です。

    キャンセル

0

以下のサイトにこんな記述があったので参考にどうぞ。
サンプルコードもあります。

[C] 定数の定義は,const intか,#defineか,それとも

どのコードを選ぶかは,その値を何に使いたいかによる.3番目の選択肢があることを忘れているよ.

static const int var = 5; /* (1) */
#define var 5             /* (2) */
enum { var = 5 };         /* (3) */

名前のつけ方は無視するとして,これらには次の違いがある.

  • そこからポインタを得る*1のなら,(1)を使わないといけない.
  • (2)は明らかに,そこからポインタを得ることができない.
  • (1)と(3)はデバッガのシンボルテーブル内でシンボルが作られる.ということでデバッグ作業がより容易になるデバッガに優しい.(2)ではシンボルが作られないので,デバッグ時に都合が悪い.
  • (1)はグローバルスコープの配列の次元として使うことができない.(2)と(3)ならできる.
  • (1)は関数スコープの静的配列の次元として使うことができない.(2)と(3)ならできる.
  • C99では,これらのいずれもローカルな配列(の次元)として使うことができる.技術的には,(1)を使うと可変長配列になるが,その次元はvarにより5に固定される.
  • (1)はswitch文なんかの定数として使うことができない.(2)と(3)ならできる.
  • (2)は,前処理の段階で望まないコードの変更ができてしまう.(1)と(3)ではそのような期待されない副作用は起こらない.
  • (2)では前処理の段階で値の設定がなされることを検出できる.(1)と(3)ではできない.

なので,多くの状況で,enumを使った(3)は,(1)や(2)よりも良い.別の面では,箇条書きの最初と最後の項目に注意をしたい.同時に両方を満たしたいのなら,よく考えないといけない.
C++だと,常に,static constの(1)を使えばいい.

サンプルコード

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

-1

答になってません。
すみません。

エラーになる場合とそうでない場合があるというのは興味深い話です。
エラーになる場合は、どんなエラーになるのでしょうか?

const int で定義した変数がまだコンパイラに認識されていないか、2重定義の2通りくらいしか思いつきません。

でも、これらのエラーなら原因は簡単にわかりますよね?
だから、何だろうなと思っています。

ついでに、私の場合ですが、

大半の場合は #define でもかまわないと思っていますが、厳密に型チェックをしたい場合は、const を使った方が良いのでしょうね。

コンパイルスイッチになっているような特別な場合でなければ、#undef しようと思ったことはありません。
1つのソースファイルのコンパイルが終われば、メモリは解放されると思うので、「識別子フィールドの汚れ」は気にしません。次のソースファイルのコンパイルへの悪影響は無いでしょう。

コンパイラにバグが無ければ、ですが、現在のCコンパイラにこんなバグがあるとは思えませんし。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.37%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る

  • C

    4832questions

    C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。