最近、昔に書いた記事のアクセス数が増加している事に気がつきました。
Googleで「devel」と検索してみると当該記事が一番上に出てくるようになっていた。素晴らしい。
恐らく新入社員の方々が「develってなんやねん?」という素朴な疑問を検索してくれているのだと、勝手に思っています。
ここでふと自分に後輩が出来た時に「このdevelってなんですか?」と聞かれた事を想像する。上記の記事はあくまでもdevelが付いているパッケージの中身を確認しただけであり、詳細を全然理解出来ていないのだ。。
これは間違いなくシドロモドロになる(確信)
「エンジニアなんてそれでいいんだよ。お前かっこいいよ」
と言うのはあまりにもかっこ悪いので、この機会にもう一度しっかり調べて見ることにしました。
目次
一旦整理してみる
どうやらこの議題、プログラマの人なら簡単に分かる話らしい。しかし、こちらはロクにプログラムも組んだ事が無い毛の生えた素人。
「共有ライブラリがー」「ヘッダーファイルがー」と言われても、なんで必要なのかわからない限り納得出来ない。
そんなわけで一旦整理して、分からない事を(分かる範囲で)理解し、実物を作りつつ理解を深めようと思う。
ライブラリとは
とりあえずライブラリの事は「mainの無い関数の集まり」と勝手に仮定して話をしてみます。
まずはC言語で「5回abcを出力するプログラム」を記述します。
#include <stdio.h> int print_abc(int n); int main(void) { print_abc(5); return 0; } int print_abc(int n) { int i; for (i = 0; i < n; i++) { printf("abc\n"); } }
下のprint_abcという関数は、渡された数字の回数分、文字列abcを画面に出力する関数です。
main側でprint_abc(5)で呼ばれているので、このプログラムを起動すると5回画面にabcが出力されます。
# gcc ./print.c -o print # ./print abc abc abc abc abc
では、この関数部分print_abcをライブラリとして作っていきます。
#include <stdio.h> int print_abc(int n) { int i; for (i = 0; i < n; i++) { printf("abc\n"); } }
ではこれを共有ライブラリとしてコンパイルします。
# gcc -shared -fPIC -o libprintabc.so print_abc.c
つづいて、この共有ライブラリを使用するだけのプログラムを作ります。
#include <stdio.h> int print_abc(int n); int main(void) { print_abc(5); return 0; }
最初に作ったプログラムのメインと一緒です。但し、これをコンパイルしようとしても。。。
# gcc -o mainprint mainprint.c /tmp/ccUCO5i5.o: In function `main': mainprint.c:(.text+0xa): undefined reference to `print_abc' collect2: error: ld returned 1 exit status
関数print_abcが無いのでエラーが出て怒られます。コンパイルの際に共有ライブラリlibprintabc.soが使えるように-lオプションで指定してからコンパイルします。
# gcc -L . -lprintabc -o mainprint mainprint.c # ./mainprint abc abc abc abc abc
共有ライブラリにあるprint_abc関数をmainprintコマンドで使えるようになりました。
ヘッダファイル
ここまでだとヘッダファイルは特に出番が無いです。次にヘッダファイルを使って上のプログラムを作っていきます。
【mainprint.c】
#include <stdio.h> #include "mainprint.h" int main(void) { print_abc(5); return 0; }
【printabc.c】
#include <stdio.h> #include "printabc.h" int print_abc(int n) { int i; for (i = 0; i < n; i++) { printf("abc\n"); } }
【printabc.h】
#ifndef PRINTABC_H #define PRINTABC_H int print_abc(int n); #endif
関数の宣言の部分をヘッダファイルに移しました。
今回は-Iオプションでヘッダファイルを読み込むディレクトリを支持してコンパイルしていきます。
# gcc -shared -fPIC -o libprintabc.so printabc.c # gcc -I/usr/local/src/test -L./ mainprint.c -o mainprint -lprintabc # ./mainprint abc abc abc abc abc
分かったような、分からないような…
develパッケージに戻ってみる
以前にインストールしたzlib-develのパッケージの中身をもう一度見てみる。
yumでインストールすると「zconf.h」「zlib.h」がインストールされる。
zlib.hの中身を一部覗いてみると、
typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); typedef void (*free_func) OF((voidpf opaque, voidpf address)); struct internal_state; typedef struct z_stream_s { z_const Bytef *next_in; /* next input byte */ uInt avail_in; /* number of bytes available at next_in */ uLong total_in; /* total number of input bytes read so far */ Bytef *next_out; /* next output byte should be put there */ uInt avail_out; /* remaining free space at next_out */ uLong total_out; /* total number of bytes output so far */ z_const char *msg; /* last error message, NULL if no error */ struct internal_state FAR *state; /* not visible by applications */ alloc_func zalloc; /* used to allocate the internal state */ free_func zfree; /* used to free the internal state */ voidpf opaque; /* private data object passed to zalloc and zfree */ int data_type; /* best guess about the data type: binary or text */ uLong adler; /* adler32 value of the uncompressed data */ uLong reserved; /* reserved for future use */ } z_stream; typedef z_stream FAR *z_streamp;
上記のように構造体やプロトタイプ宣言が定義されています。
なのでココで定義されている宣言を使いたい場合にこのヘッダファイルが必要になってくるのでしょうね。
ざっくりとした順番としては以下みたいな感じでしょうかね。。
1.プログラムAをコンパイルする為には関数Bや構造体Cが必要だ
2.関数B、構造体CはライブラリDの中にありヘッダファイルEで宣言されている
3.OS標準でライブラリDはインストールされている。でもヘッダファイルEはインストールされていない。
4.e-develをインストールするとヘッダファイルEがインストールされる
仮定と推測だらけになりましたが、こんな感じでしょうか…
もう少し勉強して理解出来たら、内容補強します。
ここまで書いてきて大分自信が無くなったので、この記事は後日消すかもしれません。。