2008年11月10日

Cでの仕切り直し、includeとexternを理解する。

まじめにCで仕切り直すことにした。
Cとdelphiの違い、それはヘッダーファイルとincludeとusesの違いだと分かった。
「学習レベル」のプログラミングではなくて実用的なプログラムの場合
ライブラリーなどを使いつつソースを分散してもリンクは最小限にしたい。
そういう事も含めて根本的な指針の為の確認を行った。
とりあえずdelphiの「include」とCのそれとほぼ同一仕様とみていいようだ。
C自体javascriptなりいろいろなソースで見慣れている訳だが
汎用ライブラリーを使いつつ再利用できるものとアプリケーション本体を構築しようとすると
delphiの感覚で組むとひっかかる。
で最初にひっかかったのは「extern」と「include」だ。

今までZLIBなどライブラリー系のソースなどをみる機会がありコンバートしたりしたのだが
ヘッダーファイルの関数宣言に「extern」がついていたので
てっきりpascalのint部に相当するものがhファイルでexternは外部出力宣言だと思っていたのだが
そうではなくて

よそで宣言されているものをプロトタイプ宣言する時に使う

のがexternだと分かった。
ちなみにユニット内のスコープのものはstatic宣言らしい。

またincludeはdelphi同様単なるコピペで参照などではないことも確認した。
つまりちゃんとしたクラスや型宣言である必要はない。

例えば

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "SDL.h"

#define SCREEN_WIDTH 640
#define SCREEN_HEIGHT 480
#define SCREEN_BPP 32
#define WINDOW_CAPTION "SDL_001"

int PollEvent()
{
SDL_Event event;
SDLKey *key;
while(SDL_PollEvent(&event) )
{
switch(event.type){
case SDL_QUIT:
return -1;
break;
case SDL_KEYDOWN:// キーボード入力
{
key=&(event.key.keysym.sym); // キー取得
if(*key==27){//esc
return -1;
}
}
break;
}
}
return 0;
}


int main(int argc, char *argv[])
{
SDL_Surface *sdl_sf;
if ( SDL_Init(SDL_INIT_VIDEO) < 0 ) {
exit(1);
}
sdl_sf = SDL_SetVideoMode(
SCREEN_WIDTH,
SCREEN_HEIGHT,
SCREEN_BPP,
SDL_SWSURFACE
);

SDL_WM_SetCaption( WINDOW_CAPTION, NULL );

while( 1 ){
if( PollEvent()<0 )break;
}
//
SDL_Quit();
//
return(0);

}



というSDLのWindowを作るcファイルsdl_001.cがあったとする。


gcc -Wall -pipe sdl_001.c -o sdl_001 \
-I/usr/local/include -I/usr/local/include/SDL\
-L/usr/local/lib -Wl,-rpath,/usr/local/lib -lSDL

でビルドできる。

この時int PollEvent()関数をいろいろ変えたい場合
内部の一部だけを外部ファイルとして試行錯誤できる。
この例は変えていない。
inc0011.inc

{
switch(event.type){
case SDL_QUIT:
return -1;
break;
case SDL_KEYDOWN:
{
key=&(event.key.keysym.sym);
if(*key==27){//esc
return -1;
}
}
break;




sdl_001.c

int PollEvent()
{
SDL_Event event;
SDLKey *key;
while(SDL_PollEvent(&event) )
#include "inc0011.inc"
}
}
return 0;
}

と区切りの悪い所を切ってincludeするこことができた。
これはdelphiのincludeと同じ仕様でもある。
つまりincludeはヘッダーとかではなくて透過的にコピペしているだけで
便宜上再利用しているだけということになる。
特に共通部分と切り替え部分を分ける場合無理に関数化しなくてもいいとか
一つのCなのでスコープがstaticでいいなどコードの効率が分割するより良くなるはずだ。

ようするにCはdelphiのusesに相当するものがない。
ひとつのCユニットには必ず使用する外部ユニットの関数をプロトタイプ宣言する必要があり
それを繰り返さないために

#ifndef TEST_H
#define TEST_H


#endif /* TEST_H */

で区切って重複使用しても宣言がかさならないようにするということらしい。
自分自身delphiでも単一のライブラリーにしたいものはincludeを使っていて
重複をさけるためにあえてusesにするケースもあったが
このC風の使い方をすればよいときづいた。

で「インターフェース」として使う場合は実態とならない型宣言とか
実態の無いプロトタイプ宣言を便宜上使っているということのようだ。

というと「ヘッダー」という名義でincludeする場合は関数本体を書くのは良くないが
単にソースファイルを分散して一つのライブラリーを作るというのならば
実態があっても構わないだろうといえる。
一つのユニットがライブラリーとライブラリーのラッパーをincludeする場合
ラッパーをincludeして使う前提なら
ライブラリーのヘッダーはラッパーに含まれているのでいらないということになる。

またラッパーをコンパイルしてリンクする場合は
ラッパーの関数のプロトタイプ宣言が必要になる訳で
プロトタイプ宣言にライブラリーのヘッダーが入っていなければ
メインのユニットにはライブラリーのヘッダーのincludeが必要になる。
ただし大抵はラッパーのヘッダーにライフラリーのヘッダーをincludeしておいて
確実に不必要にする方がすっきりする。

extern宣言のサンプルが実態とexternでの宣言が一対一のソースが多いのは
すっきりと整理されているからで
宣言と実態をちゃんと整理しないとあちこちでextern宣言して分割コンパイルするハメになるのだろう。

勘違いしたままでdelphi記法でヘッダーにexternを入れるのは宣言その物だと思っていても
結果的にちゃんとコンパイルできるわけだが
Cのプロトタイプ宣言の意味合いがようやく分かった気がする。


ラベル:include SDL
posted by Xo_ox at 23:18| Comment(0) | C | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。
×

この広告は180日以上新しい記事の投稿がないブログに表示されております。