2013/11/18(Mon)
○[Unix C][*BSD][絶許、訴訟] sys/cdefs.hとは何ですか? (その5)
4.3BSD Net/2で「K&RとANSI/ISO-Cのポータビリティレイヤ」として初めて導入されたsys/cdefs.hですが、前回触れた ANSI/ISO-Cのキーワード周りで
- 大正義gcc軍
- それ以外のコンパイラ
というifdefが入ってしまったことで、その後の*BSDにおいて「Cコンパイラの実装の差異を吸収する」役割に変化します。
@4.3BSD Net/2 → 4.4BSD(聖域化)
Cコンパイラをpccからgccに置き換えたNet/2をベースに、フリーでは 386BSDそして商用では BSD/386が生まれるわけですが
ここちょっと消えてますね、44番という選手がいたんですけど 。
行方不明だったBSD選手(背番号44)は ライトにコンバートされて発見されますが この時点では sys/cdefs.hには更にgcc独自機能のラッパー(Dr. Dre感)マクロが追加されています。
/*
* GCC has extensions for declaring functions as `pure' (always returns
* the same value given the same inputs, i.e., has no external state and
* no side effects) and `dead' (nonreturning). These mainly affect
* optimization and warnings. Unfortunately, GCC complains if these are
* used under strict ANSI mode (`gcc -ansi -pedantic'), hence we need to
* define them only if compiling without this.
*/
#if defined(__GNUC__) && !defined(__STRICT_ANSI__)
#define __dead __volatile
#define __pure __const
#else
#define __dead
#define __pure
#endif
@__dead マクロ
以下の例のように、関数内でexit(3)やabort(3)などを呼ぶ事で決して元の関数に戻らないケースは多々あります。
#include <stdlib.h>
void
suicide(void)
{
exit(EXIT_FAILURE);
}
int
main(void)
{
suicide();
}
このコードをコンパイルしようとするとgccは警告を出します *1。
$ gcc -Wall test.c
test.c: In function 'main':
test.c:13:1: warning: warning: control reaches end of non-void function
プログラミング言語C 戻り値を返さず死亡、というシナキーンいかんでしょ(9800点)なミスはありがちです。
Cコンパイラにpccを使用していた頃はこのようなミスを発見する
性静的コード解析ツールはlint(1)
*2コマンドの出番でした(もちろん今でも残っていますが)。
$ lint test.c
test.c:
...
test.c(13): warning: function main falls off bottom without returning value [217]
...
Lint pass2:
しかしプログラムにおいてmain関数には戻らずイベントハンドラで終了というのはよくあることです *3。ですのでこの警告が煩わしい場合が多々あります。
lint(1)の場合はコメントにNOTREACHEDコマンドを埋め込むことでこの警告を抑制可能です。
int
main(void)
{
suicide();
/*NOTREACHED*/
}
ところがgccは当然ですがこのlint(1)のコマンドを認識しません、gccでこの警告を抑制するには
extern void suicide(void) __attribute__((noreturn));
のように返らない関数に対してノータリンじゃなくてnoreturn属性を指定する必要があります。 この属性(__attribute__)というのはgcc独自機能です。
古いgcc(2.5以前)ではnoreturn属性は存在しなかったので、以下のようにvolatileとして宣言する必要がありました。
extern void volatile suicide(void);
今のvolatile型修飾子(最適化抑止)とは意味合いが違うことに注意、ですので今時のコンパイラではエラーになります。 gcc.infoには
The `volatile' keyword tells the compiler to assume that `fatal' cannot return. This makes slightly better code, but more importantly it helps avoid spurious warnings of uninitialized variables. It does not make sense for a `volatile' function to have a return type other than `void'.
と書いてありますやな。
実際の使用例としてはinfoに「fatal」とある通り、 err.hあたりの致命的エラーでメッセージ吐いて死ぬ関数とかですな。
__dead void err __P((int, const char *, ...));
__dead void verr __P((int, const char *, _BSD_VA_LIST_));
__dead void errx __P((int, const char *, ...));
__dead void verrx __P((int, const char *, _BSD_VA_LIST_));
現在の実装は、gcc2.5以下ならvolatileキーワード、それ以上ならnoreturn属性使っとりますやね。
#if __GNUC_PREREQ__(2, 5)
#define __dead __attribute__((__noreturn__))
#elif defined(__GNUC__)
#define __dead __volatile
#else
#define __dead
#endif
@次回予告
今回書ききれんかった__pureマクロについて、そして gcc(1) と lint(1) の危うい関係についてお話します。