• ベストアンサー

includeが必須ではない理由

C言語で printf 等を使用する場合 include は必須だと色々なところで書かれているのですが、以下の構文で警告は出ているものの正確な結果が出力されてしまいました。 //#include <stdio.h> int main(){ int i = 0; scanf( "%d", &i ); printf("%d, %f, %s\n", i, 0.1, "STR"); return 0; } 実行結果 入力値:i = 1 1, 0.100000, STR 警告:関数 'scanf' は定義されていません。int 型の値を返す外部関数と見なします。 警告:関数 'printf' は定義されていません。int 型の値を返す外部関数と見なします。 これはなぜでしょうか。 VisualStudio2008を使用しています。

質問者が選んだベストアンサー

  • ベストアンサー
回答No.2

Cの古い標準規格であるISO/IEC 9899:1990の国内一致規格であるJIS X3010-1993の 6.3.2.2 関数呼び出し には,次のような一文があります。 > 関数呼び出しの括弧で囲まれた実引数並びの前の式が識別子だけからなり,この識別子に対して宣言が可視でない場合,その識別子は,関数呼出しを含む最も内側のブロックに,次の宣言が現れたかのように暗黙に宣言される。 > extern int 識別子( ); 今回の場合は,scanfおよびprintfは宣言が可視ではないので, extern int scanf(); extern int printf(); が,main関数の先頭に記述されたかのように取り扱われます。 この宣言は, int scanf(const char *, ...); と, int printf(const char *, ...); に対して正しい引数であれば,少なくともVisual C++のCコンパイラは同じ様に振る舞います。 このため,提示されたプログラムはうまく動きます。 ただし,ISO/IEC 9899:1999には上記に相当する規定はなく,また規格に含まれない部分ではあるもののForewordには, > Major changes from the previous edition include: (略) > - remove implicit function declaration とあります。 また,C++の標準規格であるISO/IEC 14882:2003においても,上記に相当する規定はなく,やはり規格に含まれない部分ではあるもののAnnex.C Compatibility - C.1 C++ and ISO C - C.1.3 Clause 5: expressions - 5.2.2 に, > Change: Implicit declaration of functions is not allowed > Rationale: The type-safe nature of C ++. という記述があります。 ので,この「暗黙の関数宣言」には頼らずにプログラムを書く事をお勧めします。

ShimantoGa
質問者

お礼

ご回答ありがとうございます。 >暗黙の関数宣言 標準関数で、かつ宣言が可視でない場合、暗黙で exturn というものを利用して宣言している。 それにより、どこかのソースから int printf(const char *, ...); を探し出して実行しようとした。 という理解でよろしいでしょうか。

その他の回答 (7)

  • TERABIT
  • ベストアンサー率44% (4/9)
回答No.8

