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

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

新規登録して質問してみよう
ただいま回答率
85.47%
C

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

Q&A

解決済

5回答

10680閲覧

c言語 配列の要素数

mightyMask

総合スコア143

C

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

0グッド

1クリップ

投稿2017/06/10 08:19

編集2017/06/10 10:13

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

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

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

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

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

#追記

c

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

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

c

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

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

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

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

c

1const static int N = 624; 2 3static unsigned long X[N]; 4static 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;

気になる質問をクリップする

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答5

0

ベストアンサー

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

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

つまりCにおいては

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

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

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

c

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

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

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


追記見て

c

1const static int N = 624; 2 3static unsigned long X[N]; 4static 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

まあ

c

1#ifdef _countof 2# define countof _countof 3#else 4# define countof(arr) (sizeof(arr)/sizeof(*arr)) 5#endif 6 7static unsigned long X[624]; 8static int I = countof(X)+1;

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

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

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

投稿2017/06/10 09:01

編集2017/06/10 10:42
yumetodo

総合スコア5850

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

mightyMask

2017/06/10 12:51

確かに「N」「X」「I」は可読性が悪いですね。 http://random.ism.ac.jp/info01/random_number_generation/node6.html ここのサイトで紹介されている数式の「n」「x」から名付けていました。 「I」はカウンタ変数として使用しています。 数式をプログラムに起こす時は普通はその数式を知らないと解読不能というかデバッグができないので、参考URLだけコメントして1文字の変数も普段から許容してしまっていました。 どうするのが普通なんでしょうか。「メルセンヌツイスタ」より「MT_N」とでもしたほうが良いですかね。 countof(X)というのはいろいろ使い勝手が良い場合もあるかもしれませんが、この場合「N」は配列のサイズという意味合いというより定数の1つという意味合いの方が合っているのでここでは違う方法をとる事にします。
yumetodo

2017/06/10 15:14 編集

うーん。まずX, N, Iだとさすがにわかりにくいので、MTの状態を保存していることが伝わる名前が望ましいですね。 で本家のコード見に行ったらそれぞれmt, N, mtiだったのでなんだかな、という思い。 一応NはC++標準ライブラリだと https://cpprefjp.github.io/reference/random/mersenne_twister_engine.html state_sizeとなっているのでそれっぽい名前にするのが良さそう。 XとIは・・・gccのstd::mersenne_twister_engineの実装見に行ったら、_M_x, _M_pであんま変わんねーなと言う感(_M_で始まるのはgccの実装おなじみprivateメンバ変数の命名に固有のもの)。 まあiよりは_M_pのほうが今見ている状態の位置感がでていいのか・・・?(錯乱 (_から始まる識別名は予約されているので自分では使わないように)
yumetodo

2017/06/10 15:19 編集

まあこうして結局Cだとマクロになっちゃんですね。constexprください。標準化委員会の連中はクラスの出来損ないを提案する暇があったらconstexprをですね(以下略 ああ、ところで状態を保持するテーブルの型ですが、見た感じmt19937_32を作りたいように思うので、unsigned longとかいう大きさ不定の型を使うんじゃなくて、stdint.hをincludeしてuint_fast32_tを採用するべきかと思います(最低32bitのunsignedな整数型のうち最も高速な型)。 それから今見ている状態の位置を表す変数の型はunsignedなもの(size_tとか)であるべきかと思います。 http://qiita.com/yumetodo/items/4ea151e15b5e540cfef5
mightyMask

2017/06/12 04:18

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

2017/06/12 04:41 編集

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

2017/06/13 03:27

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

0

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

の具体的なコードを見ていないので厳密には分かりかねますが、"配列の宣言部分で 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を有効にし以下のようなコードをコンパイルしている

cpp

1int i=42; 2const int s=i; 3int ar[s];

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


追記

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

投稿2017/06/10 09:48

編集2017/06/10 10:04
kjfkhfhgx

総合スコア48

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

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

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

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

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

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

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

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

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

投稿2017/06/10 09:09

Tetsu.S

総合スコア25

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

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

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

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

c

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)を使えばいい. [サンプルコード](https://gist.github.com/takehiko/75522b72132d53d486d6)

投稿2017/06/10 09:06

退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

こんにちは。

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

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

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


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

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

投稿2017/06/10 08:54

編集2017/06/10 09:10
Chironian

総合スコア23272

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yumetodo

2017/06/10 09:02

>これは比較的一般的、かつ、好ましい方法です もしC++だったらそうだろうけど。
Chironian

2017/06/10 09:07

yumetodoさん。 そう言えば、const int 云々を使い始めたのはC++でした。 同時にC言語もよく使っていたので混乱していたようです。 訂正ありがとう。
mightyMask

2017/06/10 13:04

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

2017/06/10 13:24

VLAはC99で定義されているので標準規格です。ですのでC言語用のgccのVLA対応はgcc拡張ではないと思います。 しかし、C++の標準規格にVLAはありません。なのでC++におけるgccのVLA対応はgcc拡張です。 gccのVLAはスタックに確保するようです。大きく取りすぎると直ぐにスタックオーバーフローがおきます。 VLAISは良く知りませんが、yumetodoさんのリンクを見ると、スタックっぽいですね。
yumetodo

2017/06/10 14:35

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.47%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問