ヘッダファイルをincludeしただけで関数が使えるようになる理由

解決済

回答 2

投稿

  • 評価
  • クリップ 1
  • VIEW 3,432

anaprestoo

score 197

C言語では、#include <stdio.h>と先頭に書くだけで、printf関数などが使えるようになりますが、なぜ関数が使えるようになるかがわかりません。
ヘッダファイルは「宣言の集まり」なのに、なぜ関数が使えるようになるのでしょうか。printf関数の実体はどこにあるのでしょうか。

初歩的な質問で申し訳ありません…。
よろしくお願いします。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

+3

「ライブラリ」という形で、あらかじめコンパイル済みのものがコンパイラとともに提供されています。そして、「リンク」という行程(基本的にはコンパイル後に自動で行われる)で実行モジュールに組み込まれて使えるようになります。一般的なコンパイラはprintfのような「標準関数」は、特に何もしなくてもリンクするようになっています。もちろん、ユーザーが細かく指定することもできます。

あるいは「動的リンク」という仕組みにより、実行させたときに自動的に必要なライブラリをリンクするという方式もあり、現在はそのケースが多いです。OSと一緒にライブラリモジュールが提供されていて、それを呼び出しています。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/12/17 20:54

    回答ありがとうございます。
    リンク作業をコンパイラが自動で行っているため、printfなどの関数が使えるようになるのですね。非常にすっきりしました。
    ありがとうございました!

    キャンセル

checkベストアンサー

+2

まずコンパイルでソースからオブジェクトファイルを作ります。
ひとつのソースをコンパイルするとき、他のソースは気にしません。
宣言しか見当たらなかった場合、「そういう物がある」で済ませます。
その次に行うリンクという作業で、オブジェクトファイルやライブラリをまとめます。
オブジェクトファイルを眺めて、宣言だけのものが見つかった場合、
別のオブジェクトファイルやライブラリから探してきます。
printfはどこかにある~.libの中に実装されているはずです。

というわけで
「何故標準ライブラリの関数の宣言を書くだけでそれが使えるようになるか」は、
ざっくり言うと「リンカが気を利かせて上手いことやってくれている。」です。

参考:#includeとリンクの違いはなんでしょうか

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/12/17 20:38

    回答ありがとうございます。
    回答を頂くまでは、コンパイルやリンクについての知識が足りませんでした。しかし、ozwkさんとcatsforepawさんが回答してくださり、#include、コンパイル、リンクの関係性がはっきりと分かりました。
    本当に、ありがとうございました。

    キャンセル

  • 2015/12/19 14:14 編集

    解決済みですが、老婆心から念のため。

    「includeしないとライブラリ関数がリンクされない」と誤解しないでいただきたい。
    現に、手元のgccで小さなコードを試してみると、#include <stdio.h>が無くてもコンパイルが通ります(お使いのコンパイラでは違う結果になるかもしれない)。ただし、警告(warning)メッセージは出ます。その警告またはエラーメッセージが何故出たのか、或いはメッセージを出したのはコンパイラなのか、リンカか…等々、その都度メッセージの意味をきちんと確かめられるようになると、あなたの理解は深まっているはずです。
    さて、コンパイルが通り、プログラムができあがったとします。問題なく動作する場合もありますが、しばしば意図と違う動作をする、いわゆるバグになる場合があります。これがincludeしない場合の重大問題なのです。

    ご質問に「ヘッダファイルは宣言の集まりなのに…」とありますね。ここで言う、ヘッダファイルの中の宣言を「関数プロトタイプ宣言」と呼びます。これが鍵です。
    プロトタイプ宣言が何のためにあるのか、或いは、無いとどう不具合があるのか・起こるのか、理解されていれば結構ですが、そうでなければ、この辺りをおさらいすることをお勧めいたします。

    キャンセル

  • 2015/12/19 16:36

    アドバイスありがとうございます。

    確かに私は、まだ関数の宣言が何のためにあるかがわかっていません。どこにも宣言・定義していない関数をmain関数の中に書いてコンパイルしても警告・エラーとはなりませんので、わざわざ関数の宣言をする必要はあるのか?と思ってしまいます。

    それに、宣言・定義していない関数を使用しても何もメッセージは表示されないのに、#include<stdio.h>のないファイルでprintf関数を使用すると「incompatible implicit declaration of built-in function 'printf'」という警告が表示される理由もわかりません(コンパイラはGCCを使用しています)。普通は逆の結果になるように思うのです。例えば次のようにです。

    ・printf→コンパイラはこの関数の定義を知っている→何も言わずにその関数を使ってくれる
    ・宣言・定義していない関数→コンパイラはこの関数を知らない→警告orエラー

    このような疑問がまだ湧いてきますので、まだコンパイル・リンクについての理解が足りないのだと思います。

    rubato6809さんのアドバイス通り、「関数プロトタイプ宣言」とは何か、そして何のためにあるのかを調べようと思います。コメントありがとうございました。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.21%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる