Linuxカーネルソースの場合と同様に、インライン形式で実装された小さな本体を持つ多くの関数と、MACROに実装された小さな本体を持つ他の多くの関数を見つけました。
ですから、どちらを選ぶかを決める際の考慮事項は何でしょうか。どちらも適切だと思います。それとも、これは一種の個人的なスタイルですか?
Linuxカーネルソースの場合と同様に、インライン形式で実装された小さな本体を持つ多くの関数と、MACROに実装された小さな本体を持つ他の多くの関数を見つけました。
ですから、どちらを選ぶかを決める際の考慮事項は何でしょうか。どちらも適切だと思います。それとも、これは一種の個人的なスタイルですか?
マクロは型チェックを行わず、他の特別な考慮事項 (式を 1 回だけ評価するなど) を必要とするため、可能な限り避ける必要があります。ただし、型をチェックしないことには、C89 でジェネリック関数を記述できるという利点があります。
また、実際のinline
関数を記述すると、(カーネルの構成で有効にできる特定のフラグを使用して) パフォーマンス上の利点がある場合、コンパイラはそれをインライン展開することもできますが、これは単純なテキスト置換であるマクロでは不可能です。
Linux カーネルの多くのコードがかなり前から存在していたことを思い出してください。マクロの中には、関数が行う/できる/すべきではない、入力/環境の他の部分で「巧妙なこと」を行うものもあります。
上記で説明したように、関数に関する「落とし穴」が少ないため、原則として、可能な限り (インライン) 関数を使用します。
巧妙な置換またはその他の「マクロでしかできない」ことを行う場合は、可能であれば、関数への引数を形成するマクロを作成することをお勧めします。例えば
#define ASSERT(x) do { if (!(x)) { fprintf(stderr, "Assertion failed on %s:%d", __FILE__, __LINE__); exit(1); } while(0);
次のように実行できます。
#define ASSERT(x) do_assert(!!(x), __FILE__, __LINE__);
void do_assert(bool val, const char *file, int line)
{
if (!val)
{
fprintf(....);
exit(1);
}
}
たとえば、デバッグ ビルドで、if 内のアサーションにブレークポイントを設定すると、アサーションが失敗するたびに、そこに到達した方法を確認できます。マクロでは、それは不可能です。
最新のコンパイラは、可能性が半分であれば、適切な場合に物事をインライン化するという非常に優れた仕事をします。
テキスト置換であるマクロは、インライン関数以上のことを行うことができます。また、望ましくない副作用がある場合もあります。よく知られているスワッピング マクロを考えてみましょう。
#define SWAP(a,b) {int t=a; a=b; b=t}
SWAP(i,t[i])
不要なことをする通知。
インライン化された関数には、関数呼び出しのようなセマンティクスがあるという利点があります (引数は 1 回評価されるなど)。
したがって、それは主にコーディング スタイルの問題であり、マクロと関数が異なる種類の動物であることを認識することです。
そして、あなたの質問はカーネルに特に関連していません。どの C (または C++) プログラムにも同じ問題があります。