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

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

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

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

Q&A

解決済

3回答

10103閲覧

グローバル変数にヘッダー内で代入する場合に生じるコンパイルエラー

physics303

総合スコア89

C

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

0グッド

0クリップ

投稿2017/09/08 01:41

C言語ユーザーです。初心者です。
プログラムを通して(すなわち異なる関数間で)同一の変数を用いる際にはヘッダー部分にグローバル変数を定義するのが一般的だと思います。すなわち、

C

1#include <stdio.h> 2const double t1=1.0; 3const double t2=2.0; 4 5int main(void){ 6 7 return(0); 8 9} 10

とすれば、t1,t2がこのファイルの中であればどこでも使えますね。しかし、

C

1#include <stdio.h> 2const double t1=1.0; 3const double t2=2.0*t1; 4 5int main(void){ 6 7 return(0); 8 9} 10

というように、グローバル変数に値を代入する際に、他のグローバル変数を用いると次のようなコンパイルエラーが生じます。

icc でコンパイルした場合

error: expression must have a constant value

double t2=1.0*t1;

gcc でコンパイルした場合

error: initializer element is not constant

これはなぜでしょうか。また、これを解決するためにはどうしたらよいでしょうか。ヘッダー部分で複数の変数を定義しておきたい私にとって、ヘッダー部分でグローバル変数をグローバル変数に代入できないというのはとてもつらいです。

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

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

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

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

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

guest

回答3

0

ベストアンサー

グローバル変数に値を代入する際に、他のグローバル変数を用いると次のようなコンパイルエラーが生じます。

プログラミングC言語の制限(仕様)です。Chironianさん回答にあるように、C言語では「グローバル変数(const付きも含む)の初期化には"定数"しか利用できません」。またC言語では「const付きで定義された変数は"定数"ではありません」。後者はC++言語ならば"定数"として扱われます。

c

1// (ファイルスコープ) 2int gv1 = 10; // OK: 値10は定数 3const int gc1 = 20; // OK: 値20は定数 4 5int gv2 = gv1 * 2; // NG: gv1は定数でない 6const int gc2 = gc1 * 2; // NG: gc1は定数でない 7 8int main() 9{ ...

また、これを解決するためにはどうしたらよいでしょうか。

