
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 ++.
という記述があります。
ので,この「暗黙の関数宣言」には頼らずにプログラムを書く事をお勧めします。
ご回答ありがとうございます。
>暗黙の関数宣言
標準関数で、かつ宣言が可視でない場合、暗黙で exturn というものを利用して宣言している。
それにより、どこかのソースから int printf(const char *, ...); を探し出して実行しようとした。
という理解でよろしいでしょうか。
No.8
- 回答日時:
scanf や、printf 等の、可変長引数の関数は、引数の受け渡し方が変更されることがあり、
宣言が無いと、コンパイラによっては正常な動作が行われない可能性がありますので、注意した方がいいです。
まあ、現状、ごく一部のコンパイラだけのようなので、普通は大丈夫でしょうが。
ANSI C89 からの仕様です。
ANSI C99 は、宣言が無いとエラーになるんだったっけ? C++ だけだったか?(忘れた(^^;)
ご回答ありがとうございます。
何のコンパイラを使用しているか、何てことはあまり意識したくない所です。
やはり明示的に宣言するのが一番のようですね。(他者が解析する時も読みやすいでしょうし)
No.7
- 回答日時:
いや, その
「ライブラリで printf が定義されており、ヘッダで printf が宣言されている。」
という考え方が正しいんです. だから, この警告メッセージの「定義」が間違ってる (「宣言されていません」じゃないとおかしい), と.
なお, 「正しく宣言できれば #include しなくていい」というのは, 逆に「正しく宣言できなければ #include しなければならない」という意味でもあります. たとえば fprintf を宣言するためには FILE が宣言されていなければならないので, 結局 stdio.h を #include しなければならない」ということになります... でも, こんなことを考えるくらいなら最初から #include した方がきっと楽.
あと #3 に絡みますが, #2 でいわれるように「関数の宣言がなければ勝手に int を返す関数だとみなす」というルールがありました. これは標準ライブラリ関数に限らず適用されるので, 自分で
int hoge() { ... }
という関数を作って置いた場合, 別ファイルから hoge を宣言せずに使うことができます (もちろんリンクするときに適切なオブジェクトファイルも指定しないとこけますが). このことと「ポインタと int は同じだった」というご都合主義との相互作用で, ふる~い (ANSI 以前の) Cコンパイラについているヘッダでは十分な数の関数を宣言していなかったりします.
>この警告メッセージの「定義」が間違ってる (「宣言されていません」じゃないとおかしい
理解が足らず申し訳ありません。
確かに宣言がないため 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 は同じだった」というのはよくわかりませんが、宣言をしない記述が普通だった頃があったということでしょうか。
再度のご回答誠にありがとうございました、大変勉強になります。
No.6
- 回答日時:
念のため書いておくと, #include がなくても「きちんと正しく宣言できる」ならプログラムとしては問題ありません. 例えば
int printf(const char *, ...);
int scanf(const char *, ...);
と書いてあれば #include はなくても OK.
とはいえ「正しい宣言」を頑張って書くよりも「その関数を宣言しているヘッダファイル」を #include した方が安全かつ確実なので #include してください.
でも, これ警告の文章は間違ってるよね. 今まで printf や scanf が「定義されている」プログラムって見たことないんだけど. もちろん「(#include 経由で) 宣言している」プログラムならごく普通.
ご回答ありがとうございます。
>念のため書いておくと, #include がなくても「きちんと正しく宣言できる」ならプログラムとしては問題ありません
なるほど、 include せず自分で宣言してしまうという方法もあるのですね。(あまりやらないでしょうが)
>でも, これ警告の文章は間違ってるよね. 今まで printf や scanf が「定義されている」プログラムって見たことないんだけど. もちろん「(#include 経由で) 宣言している」プログラムならごく普通.
間違っているのですか、気づきませんでした。
ライブラリで printf が定義されており、ヘッダで printf が宣言されている。
という考え方をしていました。
No.5
- 回答日時:
>stdio.h を include しなければ、printf や scanf という関数は存在すらしないと考えております。
関数が存在するのはヘッダではなくライブラリです
コンパイルは外部参照が未解決のまま行われリンク時にライブラリ内を探して外部参照が解決されます
標準関数ライブラリはヘッダの有無にかかわらず勝手にリンクされるのでリンクエラーにはなりません
一般にヘッダをincludeしてライブラリをリンクしないときに外部参照が未解決となります
ご回答ありがとうございます。
>関数が存在するのはヘッダではなくライブラリです
私の勘違いです、大変失礼しました。
>コンパイルは外部参照が未解決のまま行われリンク時にライブラリ内を探して外部参照が解決されます
つまり、正確にはコンパイルエラーではなくリンクエラーということですね。
>標準関数ライブラリはヘッダの有無にかかわらず勝手にリンクされるのでリンクエラーにはなりません
推奨はされないが標準関数であれば include をしなくてもコンパイル、リンクは通ってしまうという考えでよろしいでしょうか。
No.4
- 回答日時:
>#1さん
「日本語」と「今回の警告メッセージ」とでは、
前者の方がより広い範囲を表わすことは理解できますよね。#1さんなら。
ということは、仮に、質問者さんが後者を理解できなかったとしても、
だから前者も理解できていない、ということにはならないことは理解できますね。
つまり、
「日本語が理解できていないならば、警告の意味は理解できない(※1)」はおそらく成立しますが、
#1さんがお書きになった「※1の逆」は必ずしも成立するとは限らない、ということです。
そこまで質問者さんを罵倒する必要が、本当にあるのでしょうか。
私には理解できません。
(運営スタッフに連絡済)
ありがとうございます。
人それぞれ考え方がありますので何とも申し上げられませんが、このようなご意見をいただけたこと嬉しく思います。
No.3
- 回答日時:
ああ、#include <stdio.h>しなくても、コンパイラ側の方で「付け加えてくれる」実装もあるんです。
推奨ではないんですが。例えばオリジナルのUNIXのCコンパイラ(cc)なんかもそう言う感じで実装してた模様ですよ。
アスキーから出版されている
C言語入門:
http://ascii.asciimw.jp/books/books/detail/4-756 …
なんかは、その実装の「クセ」を利用して、導入部では全く#include <stdio.h>を記述しないプログラムばっかが紹介されていたりします。
ただし、やっぱり#include <stdio.h>は付け加えるようにした方が良いですね。実装依存のコードは書くべきじゃないですから。
ご回答ありがとうございます。
>コンパイラ側の方で「付け加えてくれる」実装もあるんです。
コンパイラが printf などの標準関数(宣言だけ?)を自動的に生成するのですか。
>実装依存のコードは書くべきじゃないですから。
確かにコンパイラに頼った記述の仕方は良くないかもしれませんね。
No.1
- 回答日時:
>警告:関数 'scanf' は定義されていません。
int 型の値を返す外部関数と見なします。scanfを、int 型の値を返す外部関数と見なしたら、偶然、本当にint 型の値を返す外部関数だったので、偶然、何も問題無かっただけ。
引数の並びや個数もチェックされなかったが、偶然「正しく動くような引数が、正しい順番で並べられていた」ので、偶然、正しく動いただけ。
>警告:関数 'printf' は定義されていません。int 型の値を返す外部関数と見なします。
printfを、int 型の値を返す外部関数と見なしたら、偶然、本当にint 型の値を返す外部関数だったので、偶然、何も問題無かっただけ。
引数の並びや個数もチェックされなかったが、偶然「正しく動くような引数が、正しい順番で並べられていた」ので、偶然、正しく動いただけ。
つまり「すべて、偶然、うまく動いちゃっただけ」です。
ご回答ありがとうございます。
>警告の意味を理解できていない。
申し訳ありません、確かに警告の意味を正確に把握できておりません。
>つまり「すべて、偶然、うまく動いちゃっただけ」です。
stdio.h を include しなければ、printf や scanf という関数は存在すらしないと考えております。
であれば、
error 外部シンボル "_a" は未解決です。
error 外部参照 1 が未解決です。
のようにコンパイルエラーとなるのではないかと考えておりました。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
main.c:7:43: warning: implici...
-
DWORDの実際の型は何でしょうか
-
ハンドルされていない例外が発...
-
構造体を引数とする、クラス間...
-
sshdログの意味
-
void func( void )について
-
【#define】 defineで定義した...
-
プログラムの中で別のmainを呼...
-
typedefの使い方
-
C++のコンストラクタを宣言する...
-
24ビットの変数
-
2重定義って??
-
C言語のコンパイルエラー
-
相互参照するクラス、俺こんな...
-
最早開始時間と最遅完了時刻を...
-
20'(角度)の計算がわかりま...
-
信頼区間の1.96や1.65ってどこ...
-
Aの値からBの値を除するとは??
-
数字以外が入力されたらエラー...
-
値差の%計算方法について
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
main.c:7:43: warning: implici...
-
long型の定数の末尾にLを付ける...
-
DWORDの実際の型は何でしょうか
-
visualstudio C# テキストボッ...
-
2重定義って??
-
C++のfor文について
-
変数の型を定義しなかった場合...
-
ハンドルされていない例外が発...
-
intとINTの違いは?
-
C++でboolにintの値を代入する...
-
構造体の要素すべてに対する四...
-
プログラムの中で別のmainを呼...
-
エラー「invalid conversion fr...
-
GCCで暗黙の型変換の警告を出し...
-
【#define】 defineで定義した...
-
sshdログの意味
-
DDVによるメッセージの変更
-
typedef enumの使い方を教えて...
-
構造体を生成時にわざわざ初期...
-
DLLでLIBファイルが作成されない
おすすめ情報