m_nukazawaの日記: C言語のstatic関数がファイルスコープになる理由を教えて! 10
日記 by
m_nukazawa
ください。
C言語で関数にstaticを付けると、関数のスコープが"ファイルスコープ"になって、スコープ外で関数名が重複しても問題が起こらない、という話について。
staticの意味が、関数と変数のどちらに付けるかによって違う。
何かご存知の方がいらっしゃれば、教えていただければ幸いです。
歴史的経緯が特に知りたいです。
解説サイトなどでは、
『関数に付いたstaticはlocalだと思え』
という、C言語のお約束だ、呪文だと思ってください、天と地があり、太陽は東から登って西から沈むものだ、という説明しか見当たりません。
まるで、イントメインとインクルド・スタドアイオーもそうだったじゃないですか、変数とは箱です。ポインタとは箱の箱で...という、ありがちなC言語初心者向け解説のような。
以下妄想。
変数のstatic = 実行中は値を保持し続けるところから。
staticな変数や関数にはグローバルなメモリ位置が割り当てられる。メモリ位置が決まっているstatic関数はリンク時に関数名から呼び出したい関数を探す必要がない、よって同名の関数が複数あっても混同することがそもそも無い。...といったあたりが初期のCコンパイラの挙動で、static関数がファイルスコープになるというのはその時代からの互換性による名残り、という説はどうでしょう。
そういえばファイルスコープって、Cソースとオブジェクトファイル、どちらの"ファイル"なんだろう?
アドレスの解決をコンパイル時に行うからです (スコア:2, 参考になる)
変数も関数もそこは同じです
Re:アドレスの解決をコンパイル時に行うからです (スコア:1)
だから、コンパイル単位(オブジェクトファイル)毎になります。
static だけであれば、名前はすべて実値に替わり、オブジェクトファイルの中から消滅します。
デバッグ情報としては、残るかもしれませんが。
Ritchie のインタビューがありました (スコア:2)
http://www.gotw.ca/publications/c_family_interview.htm [www.gotw.ca]
ユーザーの圧力が原因だそうで…。
> そういえばファイルスコープって、Cソースとオブジェクトファイル、どちらの"ファイル"なんだろう?
インタビューの通り linkage 単位ですね。
Cソース単位にならない例として、
include されるファイルで static な変数を定義して include する側で参照したりできます。
Re:Ritchie のインタビューがありました (スコア:2)
とりあえず、現在意味と単語の合っていない'static'が存在する理由は、ユーザによる過去互換への要望があったから、で間違いなさそうですね。
ソースのインタビュー、メンバが豪華で面白そう。
auto関数 (スコア:1)
オフトピですが、autoな関数とか出来るようにしてたら今時世の中変わってたかも(^^;
今やるとデータ実行になってしまいますが(^^;
ラベルのポインターが取れて
lb100:
void *aaa = &lb100;
goto *aaa;
とか出来てると今頃C言語は捨てられてたかも(^^;
Re:auto関数 (スコア:1)
>ラベルのポインターが取れて
gccやsunは取れます。
https://docs.oracle.com/cd/E19205-01/821-0386/bjabt/index.html [oracle.com]
# なんで、こんなのがあるのか不明です
notice : I ignore an anonymous contribution.
staticの意味の混乱 (スコア:0)
Cのstaticの意味に混乱があるのは規格化されるまえの既存プログラム
をなるべく救おうとしたからじゃないでしょうか。
関数がファイルスコープを意味するというのは、むかしの規格票しか
いまは手元にないのでいまのはちがっているかもしれませんが、
6.2.2 識別子の結合で決まっています。staticがある場合は内部結合
になります。関数は外部定義(6.9)にあたりますが、これは外部定義
のオブジェクト(変数)とおなじです。
ついでですが、変数がちがうと言われるのは記憶期間の指定となる
staticのことを言われているのだとおもいますが、これは6.2.4で決
まってます。たしかにべつの話です。
Cはその動く仕組みが丸見え的なところがあって、もともとの
staticやexternの仕組みはリンカの仕組みそのままという感じです。
リンカの仕様から考えると、staticな変数は.dataセグメントとかに割当
たる固定領域のメモリ(つまり静的記憶期間)で、そのシンボル情報が
エクスポートされるか(つまり外部結合か内部結合か)のコントロールは
extern宣言として個別に制御できると自然なのですが、規格化されるとき
におそらく既存の仕様をまとめてこうなったのでしょう。
まあ、外部定義なら静的記憶期間かつ外部結合または内部結合かしかない
のだから、問題ないわけです。
また、いわゆるファイルスコープは外部定義の内部結合でしょう。
これは翻訳単位です。
Re:staticの意味の混乱 (スコア:1)
staticは予約語にしてたけどlocalやprivateはそうじゃなかったので使ってしまった既存のソースの事を考えて新たに識別子に出来なかったような気がする(^^;
気にする人は
#define local static
#define private static
とかしてね(^^;
という積もりだったのかも。
Re: (スコア:0)
C言語をalgol風の再帰呼び出し可能な言語にすることを決めた時、呼び出し時に関数内で自動で確保される変数と静的に確保される変数を区別する必要が発生しましたが、
大域変数は最初から静的に確保した領域に割り付けるつもりだったと思いますよ。
Cのストレージクラス指定子は記憶場所(.dataセグメントやスタック等)を指定するための物とスコープを制御する物が入り混じっています。
変数のインスタンスを同一にするために外部参照の宣言としてexternが必要ですが、大域変数では記憶場所を示すストレージクラスの指定子としてのstaticは必要なかったはずです。
ではどうしてstaticを内部結合を示す指定子に使用したかということですが、あまり深い理由があった訳でもないんじゃないでしょうか。
昔のことなので、キーワードが増えると字句解析が遅くなるとか考えて、既存のキーワードを使いまわしたとか・・・
Re: (スコア:0)
Cはstaticとして定義されたグローバル変数をextern宣言
できます。これは内部結合になります。
単純にexternが外部参照の宣言になるわけではありません。
自然でないとはこういうことです。
おっしゃる通りグローバルは静的記憶期間しか持ちようが
ありません。じゃあexternだけでいいじゃんというのが
リンカの仕様とかを知っていれば自然ですが、
そうなってません。と、言いたかった。
で、少なくとも規格化時点では、考えていないわけではなく、
そうする理由があったとPlaugerが書いた記事を読んだ気が
するのですが、20年以上前の雑誌記事なので探しきれません。