プロが教えるわが家の防犯対策術!

main.hに

static int a;

と記述し、main.cで

#include "main.h"
[省略]
a=10;

のように使用して、-Wallをつけてコンパイルすると、main.cで使用しているにも関わらず、

'a' defined but not used

という警告が表示されます。
同様に、関数においても、ヘッダファイルでstaticをつけると

‘~’ declared ‘static’ but never defined

と警告されます。

静的グローバル変数などは、ソースファイル内で宣言しなければいけないのでしょうか?ヘッダファイル内で宣言しても警告が出ないような方法はありますか?

A 回答 (9件)

静的なグローバル変数というのがそもそもおかしい気がしますが。

(もしかしたら、static を「静的」と直訳したのかな?)

そもそも、関数の外で定義されたグローバル変数は、性格的に静的です。
プログラムの実行時に生成されてその後プログラムの終了時までそのまま生存し続けますから。

関数の外で static 宣言された変数は「ファイルスコープ」を持ちます。
※一般的には、「ファイルスコープ」という呼び方はされますが、実際には、「コンパイル単位」からは見えるのは、No.2で指摘されているとおり。

なので、複数のファイルからアクセスするための変数=グローバル変数 に、static をつけることはまずありません。

さて、質問の内容から判断すると、

・最初、static をつけずに、ヘッダファイルでグローバル変数を定義しようとしたら、リンクで失敗した。
・この時点で、質問の、[省略]の部分で、グローバル変数を定義して、それが生き残っている。
・何かの拍子に、ヘッダファイルのグローバル変数に、static をつけてみたら、リンクも成功してしまった。

という流れが見える気がするのですが。
そもそも、Cにおけるグローバル変数は、「一カ所だけ宣言」「使うところですべて定義」という決まりなので、ちょっと管理がやっかいです。

こういう流れを仮定して、ヘッダファイルに普通にグローバル変数を定義するのに、よく使われていた方法は、

---- main.h ---

#if defined(_GLOBAL_HERE)
#define GLOBAL
#else
#define GLOBAL extern
#endif

GLOBAL int a;

----- ここまで ----

というヘッダファイルを作って、

main.c では、

#define _GLOBAL_HERE
#include "main.h"

その他のファイルでは、
#include "main.h"

で、main.c では、extern なし、それ以外のファイルでは、 extern つきの変数宣言に差し替えるという手法があります。

初期化を含む場合は、

---- main.h ---

#if defined(_GLOBAL_HERE)
#define GLOBAL
#define DEF(x) = (x)
#else
#define GLOBAL extern
#define DEF(x)
#endif

GLOBAL int a;
GLOBAL int b DEF(1);

----- ここまで ----

とか。
(ただし、この DEF() は万能ではないです)

あと、関数で static をつけた場合も、ファイルスコープ(こちらも、実際には、コンパイル単位内スコープ)となりますから、コンパイル単位の中の関数しかアクセスできません。そこで、コンパイル単位の中に、関数の実体がない場合に、質問されたような警告が出たのだろうと思います。
    • good
    • 0

No.3 訂正です。



回答の中で使っている、

_GLOBAL_HERE

と、_ で始まるマクロは、よろしくないようです。
ということで、この部分、たとえば GLOBAL_HERE などにしてください。
訂正します。

さて、ご指摘がありまして、ありがとうございました。
そこまで、明文化されているとは知りませんでした。

失礼しました。
    • good
    • 0

>main.cで使用しているにも関わらず


>'a' defined but not used
main関数のなかにローカル変数としてaが存在してたりするのでは?


>静的グローバル変数などは、ソースファイル内で宣言しなければいけないのでしょうか?
そもそも、変数や、関数の実体(定義)をヘッダに書くべきではない。
「定義」と「宣言」がごっちゃになっていないでしょうか?


>ヘッダファイル内で宣言しても警告が出ないような方法はありますか?
ヘッダファイルには宣言を行い、ソースファイルに定義を書く。
---main.h---
extern int a;
---main.c---
int a;
------------
他の方も書かれていますが、そもそもグローバル変数は静的記憶期間を持つので、
staticをつける意味はさほどありませんし、明示的に使用するにしても、
その使用はソースファイル内で限定されて、行われるべきものです。


蛇足ですが。。。
>あと、_ + 大文字は、Cの定義済みマクロは、__ (_ 2個)で始まるからOKなんじゃ……
>と思ったら、ベンダー独自の定義済みマクロを、_ + 大文字だったりするのですね。
ベンダーではなくて、C言語の規格上で処理系における、
予約済み識別子の定義が許可されているからで、
問題なのは、予約済み識別子をマクロとして定義した場合は、
動作が未定義とされているところです。

