かなり長いタイトルですが、そのまんまの内容で引っかかってる。
サンプルプログラムは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’
そうじゃないらしい。
うむむむ、切り分けが難しいのだ。
切り分けでどうにかなる問題かも怪しいのだが。
(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でリンクする必要があるのだと思います.
この予測を裏付ける検証として,引数に変数を与えた時にコンパイルオプションで最適化するとエラーが消えます.
少々コメントが長くなって来ましたので,実験は別にまとめさせてもらいます.