scanf や、printf 等の、可変長引数の関数は、引数の受け渡し方が変更されることがあり、 宣言が無いと、コンパイラによっては正常な動作が行われない可能性がありますので、注意した方がいいです。 まあ、現状、ごく一部のコンパイラだけのようなので、普通は大丈夫でしょうが。 ANSI C89 からの仕様です。 ANSI C99 は、宣言が無いとエラーになるんだったっけ? C++ だけだったか?(忘れた(^^;)

ShimantoGa
質問者

お礼

ご回答ありがとうございます。 何のコンパイラを使用しているか、何てことはあまり意識したくない所です。 やはり明示的に宣言するのが一番のようですね。(他者が解析する時も読みやすいでしょうし)

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.7

いや, その 「ライブラリで printf が定義されており、ヘッダで printf が宣言されている。」 という考え方が正しいんです. だから, この警告メッセージの「定義」が間違ってる (「宣言されていません」じゃないとおかしい), と. なお, 「正しく宣言できれば #include しなくていい」というのは, 逆に「正しく宣言できなければ #include しなければならない」という意味でもあります. たとえば fprintf を宣言するためには FILE が宣言されていなければならないので, 結局 stdio.h を #include しなければならない」ということになります... でも, こんなことを考えるくらいなら最初から #include した方がきっと楽. あと #3 に絡みますが, #2 でいわれるように「関数の宣言がなければ勝手に int を返す関数だとみなす」というルールがありました. これは標準ライブラリ関数に限らず適用されるので, 自分で int hoge() { ... } という関数を作って置いた場合, 別ファイルから hoge を宣言せずに使うことができます (もちろんリンクするときに適切なオブジェクトファイルも指定しないとこけますが). このことと「ポインタと int は同じだった」というご都合主義との相互作用で, ふる~い (ANSI 以前の) Cコンパイラについているヘッダでは十分な数の関数を宣言していなかったりします.

ShimantoGa
質問者

お礼

>この警告メッセージの「定義」が間違ってる (「宣言されていません」じゃないとおかしい 理解が足らず申し訳ありません。 確かに宣言がないため exturn int ~ で自動的に宣言するのですから「定義がない」というのは誤解を招く警告メッセージかもしれませんね。 >標準ライブラリ関数に限らず適用される test1.c ---------- #include "stdio.h" //可視宣言がない場合、暗黙的に宣言される //exturn int hoge(); int main(){ int c = hoge(); printf( "%d", c ); } test2.c ---------- int hoge(); int hoge(){ int a = 10; return a; } ------------------- 実行結果:10 ------------------- <関数呼び出しの括弧で囲まれた実引数並びの前の式が識別子だけからなり,この識別子に対して宣言が可視でない場合,その識別子は,関数呼出しを含む最も内側のブロックに,次の宣言が現れたかのように暗黙に宣言される。> 確かに「標準関数」のみとはどこにも書いておりませんでしたが、こんなこともできるんですね。 >十分な数の関数を宣言していなかったりします. 「ポインタと int は同じだった」というのはよくわかりませんが、宣言をしない記述が普通だった頃があったということでしょうか。 再度のご回答誠にありがとうございました、大変勉強になります。

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.6

念のため書いておくと, #include がなくても「きちんと正しく宣言できる」ならプログラムとしては問題ありません. 例えば int printf(const char *, ...); int scanf(const char *, ...); と書いてあれば #include はなくても OK. とはいえ「正しい宣言」を頑張って書くよりも「その関数を宣言しているヘッダファイル」を #include した方が安全かつ確実なので #include してください. でも, これ警告の文章は間違ってるよね. 今まで printf や scanf が「定義されている」プログラムって見たことないんだけど. もちろん「(#include 経由で) 宣言している」プログラムならごく普通.

ShimantoGa
質問者

お礼

ご回答ありがとうございます。 >念のため書いておくと, #include がなくても「きちんと正しく宣言できる」ならプログラムとしては問題ありません なるほど、 include せず自分で宣言してしまうという方法もあるのですね。(あまりやらないでしょうが) >でも, これ警告の文章は間違ってるよね. 今まで printf や scanf が「定義されている」プログラムって見たことないんだけど. もちろん「(#include 経由で) 宣言している」プログラムならごく普通. 間違っているのですか、気づきませんでした。 ライブラリで printf が定義されており、ヘッダで printf が宣言されている。 という考え方をしていました。

  • php504
  • ベストアンサー率42% (926/2160)
回答No.5

>stdio.h を include しなければ、printf や scanf という関数は存在すらしないと考えております。 関数が存在するのはヘッダではなくライブラリです コンパイルは外部参照が未解決のまま行われリンク時にライブラリ内を探して外部参照が解決されます 標準関数ライブラリはヘッダの有無にかかわらず勝手にリンクされるのでリンクエラーにはなりません 一般にヘッダをincludeしてライブラリをリンクしないときに外部参照が未解決となります

ShimantoGa
質問者

お礼

ご回答ありがとうございます。 >関数が存在するのはヘッダではなくライブラリです 私の勘違いです、大変失礼しました。 >コンパイルは外部参照が未解決のまま行われリンク時にライブラリ内を探して外部参照が解決されます つまり、正確にはコンパイルエラーではなくリンクエラーということですね。 >標準関数ライブラリはヘッダの有無にかかわらず勝手にリンクされるのでリンクエラーにはなりません 推奨はされないが標準関数であれば include をしなくてもコンパイル、リンクは通ってしまうという考えでよろしいでしょうか。

  • asuncion
  • ベストアンサー率33% (2127/6290)
回答No.4

>#1さん 「日本語」と「今回の警告メッセージ」とでは、 前者の方がより広い範囲を表わすことは理解できますよね。#1さんなら。 ということは、仮に、質問者さんが後者を理解できなかったとしても、 だから前者も理解できていない、ということにはならないことは理解できますね。 つまり、 「日本語が理解できていないならば、警告の意味は理解できない(※1)」はおそらく成立しますが、 #1さんがお書きになった「※1の逆」は必ずしも成立するとは限らない、ということです。 そこまで質問者さんを罵倒する必要が、本当にあるのでしょうか。 私には理解できません。 (運営スタッフに連絡済)

ShimantoGa
質問者

お礼

ありがとうございます。 人それぞれ考え方がありますので何とも申し上げられませんが、このようなご意見をいただけたこと嬉しく思います。

回答No.3

ああ、#include <stdio.h>しなくても、コンパイラ側の方で「付け加えてくれる」実装もあるんです。推奨ではないんですが。 例えばオリジナルのUNIXのCコンパイラ(cc)なんかもそう言う感じで実装してた模様ですよ。 アスキーから出版されている C言語入門: http://ascii.asciimw.jp/books/books/detail/4-7561-0270-0.shtml なんかは、その実装の「クセ」を利用して、導入部では全く#include <stdio.h>を記述しないプログラムばっかが紹介されていたりします。 ただし、やっぱり#include <stdio.h>は付け加えるようにした方が良いですね。実装依存のコードは書くべきじゃないですから。

ShimantoGa
質問者

お礼

ご回答ありがとうございます。 >コンパイラ側の方で「付け加えてくれる」実装もあるんです。 コンパイラが printf などの標準関数(宣言だけ?)を自動的に生成するのですか。 >実装依存のコードは書くべきじゃないですから。 確かにコンパイラに頼った記述の仕方は良くないかもしれませんね。

回答No.1

>警告:関数 'scanf' は定義されていません。int 型の値を返す外部関数と見なします。 scanfを、int 型の値を返す外部関数と見なしたら、偶然、本当にint 型の値を返す外部関数だったので、偶然、何も問題無かっただけ。 引数の並びや個数もチェックされなかったが、偶然「正しく動くような引数が、正しい順番で並べられていた」ので、偶然、正しく動いただけ。 >警告:関数 'printf' は定義されていません。int 型の値を返す外部関数と見なします。 printfを、int 型の値を返す外部関数と見なしたら、偶然、本当にint 型の値を返す外部関数だったので、偶然、何も問題無かっただけ。 引数の並びや個数もチェックされなかったが、偶然「正しく動くような引数が、正しい順番で並べられていた」ので、偶然、正しく動いただけ。 つまり「すべて、偶然、うまく動いちゃっただけ」です。

ShimantoGa
質問者

お礼

ご回答ありがとうございます。 >警告の意味を理解できていない。 申し訳ありません、確かに警告の意味を正確に把握できておりません。 >つまり「すべて、偶然、うまく動いちゃっただけ」です。 stdio.h を include しなければ、printf や scanf という関数は存在すらしないと考えております。 であれば、 error 外部シンボル "_a" は未解決です。 error 外部参照 1 が未解決です。 のようにコンパイルエラーとなるのではないかと考えておりました。

関連するQ&A