• ベストアンサー

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% (2126/6288)
回答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 などの標準関数(宣言だけ?)を自動的に生成するのですか。 >実装依存のコードは書くべきじゃないですから。 確かにコンパイラに頼った記述の仕方は良くないかもしれませんね。

  • chie65535
  • ベストアンサー率43% (8526/19383)
回答No.1

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

ShimantoGa
質問者

お礼

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

関連するQ&A

  • C++の質問です!

    課題なんですけど分からないです(泣) 解答お願いします(;;) 下記のプログラム中の(1)~(7)には何が入るか? #include <stdafx.h> (3) sho((4)) //関数sho の定義 { return((5)m/n); } void irekae(int (6),int (7)) //関数irekae の定義(2つの引数をp,q とする、型に注意!) { int k; k=(6); (6)=(7); (7)=k; } int _tmain(int argc, _TCHAR* argv[]) //関数_tmain の定義 { int i,j; double x; printf("整数i,j?:"); scanf("%d%d",&i,&j); (1); //整数i,j を引数として実数除算した商を返す関数sho を呼び出す。 printf("i/j=%f¥n",x); //結果の確認 (2); //整数i,j の値を入れ換える関数irekae を呼び出す。 (1); printf("i/j=%f¥n",x); }

  • 文字列の扱い方

    初歩的な質問ですみません… str文字列からcという文字を見つけたら添字を返すという関数を作ったのですが、 iにこの関数を代入して、if文の制御式にiを使って比較するまでは正常なのですが、 真文にiを使うと何故か偽文(という言い方でいいのでしょうか…この場合("そんな値はありません。"というところです)が実行されてしまいます。 よろしければご教授お願い致します。 #include <stdio.h> int str_char(const char str[],int c) { int len = strlen(str); int i; for (i = 0;i < len;i++) { if (str[i] == c) return i; } return -1; } int main() { char str[64] = "Fucking Brutal Death Metal"; int ch,i; printf("どの文字を調べますか?"); scanf("%c",&ch); i = str_char(str,ch); if (i >= 0) printf("その文字は%d番目にあります。",str_char(str,ch) + 1); //何故かiだと動かない else printf("そんな値はありません。"); return 0; }

  • 課題で、アルファベットを入力するとすべてのアルファベットを小文字に変換

    課題で、アルファベットを入力するとすべてのアルファベットを小文字に変換して出力するプログラムを作成しました。2度以上同じ処理を行う場合はその部分を関数としなければいけないのですが、うまくできなかったようです。一応実行結果のとおりに表示されますが、複数のエラーや警告が出ました。どこが間違っているのでしょうか? ・警告 W8065 test.c 9: プロトタイプ宣言のない関数 'henkan' の呼び出し(関数 main ) ・エラー E2379 test.c 10: ステートメントにセミコロン(;)がない(関数 main ) ・警告 W8070 test.c 10: 関数は値を返すべき(関数 main ) ・エラー E2356 test.c 12: 'henkan' の再宣言で型が一致していない ・エラー E2344 test.c 5: 一つ前の 'henkan' の定義位置 ・エラー E2040 test.c 16: 宣言が正しく終了していない ・エラー E2141 test.c 16: 宣言の構文エラー ・エラー E2141 test.c 16: 宣言の構文エラー ・エラー E2190 test.c 29: 不要な } 実行結果↓ **************************** Atsushi TAKEDA ==>atsushi takeda **************************** #include <stdio.h> #include <string.h> #include <ctype.h> int henkan(); int main(void) { henkan() } int henkan; char str[100]; int i,j,chk,len; for(j=0;j<6;j++){ gets(str); len=strlen(str); printf("==>"); for(i=0;i<len;i++){ chk=isupper(str[i]); if(chk!=0) str[i]=tolower(str[i]); printf("%c",str[i]); } printf("\n"); } return 0; }

  • C言語の配列について

    配列を20 定義し値を入力して合計値を出したいのですがどうすればよいのでしょうか 下のソースでエラーはおこりませんでした 何がちがうのでしょうか #include <stdio.h> int main() { int a[20]={}; int i, sum; printf("整数を入力してください:"); scanf("%d",&a); printf("\n"); for (i = 0; i < 10; i++) { sum += a[i]; } printf("sum= %d\n", sum); return 0; }

  • C言語文字数4つカウント

    こんにちは。大学の課題で「キーボードから入力した文字数に含まれる'h''o''g''e'の4つの文字数をカウントする関数を含むプログラムを完成させよ」という課題がありました。なかなかできず現段階では以下のようになっています。どうすれば問題文のように実行できますか? #include <stdio.h> char str_chnum(const char str[], int ch) { int count = 0; int i; for(i = 0; str[i] != '\0'; i++) { if(str[i] == ch) count++; } return(count); } int main(void) { char str[128]; int ch; printf("文字列を入力してください:"); scanf("%s", str); printf("検索する文字を入力してください:"); scanf(" %c", &ch); printf("%cは%d個ありました。\n", ch, str_chnum(str, ch)); return(0); }

  •    #include<stdio.h>

       #include<stdio.h> #include<math.h> int main(void) { int i,n,limit; printf("data? "); scanf("%d",&n); i(n>=2){ limit=int)sqrt(n); for(i=limit;i1;i--){ if(n%i==0) break; } if(i==1) printf("素数\n); else printf("素数でない\n"); } return 0; } というプログラムがあるのですが、それを改良して int型(符号付32ビット整数)および、unsigned int型(符号なし32ビット整数)のそれぞれの最大の素数を求めよという問題があり、ただし、エラトステネスのふるいは使わずに、上のプログラムを改良してみよという問題がどっかにあったんですが、全然わからないので、教えてください。

  • このプログラムのどこを直せばいいんでしょうか?

    いま、文字列strの中に文字cが含まれている個数を表示するプログラムを作っているんですが、下のように作っても、上手く行きません。 どこをどう直せばいいのか教えてください! #include <stdio.h> int str_chnum (const char str[],int d) { int i=0; int sum=0; while(str[i]!=0){ if(str[i]==d ){ sum++; } i++; } return(sum); } int main(void) { int c; char str[128]; puts("put sentence..."); scanf("%s",str); puts("search character..."); scanf("%d",&c); printf("there are %d %ds in this sentence.",str_chnum(str,c),c); return(0); }

  • 文字列

    下のプログラムは何をするためのプログラムなのか教えてください。 個人的にはJISコードに関係していると思うのですがさっぱりわかりません。 どなたか詳しい説明お願いします。 #include <stdio.h> #define LEN 255 int main(void) { char str[LEN]; char cipher[LEN]; int ikey; int i,n; printf("文字列を入力せよ : "); scanf("%s", str); printf("鍵を入力 : "); scanf("%d", &ikey); i = 0; while (str[i]!='\0') { n = (str[i]-'A'+ikey)%26; cipher[i] = 'A'+n; ++i; } cipher[i] = '\0'; printf("%s\n", cipher); return 0; }

  • scanf が無視されます

    例えば下のようなプログラムを実行するとmain関数の scanf が無視 されてしまいますが何故なのでしょうか。コンパイラはLSI C-86試食版です。 ----------------------------- #include <stdio.h> int main ( ) ; void aa ( ) ; int main ( ) { int i ; aa ( ) ; printf ( " input i. \n " ) ; scanf ( " %d ", &i ) ; printf ( " i = %d\n ", i ) ; } void aa ( ) { int x, y ; while ( scanf ( " %d ", &x ) ! = EOF ) { y = x ; printf ( " y= %d\n ", y ) ; } } --------------------------------

  • ポインタ配列の問題で、、。

    ポインタ配列の問題ですが、このmain関数でどこかがおかしいのですが、どこをどう変更すれば正常に動作するのかわかりません。どなたかお願いします(注)string入力文字は9文字以下です。 #include <stdio.h> #define NUM 5 main(void) {     char *str[NUM];     int i;     for(i = 0; i < NUM; i++)     {      printf("string ->"); scanf("%s", str[i]);      }      for(i = 0; i < NUM; i++)      {        printf("str[%d] -> %s\n", i, str[i]);      } }