実開発に使われるC言語 [基礎から裏技まで] (1)
-
(1)
/**- ここに書いてある内容は、
- すべて個人的な開発経験に基づいたもので、
- ときには間違いが免れないと思います。
- ご指摘、ご意見を是非お願い致します。
*/
<1>. Cの標準
C言語といえば、まず標準というものが存在している。僕の大学時代(2000年初頭)に教わったC言語はC89で、その後、実際に日本で仕事する際、ほとんどC99を使ってました。C89とC99の違いをいくつかありますが、下記に自分がよくあうものを挙げてみました。
・コメント表記の // を導入したのはC99で、C89では使ってはいけません。
・C99に新しいDataタイプを導入した:例えばbool (#include <stdbool.h>, true, false)
・C89には、変数宣言をブロックの最初の部分にしか書けないが、C99はどうなのか確認していませんけど、いい習慣として、やはりC89の規定に従ったほうがいいと思います。どの標準でCのソースをコンパイルするかは、コンパイラのコンパイル命令で指定できます。
例えばコンパイラがgccの場合
【例】gcc xxx --std=c99 あるいは gcc xxx --std=c89で指定できます。<2>. コンパイラはそこまで賢くない。
例えば、今2つの.hファイルa.hとb.hがあるとして、各自に下記のソースコードを書いております:// a.h #include "b.h" // b.h #include "a.h"
今main.cというファイルはこの2つのheaderを利用したい場合、下記のように書いています:
// main.c #include "a.h" #include "b.h"
これでmain.cをコンパイルすると、どうなるでしょうか?
a.hとb.hをお互いにincludeしてて、コンパイラはa.hとb.hを繰り返してincludeし、無限ループになってしまいます。includeが重複しているのをわかって、自動的にincludeを停止するとか、コンパイラはそこまで賢くない
こういった問題を避けるためには、Cのheaderファイルを書くとき、大体下記のような定番的な書き方をします:
#ifndef A_H #define A_H #include "b.h" #if defined (__cplusplus) extern "C" { #endif // TODO:structやenum、関数などの宣言はここから書く #if defined (__cplusplus) } #endif #endif /* A_H */
ここでマクロの#ifndef A_H ... #endifで囲んで、上記の繰り返しinclude問題を解決できます。
visual studio C++を使ったことのある方は、もしかしたら下記のような書き方を見たことがあるかもしれません:
#pragma once // TODO:structやenum、関数などの宣言はここから書く
ここの#pragma onceも上記と同じ意味で繰り返しinclude問題に対応していますが、あくまでコンパイラに依存しているマクロ命令なので、
標準的でどこでもどの環境でもコンパイルできるようなCコードを書きたい場合、あまりお勧めしません。<3>. CとC++
CとC++の混合的プログラミングの話になると、きりがないので、ここでまず一番簡単なextern "C" {xxx}について説明致します。
多くの方は、もしかしたらCとC++を同じように見る傾向があるかもしれませんが、実は別々の言語で見たほうが、のちに変なコンパイルエラーが少なくで済むかもしれません。
原因の一つとして、C++のコンパイラを使ってコンパイルする場合、すべての関数や構造体に対して、付加情報をその名前に追加して、新しい名前にしているのです。そうなると、のちのリンク段階では、元の名前を求めるC言語のソースコードと、名前が変わった関数や構造体との整合性が失われてしまって、使いたい関数や構造体が見つからないというコンパイルエラーが出てしまいます。そこで、ここのextern "C" { xxx }の登場になります。その意味は、「extern "C" { xxx }の括弧に囲まれたソースコードの部分を、付加情報などせずに元の名前のままにしてください」と、C++のコンパイラに教えています。
もしC++のコンパイラを使ってコンパイルする場合、必ず<2>の書き方で書いてください。とにかく無難ですので