下記の選択肢があります:

    1. マクロ定義(#define t1 1.0, #define t2 (2.0*t2))に変更する。ただし、マクロ定義名は大文字(T1T2)としておくのが一般的です。また短すぎるマクロ名も別のトラブルの元になりますから、その定数の意味を表す明快な名前に変更すべきです。
    1. "enumハック"とよばれる回避策を利用する。ただし、今回のように浮動小数点数には適用できません。例: enum{SIZE = 10}; int arr[SIZE];のように書けます。
    1. C言語ソースコードを、C++言語としてコンパイルする。いわゆる"Better C"としてのC++の利用です。

いずれの方式もあまりオススメできるものではありませんが、情報の提供として。


以下は本題からはずれた"おまけ"です:

プログラムを通して(すなわち異なる関数間で)同一の変数を用いる際にはヘッダー部分にグローバル変数を定義するのが一般的だと思います。

ヘッダー部分="C言語ソースコードの冒頭部分で、特定の関数に属さない領域" でしょうか?この位置はファイル・スコープ(file scope)と呼ばれ、ここで宣言された変数はいわゆる「グローバル変数」になります。

非常に小規模なプログラムや使い捨てのプログラムでなければ、可能な限り「グローバル変数」の利用は避けた方が良いです。値が一定の「グローバル定数」であればトラブルの元になりにくいですが、プログラム実行中に値が変化しうるグローバル変数は将来的なバグの温床になります。絶対に。

投稿2017/09/08 04:40

編集2017/09/08 04:49
yohhoy

総合スコア6189

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

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

physics303

2017/09/08 06:58

ありがとうございます。まず、ヘッダーではなく、ファイルスコープと呼ぶのですね。知りませんでした。 グローバル変数の扱いは控えたいと思います。ただ、グローバル定数も使うのを控えるとなると、かなりソースコードが汚くなってしまうと思うのですが、みなさん、どのように対処しているのでしょうか? 各関数内で同じ定数を毎度定義しているのでしょうか?個人的には#defineもバグの温床な印象があって使うのに抵抗があります。 こうした問題はC++なら解決できそうなので、そろそろC++に鞍替えする時期なのかもしれません(私はプログラム初心者でCぐらいしかまともに書けません)
yohhoy

2017/09/08 10:07 編集

定数(constant)であればグローバルに定義すべきと思います。というより、関数毎に逐一個別定義するのはそれこそバグの温床です。 真に定数が必要ならば、C言語では マクロ定義(#define) しか正攻法がありません。(印象論はともかく)実際には使わざるを得ません。 C言語の const キーワードは"定数"を意味するのではなく、"読取専用(read-only)"マーカと解釈した方がスッキリするかと思います。
physics303

2017/09/13 03:02

ご丁寧にありがとうございました。変数、代入、初期化、そのあたりの概念が整理できました。
guest

0

こんにちは。

つい先日も似た質問がありましたが、C言語は初期化子には定数式しか書けないようです。

double t1=1.0;の初期化子である1.0はコンパイル時に値が確定する定数式ですので可能です。
double t2=2.0*t1;の場合、t1は変数ですから、コンパイラは変数として取り扱うため、2.0*t1はコンパイル時には値が確定しないと判断するので初期化子として使えません。

C言語の場合はリンク先のotnさんの回答のように例えばmain()関数の頭で設定するしかないです。
C++の場合は初期化子に定数ではない変数を含んだり関数を呼び出したりするような通常の式を書くことができますのでそのままでも通るだろうと思います。

投稿2017/09/08 02:37

Chironian

総合スコア23272

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

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

physics303

2017/09/08 02:50 編集

ありがとうございます。 似た質問があったのですね。サーヴェイ不足でした。 なるほど。つまり、グローバル変数か否かには関わらず、変数を定義すると同時に値を代入する際には右辺は必ず定数でなければならず、右辺は他の変数を含む式ではダメだということですね。この理解であっていますでしょうか。
Chironian

2017/09/08 03:48

まず、初期化と代入は微妙に異なります。 変数定義と一緒に値を設定するものは「初期化」です。double t1=1.0;など。 定義が終わった変数に値を設定するのは「代入」です。double t1; t1=1.0;など。 次に、ごめんなさい。グローバル変数(静的変数)の初期化は定数しかダメということです。 仕組み的には、静的変数(グローバル変数やstaticなローカル変数)は、リンク時にメモリ領域が割り当てられ、その初期値もリンク時に初期化テーブルとして生成されます。そして、main()関数の前に走るstartupルーチンにて初期化テーブルが初期値のある静的変数領域へまとめてコピーされます。(この部分は処理系依存と思いますが、大差はないだろうと思います。) このような仕組みのため、静的変数の初期値はリンク時までに決定できないと処理できないのです。 (リンク時までに決定できればよいのでリンク時定数と言った方が適切かもしれませんが、コンパイル時定数と表現されることがほとんどのようです。) 非staticなローカル変数は、その関数の実行時に初期化されますから、変数や関数を使っても大丈夫です。実行時なら変数の値も確定できますし、関数呼び出しも当然可能ですから。
physics303

2017/09/08 07:01

ご丁寧にありがとうございます。代入と初期化の違いを認識できました。 どうしてグローバル変数の初期化は定数じゃないといけないのかは、静的関数がメモリでどのように保存されるのかに関係しているのですね。 くわしくありがとうございました。
guest

0

定数(constant)は、変数ではありません(誤解です)。

定数はプログラムの実行中に値が変わりませんから、コンパイル時にコンパイラが値をコード中に設定します。変数はプログラムの実行時に値の初期化や変更が行われるので、コンパイラは必要なメモリ領域の情報などを出力するのみです。

コンパイラはコードの評価(実行)をしませんから、"double t2=2.0*t1;"の行だけをみて「定数ではない(t1の値を知らない)」というエラーになります。

質問の最後に書かれている通り、グローバル変数にグローバル変数を代入したいのなら、定数ではなく定数を使ってください。

double t1=1.0; double t2=2.0*t1;

なら、問題ないはずです。

投稿2017/09/08 02:10

coco_bauer

総合スコア6915

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

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

physics303

2017/09/08 02:16 編集

ありがとうございます。さっそく、constを削除して #include <stdio.h> double t1=1.0; double t2=2.0*t1; int main(void){ return(0); } とプログラムを書き換え実行しましたがやはりコンパイルエラーが生じます。エラーの内容は次の通りです。 icc の場合  error: expression must have a constant value double t2=2.0*t1; gcc の場合 error: initializer element is not constant 私の環境が問題なのでしょうか。coco_bauerさんの環境ではコンパイルエラーは生じませんか?
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問