math.hをインクルードして数学関数を使う場合にgccだけでコンパイルできない本当の理由は??

かなり長いタイトルですが、そのまんまの内容で引っかかってる。

サンプルプログラムはsqrt(x)でforで回して計算していくものですが、この形式だとgccでコンパイルするとググったら出てくる答えの通りにオプション -lm をつけないとエラーとなります。

[root@localhost program]# gcc k7-11.c
/tmp/ccVAr5gE.o: In function `main':
k7-11.c:(.text+0x4e): undefined reference to `sqrt'
collect2: ld はステータス 1 で終了しました

オプションをつけてコンパイルして得られる結果はこれ。

[root@localhost program]# ./a.out | head
  x   sqrt(x)
-------------
0.00      0.000
0.01      0.100
0.02      0.141
0.03      0.173
0.04      0.200
0.05      0.224
0.06      0.245
0.07      0.265
[root@localhost program]# ./a.out | tail
0.91      0.954
0.92      0.959
0.93      0.964
0.94      0.970
0.95      0.975
0.96      0.980
0.97      0.985
0.98      0.990
0.99      0.995
1.00      1.000

だがしかし、ソースをかえると、sqrtを使っていてもgccだけでコンパイルできちゃったのだ。

そーす

 1 #include <math.h>
  2 #include <stdio.h>
  3
  4 int main(void)
  5 {
  6     int     i;
  7     double  x;
  8
  9         printf("%5.3f\n",    sqrt(1 / 100.0));
 10         printf("%5.3f\n",    sqrt(5 / 100.0));
 11         printf("%5.3f\n",    sqrt(10 / 100.0));
 12         printf("%5.3f\n",    sqrt(15 / 100.0));
 13         printf("%5.3f\n",    sqrt(20 / 100.0));
 14         printf("%5.3f\n",    sqrt(25 / 100.0));
 15         printf("%5.3f\n",    sqrt(30 / 100.0));
 16         printf("%5.3f\n",    sqrt(35 / 100.0));
 17         printf("%5.3f\n",    sqrt(40 / 100.0));
 18         printf("%5.3f\n",    sqrt(100 / 100.0));
 19 }

けっか

[root@localhost program]# vim k7-11.c
[root@localhost program]# gcc k7-11.c
[root@localhost program]# ./a.out
0.100
0.224
0.316
0.387
0.447
0.500
0.548
0.592
0.632
1.000

違いの分かる男には簡単なのかもしれないけど、こっちからするとsqrt使ってるのにコンパイルできてるじゃん!ほんとの理由はなんなのさ!って思うんだ。

sqrtだけでなくてもsinでもcosでも同じくできる。だもんで、数学関数を使うときはオプションが居るという回答はちょっと違うんでないかい?と言いたい。

printf("%5.3f\n",    sqrt(1 / 100.0));
printf("%5.3f\n",    sqrt(5 / 100.0));
printf("%5.3f\n",    sqrt(10 / 100.0));
printf("%5.3f\n",    sqrt(15 / 100.0));
printf("%5.3f\n",    sqrt(20 / 100.0));
printf("%5.3f\n",    sqrt(100 / 100.0));
printf("%5.3f\n",    sin(20.0 * 100.0));
printf("%5.3f\n",    cos(35.3 * 100.0));
printf("%5.3f\n",    tan(32.5 * 100.0));
printf("%5.3f\n",    sqrt(100.0));
[root@localhost program]# ./a.out
0.100
0.224
0.316
0.387
0.447
1.000
0.930
0.408
-44.636
10.000

答えは近いのか遠いのか分からないけど、ただこうするだけでエラーとなるのね。

  1 #include <math.h>
  2 #include <stdio.h>
  3
  4 int main(void)
  5 {
  6     float   x=2;
  7
  8         printf("%5.5f\n",    sqrt(x));
  9 }

  1 #include <math.h>
  2 #include <stdio.h>
  3
  4 int main(void)
  5 {
  6         printf("%5.5f\n",    sqrt(2));
  7 }

見た目は全然ちがんだけど!(笑)

理由が分からないんだああああああああ。

あ、もしかしてsqrt(2)のときはmath.hを使ってないとか?

で、試してみた。

[root@localhost program]# gcc k7-11.c
k7-11.c: In function ‘main’:
k7-11.c:5: 警告: incompatible implicit declaration of built-in function ‘sqrt’

そうじゃないらしい。

うむむむ、切り分けが難しいのだ。

切り分けでどうにかなる問題かも怪しいのだが。

Related Posts


投稿者: Takeken

インターネット利用者のITリテラシーを向上したいという設定の2次元キャラです。 サーバー弄りからプログラミングまで手を付けた自称エッセイストなたけけんの物語。

「math.hをインクルードして数学関数を使う場合にgccだけでコンパイルできない本当の理由は??」への1件のフィードバック

  1. (Linux Mint 15基準ですので,ディレクトリ構造に多少差があるかもしれません.)

    /usr/include/math.hを読んでみると,各関数に対するプロトタイプ宣言がなされていません.
    ので,オブジェクトファイルとリンクしない状態で数学関数群がつかえるということ自体かなり不思議な現象です.

    —————————————————

    (結果的に特別得られるものはありませんでしたが)glibを読んでみました.
    WEBページ: http://www.gnu.org/software/libc/index.html
    ./glibc-2.19/math/
    ./w_sqrtf.c
    ./s_csqrtl.c
    ./w_sqrt.c
    ./s_csqrtf.c
    ./e_sqrtl.c
    ./w_sqrtl.c
    ./s_csqrt.c
    sqrt一つに対してもこれだけのバリエーションが有りました.
    多分w系が実数,s_c系が複素平面だと思います.
    e系はIEEE 754(IEEE 浮動小数点数演算標準)に則った計算だそうです.

    ———————————————————-

    この調査で確証は得られませんでした.
    以下は私の予測です.
    数学関数の引数に定数を入れた場合は実行時に値が変わり得ないのでコンパイラがlibmで計算(つまりgccの実装内でlibmにリンクされていると予測)し,バイナリに定数として組み込んでいる.
    対して変数を引数としている場合,実行時に関数へ値が渡される可能性があるので,コンパイラはソースにオブジェクトファイルを組み込んでいる.このとき,オブジェクトファイルの実体がわからないため,gccに-lmでリンクする必要があるのだと思います.

    この予測を裏付ける検証として,引数に変数を与えた時にコンパイルオプションで最適化するとエラーが消えます.
    少々コメントが長くなって来ましたので,実験は別にまとめさせてもらいます.

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です