6
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

C言語Advent Calendar 2016

Day 23

C言語のinlineにまつわる未規定動作で遊ぶ

Last updated at Posted at 2016-12-22

C言語のinlineにまつわる未規定動作で遊ぶ

環境

  • gcc バージョン 6.2.1 20160830 (GCC)
  • clang version 3.9.0 (tags/RELEASE_390/final)

n1570の6.7.4を精読すれば終わる話

パラグラフ7にこんなことが書いてある.

Any function with internal linkage can be an inline function. For a function with external linkage, the following restrictions apply: If a function is declared with an inline function specifier, then it shall also be defined in the same translation unit. If all of the file scope declarations for a function in a translation unit include the inline function specifier without extern, then the definition in that translation unit is an inline definition. An inline definition does not provide an external definition for the function, and does not forbid an external definition in another translation unit. An inline definition provides an alternative to an external definition, which a translator may use to implement any call to the function in the same translation unit. It is unspecified whether a call to the function uses the inline definition or the external definition.

超訳すると,

  • staticがついてる関数でinlineがついてるものはインライン関数
  • staticがついてない関数で関数宣言にinlineがついてたら,同じファイルに定義がないといけない
  • staticがついてない関数で関数宣言にinlineがついてて,externがなかったら,関数定義はインライン定義←重要
  • インライン定義と外部定義は共存できる←超重要
  • どっちを呼ぶかは未規定←これを試したい

言っときますが,これは雰囲気をつかむためのもので,この和訳は正確ではありません.

本題

皆さん,C言語を覚えたての頃にWikipediaでC言語を調べて,こんな記述に出会ったことがあるのではないでしょうか.

C99

主な追加機能:

  • インライン関数(inline キーワード)

で,gccやclangでこんなコードを書いて試すんですね.

main.c
#include <stdio.h>

inline int f(void) {return 42;}

int main(void) {printf("%d", f());
}

で,gcc main.cなんて書いてコンパイルすると,こんなエラーが出るわけです.

/tmp/cc341wl7.o: 関数 `main' 内:
main.c:(.text+0x5): `f' に対する定義されていない参照です
collect2: エラー: ld はステータス 1 で終了しました

頭脳は大人の小学生「あれれ~おかしいぞ~. fはmain.c中にちゃんと定義されてるよ~」
おっちゃん「フン,どうせ最適化がかかってないんだろ.ほらよ」

gcc -O main.c.無事コンパイルできました.
./a.outとやると無事42が出力されています.

オブジェクトファイル中にfに定義されてないということが問題なので,例えば,

f.c
int f(void){return 0;}

と書いて,gcc main.c f.cとしてコンパイルしましょう.
コンパイルが通ってa.outが出来ますが,これを実行すると0が出力されます.

これが,どっちを呼ぶかは未規定ということなんですね.
なので,あえてstatic宣言を付けずにインライン定義として関数を定義し,関数を呼び出すときは外部定義の関数を呼び出す,なんてことができるわけです.
まあ,あくまで未規定動作なので,用法,容量を守って正しく使いましょう.

因みに,こんな動作に惑わされたくない人は,関数定義にexternかstaticを明示しましょう.
externとして定義する場合は,外部に公開されるのを忘れずに.

6
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?