[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.23 関数属性の宣言

GNU C では、読者のプログラムで呼び出している関数について 一定の宣言を行うと、GNU CC が関数呼出しを最適化したり、読者の コードをさらに注意深く検査することの手助けとなる。

キーワード __attribute__ を使うことにより、宣言を書くときに 特別な属性を指定することができる。このキーワードの後に、二重の括弧で 囲んだ属性の指定を置く。関数に対しては現在 9 個の属性、 noreturnconstformatno_instrument_functionsectionconstructordestructorunusedweak が 定義されている。section を含むその他の属性は、 変数宣言(see section 変数の属性の指定)や型(see section 型の属性の指定) に対してサポートされている。

また、各キーワードの前後に ‘__’ を付けた属性を指定しても良い。 これにより、同じ名前のマクロがある可能性を心配することなく、 ヘッダファイルの中でこれらの属性を使うことができる。 例えば、noreturn の代わりに __noreturn__ を使うことができる。

noreturn

標準ライブラリ関数のうち幾つかは、abortexit の ように戻ることが不可能な関数がある。GNU CC はこのことを既に知っている。 プログラムの中には、決して戻らない関数を自分で定義することもあるだろう。 読者は、そういう関数を noreturn と宣言することで、 その事実を GNU CC に知らせることができる。 例えば、以下のように書いたとする。

 
void fatal () __attribute__ ((noreturn));

void
fatal (…)
{
  … /* Print error message. */ …
  exit (1);
}

キーワード noreturn を付けると、GNU CC は関数 fatal が 戻りえないことを想定する。そうすると、fatal が戻った場合に 何が起きるかを考えることなしに最適化を行うことができる。 これによりちょっとだけ良いコードになる。もっと重要なのは、 変数が初期化されていないという見せ掛けの警告を押さえることができる点に ある。

呼出し側関数によりセーブされるレジスタが、noreturn 関数を 呼び出す前にリストアされると想定してはいけない。

nonreturn と指定された関数に void 型以外の戻り値型を 持たせるのは意味がない。

noreturn 属性は、GNU C のバージョン 2.5 以前では実装されていない。 戻ることのない関数を宣言するもう一つの方法は、次のようになる。 この方法は、現在のバージョンでも使えるし、古いバージョンの幾つかでも 使える。

 
typedef void voidfn ();

volatile voidfn fatal;
const

多くの関数は引数以外の値を見ないし、戻り値以外の値に影響を与えない。 このような関数は、単なる算術演算子と同じように、 共通部分式削除とループ最適化の適用を受ける。 こういう関数は属性 const を付けて宣言すべきだろう。 例えば、

 
int square (int) __attribute__ ((const));

とすると、架空の関数 square は、プログラムが示すよりも 呼び出す回数が少なくても安全であることを表す。

const 属性は、GNU のバージョン 2.5 以前では実装されていない。 関数に副作用が無いことを宣言する別の方法としては、以下の方法がある。 これは現在のバージョンと幾つかの古いバージョンで動作する。

 
typedef int intfn ();

extern const intfn square;

この方法は GNU C++ 2.6.0 以降では使えない。 言語仕様で ‘const’ は、戻り値に付けなければならないからである。

ポインタの引数があり、それにより指し示されるデータを参照している 関数は、const と宣言してはならないことに注意。 同様に、非 const 関数を呼び出している関数は普通は const であってはならない。void 型の関数に const を 付けるのは意味がない。

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

属性 format は、関数が printfscanfstrftime 形式の引数を取り、その引数をフォーマット指定文字列に 対して型検査を行うことを指定する。例えば、

 
extern int
my_printf (void *my_object, const char *my_format, ...)
      __attribute__ ((format (printf, 2, 3)));

と宣言すると、GNU CC は、my_printf への呼出しの引数を、 printf 形式のフォーマット指定文字列である引数 my_format との一貫性について検査を行わせる。

パラメータ archetype は、フォーマット文字列がどのように 解釈されるかを決め、printfscanfstrftime のどれかにしないといけない。 パラメータ string-index はどの引数がフォーマット指定文字列の 引数かを指定する(1 から始める)。一方、first-to-check は フォーマット指定文字列に対して検査を行うべき最初の引数の番号である。 検査に使える引数がない関数(例えば vprintf)に対しては、 3番目のパラメータをゼロと指定すること。この場合、 フォーマット文字列についてだけ一貫性があるかどうかの検査が行われる。

上の例で言うと、フォーマット文字列 (my_format) は 関数 my_print の二番目の引数であり、検査すべき引数は 3番目の引数から始まるので、format 属性の正しいパラメータは 2 と 3 になる。

format 属性を使うと、フォーマット文字列を引数として取る 読者自身の関数を認識して、GNU CC がこれらの関数への呼出しに誤りが ないかどうか検査させることができる。 GCC は ANSI ライブラリ関数 printffprintfsprintfscanffscanfsscanfstrftimevprintfvfprintfvsprintf については、 警告を要求したとき(‘-Wformat’ を指定したとき)は、常に フォーマットを検査するので、ヘッダファイル ‘stdio.h’ を 修正する必要はない。

format_arg (string-index)

format_arg 属性は、関数が、 printf あるいは scanf 形式の引数を取り、それを変更し(例えば別の言語へ翻訳する)、さらに printf あるいは scanf 形式の関数に渡すということを 指定する。例えば、

 
extern char *
my_dgettext (char *my_domain, const char *my_format)
      __attribute__ ((format_arg (2)));

と宣言すると、GCC は、結果が printfscanfstrftime 形式の関数に渡される my_dgettext への呼出しに 現れる引数を、printf 形式のフォーマット文字列引数 my_format と比べて、一貫性があるかどうかを検査する。

パラメータ string-index は、どの引数がフォーマット文字列引数 であるかを指定する(1から始まる)。

format-arg 属性を使うと、フォーマット文字列を修正するような 読者自身の関数を認識して、引数が、読者自身の関数の一つへの呼出しである ような printfscanfstrftime関数の呼出しを 検査できるようにする。GCC は、gettextdgettextdcgettext を常にこのように取り扱う。

no_instrument_function

-finstrument-functions’ を指定すると、プロファイリングを行う 関数への呼出しが、ほとんどのユーザがコンパイルした関数の入り口と 出口に生成される。この属性を指定した関数については、それが行われない。

section ("section-name")

GCC は、普通、生成したコードを text セクションに置く。 だが、時によっては、追加のセクションが必要になったり、 ある特定の関数を特別なセクションに置く必要が出てくる。 section 属性は、関数を特定のセクションに置くことを指定する。 例えば、

 
extern void foobar (void) __attribute__ ((section ("bar")));

と宣言すると、関数 foobarbar セクションに置かれる。

ファイル形式によっては、勝手にセクションを作ることをサポートしていないので、 section 属性は全てのプラットフォームで使えるわけではない。 あるモジュールの内容全部をある特定のセクションに移したい場合は、 リンカの機能を使うことを考えてみて欲しい。

constructor
destructor

constructor 属性は、指定した関数が、プログラムの実行が main () に入る前に自動的に呼び出されるようにする。 同じように、destructor 属性を指定すると、その関数が、 main () が終了した後か exit () が呼ばれた後に、 自動的に呼び出されるようにする。これらの属性を指定した関数は、 プログラムの実行の間に暗黙に使われるデータを初期化するのに 使える。

これらの属性は Objective C については現在実装されていない。

unused

この属性は、関数につけると、その関数は使われない可能性があるという ことを表す。GNU CC はこの関数に対して警告を出さないようになる。 GNU C++ は現在、パラメータが C++ で有効でない定義としては、 この属性をサポートしていない。

weak

weak 属性を使うと、それを指定した宣言は、グローバルシンボル ではなくウィークシンボルとして生成される。これは主に ユーザコードで上書き可能なライブラリ関数を定義するのに使われるが、 関数でない宣言でも使うことができる。 ウィークシンボルは ELF ターゲットでサポートされている。 GNU アセンブラとリンカを使う場合は a.out ターゲットでもサポートされる。

alias ("target")

alias 属性を指定すると、その宣言は何か別のシンボルの別名として 生成される。別のシンボルはかならず指定しなければならない。 例えば、

 
void __f () { /* do something */; }
void f () __attribute__ ((weak, alias ("__f")));

とすると、‘f’ を ‘__f’ のウィークな別名として宣言する。 C++ では、元の名前にはエンコードされた名前を使わなければならない。

全てのターゲット機種でこの属性がサポートされているわけではない。

no_check_memory_usage

-fcheck-memory-usage’ を指定すると、サポート・ルーチンの 呼出しがほとんどのメモリアクセスの前に生成され、サポートコードが 使用状況を記録できるようにし、かつ非初期化また未割当のメモリ領域を使ったのを 検知できるようにする。 GCC が正しく扱えないので、asm 文は許されない。 関数にこの属性を付けて宣言すると、その関数に対するメモリ検査コード を出さないようにし、異なるオプションを使って別個にコンパイルせずとも asm 文が使えるようになり、さらに、このオプションを使ってコンパイル しても無限再帰になることなく、望むなら読者自身のサポート ルーチンを書けるようになる。

regparm (number)

Intel 386 では、regparm 属性を指定すると、number 個の 整数引数を、スタックの代わりにレジスタ EAXEDXECX で渡すようになる。可変数引数を取る関数は、依然として全部の引数が スタックで渡される。

stdcall

Intel 386 では、stdcall 属性を指定すると、可変数引数を 取るのでない限り、呼ばれた関数が、引数を渡すのに使われたスタック をポップすると想定する。

Windows NT 向け PowerPC コンパイラは、現在、stdcall 属性を 無視する。

cdecl

Intel 386 では、cdecl 属性を指定すると、可変数引数を 取るのでない限り、呼びだし側関数が、引数を渡すのに使われたスタック をポップすると想定する。これは、‘-mrtd’ オプションの効果を 打ち消すのに使える。

Windows NT 向け PowerPC コンパイラは、現在、cdecl 属性を 無視する。

longcall

RS/6000 と PowerPC では、longcall 属性を指定すると GCC はその関数を常にポインタ経由で呼び出すようにする。 これにより、現在位置から 64 メガバイト(67,108,864バイト)以上 離れた位置にある関数も呼び出せるようになる。

dllimport

PowrPC で Windows NT を稼働している場合、dllimport 属性を 指定すると、GCC は、その関数を、Windows NT dll ライブラリにより設定 される関数ポインタへのグローバルポインタ経由で呼び出す。 このポインタ名は、__imp_ とその関数名を組み合わせて作られる。

dllexport

PowrPC で Windows NT を稼働している場合、dllexport 属性を 指定すると、GCC は、関数ポインタへのグローバルポインタを提供する。 これにより、その関数は dllimport 属性を指定することで 呼び出せるようになる。 このポインタ名は、__imp_ とその関数名を組み合わせて作られる。

exception (except-func [, except-arg])

PowrPC で Windows NT を稼働している場合、exception 属性を 指定すると GCC は、宣言された関数に対し生成される構造化例外テーブルの エントリを修正する。文字列または識別子 except-func が、 構造化例外テーブルの 3番目のエントリに置かれる。これは、ある関数を 表す。この関数は、例外が発生したときに例外処理機構から呼び出される。 指定されていれば、文字列あるいは識別子 except-arg が、 構造化例外テーブルの4番目のエントリに置かれる。

function_vector

このオプションは、H8/300 と H8/300H において、指定した関数が 関数ベクタを経由して呼び出される必要があることを示すのに使う。 関数ベクタ経由して関数を呼び出すと、コードサイズが小さくなる。 ただし、関数ベクタ大きさに制限があり(H8/300 では最大 128 エントリ、 H8/300H では最大 64 エントリである)、割り込みベクタとスペースを 共有する。

このオプションを正しく動作させるには、GNU binutils バージョン 2.7 以降の GAS と GLD を使わなければならない。

interrupt_handler

このオプションは、H8/300 と H8/300H において、指定した関数が 割り込みハンドラであることを示すのに使う。GCCは、この属性が指定されていると、 割り込みハンドラの中で使うのに適した、関数入り口点と出口点の命令列を 生成する。

eightbit_data

このオプションは、H8/300 と H8/300H において、指定した変数が 8ビットデータセクションに置かれるべきであることを示すのに使う。 GCC は、8ビットデータ領域にあるデータについては、特定の操作に対し より効率の良いコードを生成する。 8ビットデータ領域は、合計 256 バイトのデータに制限されていることに注意。

このオプションを正しく動作させるには、GNU binutils バージョン 2.7 以降の GAS と GLD を使わなければならない。

tiny_data

このオプションは、H8/300 と H8/300H において、指定した変数が 小(tiny)データセクションに置かれるべきであることを示すのに使う。 GCC は、小データ領域にあるデータについてのロードとストアに対し より効率の良いコードを生成する。 小データ領域は、合計32キロバイトよりちょっと下回る大きさに制限されている ことに注意。

interrupt

このオプションは、M32R/D において、指定した関数が 割り込みハンドラであることを示すのに使う。GCCは、この属性が指定されていると、 割り込みハンドラの中で使うのに適した、関数入り口点と出口点の命令列を 生成する。

model (model-name)

このオプションは、M32R/D において、オブジェクトのアドレス可能性と 関数に対して生成されるコードを設定するのに使う。 識別子 model-name は、smallmediumlarge のうちのどれか一つである。これらはそれぞれコード・モデルを表す。

small モデルオブジェクトは、メモリの下位 16MB に置かれ (これにより、それらのアドレスは ld24 命令によりロード可能 になる)、bl 命令で呼びだし可能である。

medium モデルオブジェクトは、32ビットアドレス空間のどこに置いても良く (GCC は、これらのアドレスをロードするのに seth/add3 命令列を 生成する)、bl 命令で呼びだし可能である。

large モデルオブジェクトは、32ビットアドレス空間のどこに置いても良く (GCC は、これらのアドレスをロードするのに seth/add3 命令列を 生成する)、bl 命令では到達可能でない可能性がある (GCC は、さらに遅い seth/add3/jl 命令列を生成する)。

複数の属性を一個の宣言の中で指定するには、二重括弧の中にカンマで区切って 並べるか、一つの属性宣言の直後に属性宣言を続けて書けば良い。

__attribute__ 機能に反対する人達もいて、代わりにANSI C の #pragma を使うべきだと主張している。#pragma を 使わないのには理由が二つある。

  1. #pragma コマンドをマクロから生成するのは不可能である。
  2. 他のコンパイラで同じ #pragma がどんな意味を持つかわからない。

この二つの理由は、#pragma を使えば良いのではないかと提案されている ほとんど全てのアプリケーションに対して適用される。 #pragma は何に対して使っても基本的に誤りなのである。


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

This document was generated using texi2html 1.78.