home ホーム search 検索 -  login ログイン  | reload edit datainfo version cmd icon diff delete  | help ヘルプ

C言語系/memos/gcc/関数の属性機能("__attribute__")

C言語系/memos/gcc/関数の属性機能("__attribute__")

C言語系 / memos / gcc / 関数の属性機能("__attribute__")
id: 577 所有者: msakamoto-sf    作成日: 2010-02-05 15:10:42
カテゴリ: C言語 

GCCで提供されている関数の"__attribute__" 機能について極々簡単に紹介する。基本的に unixwiz.net の "Using GNU C __attribute__" のざっくりとした要約になっている。なお、ここで紹介している機能を有効にするには-Wallオプションを指定する必要がある。

※動作確認はCentOS5.2上のgcc-4.1.2, binutils-2.17.50.0.6 上で行っている。

__attribute__ format

printfやscanfのような、可変長引数群を書式指定文字列に埋め込んだりする関数で、書式指定文字列と可変長引数群の組み合わせを「コンパイル時に」チェックできるようになる。

format (archetype, string-index, first-to-check)

"archetype"は "printf", "scanf" のようにチェック時に参考にする関数名を指定する。gcc側で選択肢は決められており、GCC4の場合は次の4つのいずれかとなる。

printf, scanf, strftime, strfmon

"string-index"は書式指定文字列が引数の何番目かを指定する(1始まり)。
"first-to-check"は書式に埋め込む・あるいは関連する可変長引数が何番目から始まるかを指定する(1始まり)。

printfの例を簡単に紹介する。
format_test.c:

// p1,p2,p3は適当に挟み込んだ引数。
// arhetype = printf形式
// string-index : 3番目の引数、const char *formatを書式指定文字列としてチェック
// first-to-check : p3をスキップし、5番目の引数からformatとの組み合わせをチェック
extern void myprintf(int p1, int p2, const char *format, int p3, ...)
                __attribute__ ((format(printf, 3, 5)));

void foo()
{
    myprintf(1, 2, "s = %s\n", -1, 5); // "%s"なのにintの5
    myprintf(1, 2, "n = %d, %d, %d\n", -1, 1, 2); // 引数が足りない
}

まず"-Wall"無しでコンパイルすると、エラーも警告も表示されず、コンパイルは正常に終了する。

$ gcc -c format_test.c

続いて "-Wall" をつけるとformatとのミスマッチについて警告が表示されるようになる。

$ gcc -Wall -c format_test.c
format_test.c: In function ‘foo’:
format_test.c:6: 警告: format ‘%s’ expects type ‘char *’, but argument 5 has type ‘int’
format_test.c:7: 警告: フォーマットへの引数が少なすぎます

__attribute__ noreturn

abort(3)やexit(3)は、呼んだ関数から戻らない。GCCはこれらについては自動的に判別する。それ以外の、ユーザーが定義した関数で呼び出し元へ戻らない場合は noreturn を使うことで警告表示を消すことが出来る。

文章だけでは分かりづらいので、実際の例を示す。まず警告が表示されるパターンを示す。
noreturn_test.c:

extern void exitnow();

int foo(int n)
{
    if (0 < n) {
        exitnow(); // returnせずにfoo()が終了する
    } else {
        return 0;
    }
}

"-Wall"付でコンパイルすると、exitnow()のルートでreturn無しでfoo()が終了する為警告が表示される。

$ gcc -Wall -c noreturn_test.c
noreturn_test.c: In function ‘foo’:
noreturn_test.c:10: 警告: control reaches end of non-void function

__attribute__( (noreturn) ) を付けて実行すると、警告は表示されなくなる。

extern void exitnow() __attribute__((noreturn));
(以下同じ)

コンパイルしてみる。

$ gcc -Wall -c noreturn_test.c
$

exit(3)やabort(3)の宣言を確認してみると、同様にnoreturnが指定されていることが確認出来た(ただし、"__noreturn__"とアンダースコア付になっている)。

/usr/include/stdlib.h:

extern void abort (void) __THROW __attribute__ ((__noreturn__));
extern void exit (int __status) __THROW __attribute__ ((__noreturn__));

__attribute__ const

副作用が無く引数のみで処理が完結し、引数のポインタの内容を見ない関数の場合に、gcc側でキャッシュなど最適化処理を行うようになる。stdlib.hではabs(3)やdiv(3)などで使われている。
より制限が緩いものに pure 属性もあり、こちらはstrlen(3)などポインタの内容を見る場合にも適用出来る。

/usr/include/stdlib.h:

extern int abs (int __x) __THROW __attribute__ ((__const__)) __wur;
extern div_t div (int __numer, int __denom)
     __THROW __attribute__ ((__const__)) __wur;

/usr/include/string.h:

extern size_t strlen (__const char *__s)
     __THROW __attribute_pure__ __nonnull ((1));

複数組み合わせてみる

複数の"__attribute__"を組み合わせることも出来る。"__attribute__"を連続して続けることも出来るし、一つの"__attribute__"の中にまとめることも出来る。

例:

extern void die1(const char *format, ...)
    __attribute__((noreturn))
    __attribute__((format(printf, 1, 2)));

extern void die2(const char *format, ...)
    __attribute__((noreturn, format(printf, 1, 2)));

int foo(int n) {
    switch (n) {
        case 1: die1("n = %d\n", n);
        case 2: die2("n = %d\n", n);
        default: return n;
    }
}

GCC以外のコンパイラとの互換性と関数属性に関するその他の注意点

__attribute__機能はGCC以外のコンパイラでは無視されても問題ないよう、注意深く設計されている。空のマクロに展開することで、GCC以外のコンパイラに対応出来る。__attribute__は複数のパラメータを取るようになっているが、これまでの例のように2重括弧でくくることで一つの引数としてマクロで扱えるようになる。

#ifndef __GNUC__
#  define  __attribute__(x)  /*NOTHING*/
#endif

最後に、その他の注意点として関数属性は宣言時にのみ指定できる点に注意する。
関数の定義部では使えない。
このため、関数属性を使う時は必ずプロトタイプ宣言を用意してそちらに"__attribute__"を指定する必要がある。

参考資料



プレーンテキスト形式でダウンロード
現在のバージョン : 1
更新者: msakamoto-sf
更新日: 2010-02-05 15:11:39
md5:37c16f1bd9cc791ed82ea3a6533c5bac
sha1:cd6cf5eecd83551dabcb54cd071fc7a916ee2066
コメント
コメントを投稿するにはログインして下さい。