#Hello World
誰もが通る道。プログラマーでこの文字列を知らない人はいません。新しい言語を勉強するたびに初学者はこの文字列をコンソールに出力します。こういう動作を、標準出力と言ったりしますね。
標準出力は機能としてはOSのものであり、それをサポートする機能やライブラリが各言語には提供されている場合が多いです。まあ、いわゆるprint()
的な関数を使うやつですね。言語によって若干書式や文法は違いますが、どの言語を扱っている人でも「プリント」と言われれば大体何をしているかは分かると思います。
C言語においてもこの機能はもちろん用意されています。printf()
ってやつですね。
この関数を用いるには<stdio.h>
っていうヘッダファイルを取り込む必要があるのですが、そもそもプログラミングを始めたばかりの人でC言語から入った人は特に、プログラムの始めに書く
#include <stdio.h>
という一文を”おまじない”であると思っていませんか?
”C言語入門”を謳っているwebサイトや本にはよく「この1行はとりあえずおまじないだと思っていていいです」的なことが書いてあります。
少し脱線しますがjavaにおいてもpublic static void main(String args[])
を”おまじない”だと言っているサイトや本をよく見かけます。
いや、おまじないじゃないよ。
プログラムを動かす魔法じゃないよ。
ちゃんと意味があるんだよ。
ということで、僕自身もC言語はまだまだですが勉強も兼ねてHello Worldで遊んでみたいと思います。
#よく見るHello World
参考までに、よく見るやつを載せておきます。
###プログラム
#include <stdio.h>
int main(void){
printf("hello world!\n");
return 0;
}
###コンパイル、実行結果
$ gcc -o hello hello_world.c
$ ./hello
hello world!
まあ、何も問題ありませんね。
#”おまじない”を抜いてみる
プログラム
int main(void){
printf("hello world!\n");
return 0;
}
###コンパイル、実行結果
$ gcc -o hello hello_world.c
hello_world.c:4:5: warning: implicitly declaring library function 'printf' with
type 'int (const char *, ...)' [-Wimplicit-function-declaration]
printf("hello world!\n");
^
hello_world.c:4:5: note: include the header <stdio.h> or explicitly provide a
declaration for 'printf'
1 warning generated.
$ ./hello
hello world!
#なにが起こったの?ヘッダーファイルとは。
優秀と言っていいのかわかりませんが、ヘッダーファイルがないで〜という警告が出るだけで実行できてしまいました。
厳密にいうと、直訳では「printf()
は宣言されていませんよ」という警告と、「ヘッダーファイルをインクルードするか明示的にprintf()
を宣言してね」と言われています。
どちらにせよ、ヘッダーファイルがインクルードされてないのが原因で起こった警告です。
ヘッダーファイルの名前を知っているくらいなら、printf()
くらい勝手に理解してくれって感じですね。
C言語のコンパイラはヘッダーファイルを書き忘れても補完してくれるのでしょうか。
そんなことは置いておいて、どちらにせよ#include <stdio.h>
を抜くとおかしなことが起こったのです。この”おまじない”は一体なんなのでしょうか?
ヘッダーファイルの定義はググればいくらでも出てくるので、ここで改めて引用して定義を書くことはしません。また、今から書く説明は僕のイメージや勝手に解釈していることなので厳密性にかけますし間違っている可能性もあるので、間違いがあったらぜひコメントお願いします。
###インクルードしているのは説明書
<stdio.h>
というヘッダーファイルは、標準Cライブラリと呼ばれる”ライブラリ”です。
ライブラリとは、
汎用性の高い複数のプログラムを再利用可能な形でひとまとまりにしたものである。
みたいな説明をよくされますが、簡単にいうと説明書をたくさん保管してある図書館です。
雑な絵で申し訳ありませんが僕のイメージはこんな感じです。
基本的に、関数はプログラム内で定義しないと使えません。もちろん、printf()
だって関数ですから、本来であればプログラム中で定義しないと使えないわけですが、頻繁に使う関数は前もって定義してまとめておいていつでも引っ張ってこれるようにした方が効率がいいわけです。
それを実現したのが”ライブラリ”と呼ばれるファイルです。
厳密に、stdio.h
にprintf()
の使い方が載っているわけではないのですが、、、。
今回はイメージの話なのでご理解ください。
#ヘッダーファイル無しでHello Worldしたい
ライブラリは便利だぞということを述べた直後ですが、じゃあライブラリを取り込まなくてもプログラム内で宣言しちゃえばいいやん?という発想になるのがひねくれプログラマーです。
やってみましょう。
もちろん、警告を出さないようにちゃんと実装する、という意味です。現状#include <stdio.h>
を書かなくてもHello World!は出ることには出てしまうので(笑)。
と、大口を叩きましたが色々調べてみると標準出力の関数を実装するのって結構難しい、、、。
OSの知識やアセンブラの知識がないといけないようで、アセンブラでシステムコールを直接呼ぶのが簡単そうです。
アセンブラを書かないにしろ、結局システムコールを使う必要がありそうです。
恥ずかしながらこの辺の知識はあまりないので今回は触れず、勉強してからまた記事にすることにします。
とは言え、ヘッダーファイルのインクルード無しでprintf()
を使うことはできます。
プログラム
extern int printf (__const char *__restrict __format, ...);
int main(void){
printf("hello world!\n");
return 0;
}
###コンパイル、実行
$ gcc -o hello hello_world.c
$ ./hello
hello world!
こんなことしかできないことが非常に恥ずかしですが、どうか今はお許しください。
大口を叩いた割にしょうもないことしかやってないですが、何かの参考になればと思います。
これからも精進してまいります。
#参考
64bitのOS + C言語でライブラリを使わずにHello Worldをしてみた
Wikipedia printf
立命館コンピュータクラブ ライブラリを作ろう