C言語 math.hをコンパイルする時に-lmが必要ないとき

C言語でmath.hをインクルードしたファイルをコンパイルする時に

$ gcc hoge.c -lm

上記のように末尾に -lm オプションをつけてコンパイルするとエラーが出ないと言われた。
しかし、math.hをインクルードしたファイルでも、私のMacでは-lm オプションをつけないでコンパイルしてもエラーはでない。
そもそもなぜ-lm オプションをつけるのかというと、sqrtを使う際のライブラリがlibmの中にあるため、math.hだけをインクルードしてもリンクさせないと、コンパイルが通らないそうだ。
だが、私のMacではオプションなしでもコンパイル出来る。
そのことについて調べるのにもしかしたらubuntuとMacで違うのではないかと考え、ubuntuとMacでどのような違いがあるかを調べました。

まずmath.hを使うルート(平方根)を求めるプログラムを作りました。

#include <stdio.h>
#include <math.h>

int main(){
   double a = 2.0;
   printf("sqrt(%f) = %f\n", a, sqrt(a));

   return 0;
}

このコードを使って調査していきます。

そもそもmath.hとは

C言語には標準Cライブラリというものがあります。
C言語はもともと文字列操作や入出力などといった基本的な機能を内蔵していませんでした。
そのためライブラリというものが作られました。ライブラリとは大きな本棚のようなもので、本棚の中に沢山の本(ヘッダーファイル)が入っています。
ヘッダーファイルが文字の入出力(stdio.h)、数学的な演算(math.h)などを出来るファイルのことであり、これらを使うことで便利にプログラミングが出来るようになりました。
そして -lm オプションとはコンパイル時に数学ライブラリをリンク(つなぐ)ことで数学ライブラリを使えるようにするという意味を持っています。
l がリンク、m が数学ライブラリを表しています。

ubuntuでのコンパイル

ubuntuで先程のプログラムをコンパイルしていきます。

$ gcc sqrt.c

コンパイルすると、下記のようなエラーがでました。

/tmp/ccwAvTbN.o: In function `main':
sqrt.c:(.text+0x23): undefined reference to `sqrt'
collect2: error: ld returned 1 exit status

調べたところこれらのエラーは、sqrt関数を使うには算術演算ライブラリをリンクする必要があるため、末尾に -lm というオプションをつける必要があるようです。
では末尾に-lm オプションをつけてコンパイル、実行していきます。

$ gcc sqrt.c -lm
$ ./a.out
sqrt(2.000000) = 1.414214

きちんと平方根を求めることが出来ました。

Macでのコンパイル

つづいてMacで同様のプログラムをコンパイルしていきます。

$ gcc sqrt.c
$ ./a.out

-lm オプションをつけていないのにコンパイル出来てしまいました。
これらの謎を解き明かすために、なんのライブラリが参照されているかを確かめました。

参照ライブラリの確認

ライブラリを確認するために、math.hを使わないhello.cというプログラムも作りました。

#include <stdio.h>
int main (void){
    printf("hello\n");
    return 0;
}

ubuntuでライブラリの確認

コンパイル時に使われているライブラリを確認するにはubuntuではコンパイル後にldd a.outと記述します。

$ gcc hello.c
$ ldd a.out
linux-vdso.so.1 (0x00007fffee239000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f4dc62e0000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f4dc68d3000)

$ gcc sqrt.c
$ ldd a.out
linux-vdso.so.1 (0x00007ffc5c333000)
    libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fe97309b000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fe972caa000)
    /lib64/ld-linux-x86-64.so.2 (0x00007fe97363b000)

これをみると、sqrt.cのほうにはlibm.so.6というライブラリが使われていることがわかります。

Macでのライブラリの確認

Macにはlddコマンドが使えないので、代わりに同じ機能をもつotool -Lコマンドを使用します。

$ gcc hello.c
$ otool -L ./a.out
./a.out:
    /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1252.250.1)

$ gcc sqrt.c
$ otool -L ./a.out
./a.out:
    /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1252.250.1)

これをみると、どちらも同じlibSystem.B.delibというライブラリが使われいる事がわかります。

なぜ -lm オプションがいらないのか

linuxなどで使われている数学ライブラリはMacにはありません。
Macは常にリンクされているライブラリ libSystem ライブラリに数学ライブラリのようなものが搭載されいます。
そのため、 -lm オプションを付ける必要がなく、コンパイルすることが出来ます。

おわりに

はじめはなぜ私のMacではコンパイル出来るのに、ネットの皆さんの環境だとコンパイル出来ないのかすごく謎だったのですが、もしかしてLinuxを使っているのでは?と思い今回はubuntuで試してみました。
またLinuxでmath.hをインクルードしても、オプションをつけずにコンパイルが出来る時があるそうですが、その場合はmath.hを計算中で使っていなかった場合が考えられるそうです。
プログラミングって奥が深いです。