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

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

ただいまの
回答率

90.60%

  • C

    3569questions

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

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

解決済

回答 3

投稿

  • 評価
  • クリップ 0
  • VIEW 2,593

physics303

score 32

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

#include <stdio.h>
const double t1=1.0;
const double t2=2.0;

int main(void){

  return(0);

}

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

#include <stdio.h>
const double t1=1.0;
const double t2=2.0*t1;

int main(void){

  return(0);

}

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

icc でコンパイルした場合

error: expression must have a constant value
double t2=1.0*t1;

gcc でコンパイルした場合

error: initializer element is not constant

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

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 3

checkベストアンサー

+3

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

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

// (ファイルスコープ)
int gv1 = 10;        // OK: 値10は定数
const int gc1 = 20;  // OK: 値20は定数

int gv2 = gv1 * 2;        // NG: gv1は定数でない
const int gc2 = gc1 * 2;  // NG: gc1は定数でない

int main()
{ ...

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

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

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

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


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

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

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

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

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2017/09/08 15:58

    ありがとうございます。まず、ヘッダーではなく、ファイルスコープと呼ぶのですね。知りませんでした。
    グローバル変数の扱いは控えたいと思います。ただ、グローバル定数も使うのを控えるとなると、かなりソースコードが汚くなってしまうと思うのですが、みなさん、どのように対処しているのでしょうか?
    各関数内で同じ定数を毎度定義しているのでしょうか?個人的には#defineもバグの温床な印象があって使うのに抵抗があります。

    こうした問題はC++なら解決できそうなので、そろそろC++に鞍替えする時期なのかもしれません(私はプログラム初心者でCぐらいしかまともに書けません)

    キャンセル

  • 2017/09/08 19:04 編集

    定数(constant)であればグローバルに定義すべきと思います。というより、関数毎に逐一個別定義するのはそれこそバグの温床です。
    真に定数が必要ならば、C言語では マクロ定義(#define) しか正攻法がありません。(印象論はともかく)実際には使わざるを得ません。

    C言語の const キーワードは"定数"を意味するのではなく、"読取専用(read-only)"マーカと解釈した方がスッキリするかと思います。

    キャンセル

  • 2017/09/13 12:02

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

    キャンセル

+2

こんにちは。

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

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

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

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2017/09/08 11:49 編集

    ありがとうございます。
    似た質問があったのですね。サーヴェイ不足でした。

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

    キャンセル

  • 2017/09/08 12:48

    まず、初期化と代入は微妙に異なります。
    変数定義と一緒に値を設定するものは「初期化」です。double t1=1.0;など。
    定義が終わった変数に値を設定するのは「代入」です。double t1; t1=1.0;など。

    次に、ごめんなさい。グローバル変数(静的変数)の初期化は定数しかダメということです。
    仕組み的には、静的変数(グローバル変数やstaticなローカル変数)は、リンク時にメモリ領域が割り当てられ、その初期値もリンク時に初期化テーブルとして生成されます。そして、main()関数の前に走るstartupルーチンにて初期化テーブルが初期値のある静的変数領域へまとめてコピーされます。(この部分は処理系依存と思いますが、大差はないだろうと思います。)
    このような仕組みのため、静的変数の初期値はリンク時までに決定できないと処理できないのです。
    (リンク時までに決定できればよいのでリンク時定数と言った方が適切かもしれませんが、コンパイル時定数と表現されることがほとんどのようです。)

    非staticなローカル変数は、その関数の実行時に初期化されますから、変数や関数を使っても大丈夫です。実行時なら変数の値も確定できますし、関数呼び出しも当然可能ですから。

    キャンセル

  • 2017/09/08 16:01

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

    キャンセル

-1

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

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

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

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

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


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

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2017/09/08 11:15 編集

    ありがとうございます。さっそく、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さんの環境ではコンパイルエラーは生じませんか?

    キャンセル

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

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

関連した質問

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

  • C

    3569questions

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