予約済み識別子の定義として以下が
規定されています。
以下、X3010抜粋

予約済み識別子

・下線に続き大文字1文字または、下線に続きもう一つ下線で始まるすべての識別子は、
 いかなる使用に対しても、常に予約済みとする。

・一つの下線で始まるすべての識別子は、通常の名前空間およびタグ名前空間の双方における
 ファイル有効範囲をもつ識別子としての使用に対して、常に予約済みとする。

・#他にもいくつかありますが、今回は割愛。

予約済み識別子以外でプログラムが、識別子を宣言・定義し、その文脈において
識別子が予約済みである場合または、予約済みの識別子をマクロとして定義した場合、
その動作は未定義とする。

#まぁ、被らないような名前にして置けば、実害はほとんどありませんが:p
    • good
    • 0

No.3です。



呼ばれたような気がしたので……。

「定義」と「宣言」が逆でした。

・一カ所だけ「定義」(実体があるもの)
・使うところでは、「宣言」(名前だけで実体のないもの)

でした。

あと、_ + 大文字は、Cの定義済みマクロは、__ (_ 2個)で始まるからOKなんじゃ……と思ったら、ベンダー独自の定義済みマクロを、_ + 大文字だったりするのですね。

もともとの気持ちは、「当社独自マクロ」だったのですが、少なくとも無条件にお勧めできる書き方ではないです。
    • good
    • 0

ああそうだ, #2 では「プログラムを」って書いたけど, エラーメッセージも「正確に」書いてほしい.


余談ですが
Cにおけるグローバル変数は、「一カ所だけ宣言」「使うところですべて定義」という決まり
というのは「宣言」と「定義」が逆ではないかと>#3. あと, 「_+大文字英字」で始まる識別子もちょっと危険.

まあいずれにしてもソースファイル (とエラーメッセージ) が出てこないことには「群盲象をなでる」なわけですが. 特にこの場合には「[省略]」とされている部分が完全にわからないことにはそもそもプログラムとして成り立たないので, そんな状況では回答者がめいめいに勝手に想像するしかない.
    • good
    • 0

こんにちは。


aを使用しないmain.c以外のソースでも、main.hをインクルードしているためと思われます。
例えば、main.c以外に"sub.c"というソースがあり、この2つをコンパイルしリンクする事になっているとします。

cc -Wall main.c sub.c

などとしてビルドすると、main.cの方はワーニングなしですが、sub.cでは恐らくaを使用していないため、コンパイル時に引っかかるのではないでしょうか。
いやいやmain.cだけだよと言うのなら、もう何も言う事はありませんが・・・。
    • good
    • 0

> ヘッダファイル内で宣言しても警告が出ないような方法はありますか?



警告が出ないように、変数を使用すればいいだけです。

例えば、main.cとtest.cでmain.hをインクルードしているとすると、aという変数はmain.cとtest.cの両方に存在します。そして、これらは同じものではありません。
よって、main.cでaを使用していても、test.cの方で使用していなければ、警告が出ます。

ヘッダーに限らず、#includeでインクルードした場合、そのファイル内の記述はソースに直接書かれているのと同じ効果があります。なので、main.hをmain.cとtest.cでインクルードすると、

[main.c]

static int a;

...

[test.c]

static int a;

...

という記述と同じと解釈されます。同じ変数名でも、関数が違えば別物として扱われるように、aもソースが違えば別物として扱われてしまいます。
そのため、通常はヘッダーにはexternで記述しておき、変数の実体は1つのソースで宣言することが多いのです。externであれば、変数自体は作成されず、どこかにそのように変数があるものとしてコンパイルされます。リンク時に実際の変数と結びつくため、複数のソースで同じ変数が参照できます。
    • good
    • 0

そんなあほな規定はない>#1.



これだけでは原因を判断できないので, プログラム全体を見せてください.

もっとも, 「static な変数や関数」をヘッダで宣言 (and/or 定義) する必要性は思いつかないのだが.
    • good
    • 0

"static"を利用しているためです。


"static" は,関数内で使用される場合,同じ関数が実行される限り同一のメモリに割り当てられるですが,
関数外で使用される場合は,同一ファイル上からのみ参照できるという意味になります。

main.hに"static int a;"としているので,"a"はmain.h内でのみ参照できることになります。
つまり,main.cからはアクセス出来ません。
main.hで宣言をしたい場合は,"int a;"としましょう。
どうしても,"static"を使用したい場合は,
main.cのなかに記述して下さい。
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!

このQ&Aを見た人はこんなQ&Aも見ています