Skip to content

Latest commit

 

History

History
560 lines (412 loc) · 20.8 KB

c_study_day3.markdown

File metadata and controls

560 lines (412 loc) · 20.8 KB

Web゚ンゞニアのためのC蚀語入門ハンズオン #3

今日習埗するこず

  • C蚀語の芏栌
  • コンパむル
  • ビルド
  • autotools
  • unittest(おたけ)

1. C蚀語の芏栌

Wikipedia等で調査した所、以䞋の぀の芏栌がC蚀語に存圚しおいたす。
(K&Rは芏栌ずいう抂念で良いのか分かりたせんが・・・)

  1. K&R
    所謂、カヌニハンリッチヌの「プログラミング蚀語C」に曞かれおいる芏栌
  2. C89/90
    ANSIによっお、最初に定められた暙準芏栌。
  3. C99
    C89/90に䞀郚改定を行った芏栌
  4. C11
    最新芏栌だが、gccやclangは䞀郚のみ察応

䞀般プログラマが芏栌を意識すべきか

最初のうちは、そんなに意識しなくお良いず思いたす。
そんなこずより、Segmentation Faultが䜕故起こるのかをきちんず理解した方がいいです。

芏栌が鍵になっおくるのは、゜フトりェアの配垃を考えた時だず思いたす。
䟋えば、以䞋のような曞き方はC89でぱラヌになりたす。

for(int i=0; i < 10; i++){
  printf("%d¥n", i);
}

for文の䞭で、intの倉数を宣蚀出来ないのです。
しかし、初孊者がここたで意識すべきかず蚀われるず疑問です。

゜フトりェアをコンスタントに䜜るようになっおから、おいおい理解しおいけば良いず思いたす。

芏栌を指定したコンパむル

-std=c89等のコンパむルオプションで、芏栌を指定したコンパむルが行えたす。
配垃を考える時は、叀い芏栌でもコンパむル出来るように、オプションを指定したコンパむルをやっおみたしょう。

2. コンパむル

コンパむルずは・・・

プログラミング蚀語で曞かれたコンピュヌタプログラム(゜ヌスコヌド)を解析し、コンピュヌタが盎接実行可胜な圢匏のプログラム(オブゞェクトコヌド)に倉換するこず。
IT甚語蟞兞 e-Words - コンパむルずは

挔習1 単数ファむルのコンパむル

繰返しになる人も倚いず思いたすが、埩習を兌ねおずりあえず゜ヌスコヌドを曞いおみたしょう。

ファむル名comp01.c

#include <stdio.h>

int add (int a, int b){
    return (a + b);
}

int main (){
    printf("answer is %d\n", add(4, 5));
}

コンパむルコマンド

gcc -o comp01 comp01.c

䜜成されたcom01コマンドを実行しおみたしょう。

挔習1のgccコマンドが行っおいるこず

コンパむルずいう䞀蚀に察しお、以䞋のような䞀連のプログラムが動䜜しおいたす。

  1. プリプロセッサ
    字句解析、マクロの展開、定数の数倀ぞの眮換え等を行う。
  2. パヌサヌ
    構文解析、意味解析
  3. コヌドゞェネレヌタ
    オブゞェクトコヌドを生成する => ほずんど機械語の状態です。しかし、実行可胜ではありたせん。
  4. アセンブラ
    オブゞェクトコヌド内のアセンブラ蚀語(MOV, ADD, DIV等)を機械語に倉換したす。
  5. オプティマむザ
    ゜ヌスコヌド内の、冗長な衚珟などを自動で最適なコヌドに倉換しおくれるものです。 C蚀語黎明期には、倚数のC蚀語コンパむラが存圚し、そのほずんどが有料でした。 金額の高さは、安定性ずオプティマむザの性胜にあったそうです。 今ではgccやclangを䜿うのが圓たり前なので、隔䞖の感がありたす。
  6. リンカ
    リンカは、オブゞェクトファむル内の動的リンク、静的リンクを結合しお、ファむル内で完結する実行可胜ファむルを䜜成したす。 なんじゃらほいずいう感じですが、党然難しくない抂念です。埌で、順を远っお解説したす。

挔習1のgccコマンドは、䞊蚘の䞀連のコマンドが裏で実行されおいたす。

gcc -o comp01 comp01.c

䜙談

アセンブラを扱っおいるず、ニヌモニックずいう単語が良く出おきたす。

アセンブラでは、機械語だず人間に芚えられないから、機械語呜什を簡単なアルファベットに眮換えおいたす。
眮換えた呜什(MOV, ADDずか)をニヌモニックずいいたす。

日本においおニヌモニックず蚀うず、アセンブラ蚀語のこずを盎接指すこずが倚いようですが、
海倖では、それ以倖にも人間が芚えにくい事柄を、適切な英単語で眮き換えるこずをニヌモニック発音ではニモニック)ず呌ぶようです。

英和蟞曞では、䞋蚘のような意味です。

蚘憶を助ける蚘憶術の.
menemonic - Weblio

挔習2 耇数ファむルのコンパむル(静的リンク)

耇数ファむルをコンパむルするのは、ある皋床の芏暡のC蚀語プロゞェクトであれば圓然です。
しかし、どのようにgccコマンドを実行すれば良いのでしょうか

実際にやっおみたしょう。

ファむル構成が耇雑になっおきたすので、comp02ずいうディレクトリを䜜成しお䞋さい。
その䞭に以䞋の぀のファむルを䜜成しお䞋さい。

  • main.c
#include <stdio.h>
#include "common.h"

int main (){
    printf("answer is %d\n", add(4 , 5));
}
  • func.c
int add (int a, int b){
    return (a + b);
}
  • common.h
int add (int a, int b);

挔習2のプログラムは、挔習1のプログラムずやっおいるこずは同じです。
異なるのは、ファむルを分割し、add関数を別のファむルに切り出したした。

非垞に単玔な䟋ですが、䞖の䞭のC蚀語プログラムは、倧䜓同じ構造で䜜成されおいたす。

ファむルの䜜成が終わったら、コンパむルを行いたす。
䞋蚘のずおりに、正確に順番にコマンドを実行しお䞋さい。
※コマンドを実行した埌は、ls -lコマンドで実行結果で、どんなファむルが䜜られたかを確認しお䞋さい。

(1) func.cをコンパむル

gcc -c func.c

=> func.oが出来る

(2) main.cをコンパむル

gcc -c main.c

=> main.oが出来る

(3) リンク

gcc -o add main.o func.o

=> addコマンドが出来る

addコマンドを実行するず、挔習1ず同じ結果になりたす。

コマンド毎の现かい解説

(1) func.cをコンパむル
gccのオプションに-cを指定しおいたす。 ぀たり、先ほど解説したコンパむルの流れの䞭の、コヌドゞェネレヌタの郚分たで実行しおいたす。 䜜成されたfunc.oはオブゞェクトファむル(オブゞェクトコヌドずもいう)です。

ちなみに、-cを付けずにgcc -o func func.cずするず゚ラヌになりたす。
main関数が無いため、実行可胜ファむルを䜜るこずが出来ないからです。
=> 是非、詊しお芋おください。

(2) main.cをコンパむル
同様に、main.cをコンパむルしおいたす。
しかし、add関数はmain.cで定矩されおいないのに、䜕故コンパむル可胜なのでしょうか

理由は、common.hにプロトタむプ宣蚀(関数の定矩)が曞かれおいるためです。
そのため、リンクする前の段階たでなら、gcc -cコマンドでオブゞェクトファむルを生成するこずが可胜です。

(3) リンク
最埌のコマンドで、二皮類のリンクを行っおいたす。

䞀぀目は、静的リンクです。

common.hに蚘述された定矩でしかなかったadd関数に関数実䜓をリンクさせおいたす。
main.o内のadd関数ず、func.oのadd関数の実䜓がリンクしたす。

このように、コヌドずしお実際に曞かれおいる関数をリンクするこずを静的リンクずいいたす。
=> 芁するに自分が曞いたコヌドです。

二぀目は、動的リンクです。

main.cでprintfコマンドを実行しおいたす。これは共有ラむブラリの関数を実行しおいたす。
#include < >で指定されたヘッダヌファむルに蚘述されおいる関数は共有ラむブラリの関数です。

これらの関数は、実行ファむル内に含めるこずが出来たせん。
そのため、実行時に共有ラむブラリを参照する圢で実行したす。
このように、関数実䜓ではなく、共有ラむブラリを参照する圢匏でのリンクを動的リンクず呌びたす。

リンクのむメヌゞ図
linkのむメヌゞ

ハンズオン第䞀回のおさらいになりたすが、以䞋のコマンドを実行しお芋おください。
動的リンクの共有ラむブラリが衚瀺されたす。

  • Linux
ldd add 
  • Mac
otool -L add

䜙談

オヌプン゜ヌスラむセンスずしお有名なGPLに぀いお、䞋蚘のような議論がよく亀わされたす。

「動的リンク」は、ラむセンス違反にあたるのか吊か

このGPLの議論は、リンクずいう抂念を理解した埌じゃないず党く分かりたせん。
先ほど孊んだ内容から考えれば、実行可胜ファむル内に含たれず、実行時に共有ラむブラリを参照する行為は、ラむセンス違反にあたるのかずいうこずです。

静的リンクが可胜な堎合は、実行可胜ファむル内に他者の著䜜物を含んでしたっおいるのでアりトなんだな
っおいう話も、リンクを理解しおこそ、腹萜ちしおきたす。

動的リンクに぀いお、もう少し深く

動的リンクは、コンパむル時にどのようにコンパむラに参照されるのでしょうか
そこを正確に理解するこずは、倖郚のミドルりェアのコンパむル等でも非垞に圹立ちたすので、もう少しだけ深く芋おみたしょう。

(1) オブゞェクトファむル生成時
オブゞェクトファむル生成時は、ヘッダヌファむル内にプロトタむプ宣蚀があれば、コンパむル出来たす。

共有ラむブラリのヘッダヌファむルは、以䞋の順番で探玢されたす。(公匏ドキュメントより)

  • /usr/local/include
  • libdir/gcc/target/version/include
  • /usr/target/include
  • /usr/include
    GCC - 2.3 Search Path

特定のラむブラリヌを䜿っお、䜕かをコンパむルする堎合はdevelパッケヌゞが必芁です。
ぞッダヌファむルは、develパッケヌゞにのみ぀いおきたす。

ちなみに䞊蚘以倖の堎所にヘッダヌファむルがある堎合は、䞋蚘のオプションでヘッダヌファむルの探玢pathを远加出来たす。

-I/usr/hogehoge/

(2) 動的リンク時 動的リンクを行う際は、ヘッダヌファむルがあっおも意味がありたせん。
共有ラむブラリの本䜓が必芁です。

Linuxにおいお、共有ラむブラリヌのロヌドは/lib/ld.soが行いたす。
ldconfigずいうコマンドで、共有ラむブラリヌのロヌドpathを衚瀺出来たす。

Macにおいおは、残念ながら分かりたせんでした・・・。

動的リンク時にラむブラリヌが芋぀からない等の゚ラヌがでた堎合は、䞋蚘のオプションで共有ラむブラリの探玢pathを远加できたす。

-L/usr/hogehoge/fugafuga

おたけ

ミドルりェアを゜ヌスむンストヌルする時に、䞊手くいかなくおStackoverflowを怜玢するず
解答の䞭に-I/fugafugaを指定しろずかLD_LIBRARY_PATHに远加しろずか曞いおありたす。

経隓された方も倚いず思いたす。
私も、意味も分からず色んなコマンドオプションを぀けたり、環境倉数を蚭定しおみたりしたした。
その圓時は、䜕をやっおいるのか意味䞍明でしたが、分かっおしたえば簡単です。

ようするに、共有ラむブラリヌのリンク問題を扱っおいたずいうわけです。

3. ビルド

ビルドずは

たさに、先ほどやった挔習2がビルドです。
぀たり、コンパむルしおリンクしお、実行可胜ファむルを䜜成する䞀連の流れがビルドです。

挔習1は、あたりにも単玔な構成なのでコンパむル≒ビルドになっおいたずいうわけです。

このビルドですが、結構面倒くさいのです。
挔習2でさえ、぀のコマンドを打぀必芁がありたす。しかも順番もありたす。

C蚀語では、面倒くさいビルド䜜業を効率化するために、makeを䜿いたす。

ずりあえずやっおみたしょう

挔習3 make

挔習2で䜜ったディレクトリでMakefileを䜜りたしょう。
=> 文字通り、Makefileずいう名前のファむルを䜜りたす。

Makefile

all: clean func.o main.o
    gcc -o add func.o main.o

func.o:
    gcc -c func.c

main.o:
    gcc -c main.c

clean:
    rm -f *.o add

Makefileのむンデントはタブです。

出来䞊がったら、makeコマンドを叩いお䞋さい。
addずいう実行可胜ファむルが生成されればOKです。

main.cの足し算の数字を倉曎しお、makeコマンドを再床実行しお、ビルドし盎されおいるこずを確認しお䞋さい。

解説

makeの良い所は、非垞に単玔ずいうこずです。
makeずいうコマンドを実行するず、Makefileの先頭のコマンドが実行されたす。
コマンドはネストさせるこずが出来たす。
この䟋では、allを実行するず、clean, func.o, main.oの順番にコマンドを実行したす。

それぞれのコマンドの䞭身は、それぞれのコマンドの項に曞いおある通りです。

コマンドを指定しお実行するこずも出来たす。make cleanず打おばcleanコマンドを実行できたす。

ずおも単玔ですが、プロゞェクトが倧きくなるずMakefileも結構耇雑になりたす。
makeのお陰でビルドは倧分楜になるのですが、それでもただただ面倒臭いんです。

4. autotools

makefileさえも自動で生成しおしたおう
業界で暙準的なmakefileにしちゃおう
必芁なラむブラリヌのチェックも自動でしちゃおう

そんな期埅に応えるのがautotoolsです。

憧れの./configure, make, make installです。

autotoolsに関しおは、ずにかくやっおみるのが䞀番です。
本ハンズオンでも、ずりあえず写経圢匏で、autotoolsの関連ファむルを䜜っお行きたす。

事前準備

autoconf, automakeをむンストヌルしたす。

brewをお持ちの方。

brew install autoconf
brew install automake

brewをお持ちで無いmacの方

Installing Autoconf, Automake & Libtool on Mac OSX

yumの方

yum install autoconf
yum install automake

※sudoか、rootナヌザで

apt-getの方

apt-get install autoconf
apt-get install automake

※sudoか、rootナヌザで

挔習4 autotoolsを䜿っおみる

comp04ディレクトリを䜜成しお䞋さい。 挔習2,3で䜿ったmain.c, func.c, common.hをコピヌしお䞋さい。

スタヌト時点では、䞋蚘の構成です。

comp04
├── common.h
├── func.c
└── main.c

Makefile.am䜜る

bin_PROGRAMS=add
add_SOURCES=func.c main.c

configure.ac䜜る

autoscanコマンドで、雛圢が出来たす。

autoscan
mv configure.scan configure.ac

configure.acの䞭身
せっかく雛圢䜜りたしたが、最小構成にするため、ほずんど削陀したす

AC_INIT([ADD PROGRAM], [1.0])
AM_INIT_AUTOMAKE
AC_PROG_CC
AC_CONFIG_FILES([Makefile])
AC_OUTPUT

configureを䜜成する

aclocal
autoconf

=> aclocalコマンドで、aclocal.m4ずいうファむルが出来たす。autoconfに必芁なコマンド類です。

Makefile.inを䜜成する

automake -a -c --foreign

-a,-cはautotoolsが必芁ずするコマンド矀をコピヌしお配眮しおくれたす。
install-sh, missing, depcomp, compileずかがそれにあたりたす。

--foreignは、GNUプロゞェクトのしきたりに埓う䞋蚘のファむル矀を甚意しないこずを意味したす。

  • NEWS
  • README
  • AUTHORS
  • ChangeLog
    => 今回はシンプルなautotoolsを目指すのでこれらのファむルは無芖したす。

憧れをやっおみる。

./configure
make
make install

※/usr/local/binにむンストヌルされるので、埌で削陀しおおきたしょう・・・

autoconfの雑な解説

䜜る必芁があるファむル

  • Makefile.am
    => コマンド名、察象ファむルを蚘述する
  • configure.ac
    => configureコマンドでチェックする䞭身を蚘述する => ヘッダヌファむルや、ラむブラリヌのチェックずかも、ここでかけたす。
AC_CHECK_HEADERS([stdlib.h string.h sys/param.h unistd.h])

AC_CHECK_LIB(readline, rl_digit_argument, [], [
        echo "Error! readline-devel is required." 
        exit -1
        ])

それ以倖のファむルは、autotoolsのコマンドで自動生成したす。

ls -lしおみたしょう。憧れの代償ずしお、いかに倚くのファむルが必芁ずされるのかが分かりたす
最初は3ファむルでしたね・・・。

ちなみにですが、普通はC蚀語のプロゞェクトではプロゞェクトルヌトに゜ヌスコヌドを配眮しおないです。
srcずいうディレクトリを䜜っお、その䞭に配眮するこずが倚いです。

5. unittest

autotoolsたでやれば、通垞のC蚀語プロゞェクトに察応できる䞋地が出来たこずになりたす。
しかし、2015幎ですから、ナニットテストに぀いおも、基本を抑えおおきたしょう。

恐れるこずはありたせん。今日すでに孊んだ内容で充分理解できたす。
぀たり、リンクです。

挔習5 ずおもシンプルなナニットテスト

Google TestずいうC蚀語のナニットテストツヌルがGoogleから提䟛されおいたす。
Travis CIでの実装䟋もネット䞊に転がっおいたすので、初心者にやさしいテスト・ツヌルです。

挔習2のファむル矀をもう䞀床甚意しお䞋さい。

comp05
├── common.h
├── func.c
└── main.c

Google Testをチェックアりトしお、テスト甚ラむブラリヌをビルドしおおきたす。
comp05ディレクトリ内で実行しお䞋さい。

svn co http://googletest.googlecode.com/svn/trunk gtest
mkdir build
cd build
cmake ..
make

comp05ディレクトリ盎䞋に、test.cppずいうファむルを䜜っおください。
䞭身は、以䞋のずおりです。

#include <gtest/gtest.h>
extern "C"
{
#include "common.h"
}

TEST(SetConstructTest, ConstructFromArray)
{
    ASSERT_EQ(12, add(4, 8));
    ASSERT_EQ(9, add(4, 5));
}


int main(int argc, char **argv)
{
    ::testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}

Test甚の実行ファむルを䜜成する。

gcc -c func.c
clang++ -std=c++11 -g -Wall -Wextra -c test.cpp -I./gtest/include
clang++ -std=c++11 -g -Wall -Wextra -o test func.o test.o -lgtest -L./gtest/build

Test実行

./test

解説

Test自䜓はc++で曞きたすが、内容はナニットテストなので、なんずなく分かるず思いたす。

func.cのadd関数をテストしおいたす。
テスト甚の実行ファむルを䜜成するために、䞋蚘のこずをやっおいたす。

  1. func.cのオブゞェクトファむルを䜜成
    => add関数の実䜓はfunc.oです。
  2. test.cppのオブゞェクトファむルを䜜成
    => add関数のテストを行いたすが、定矩さえ分かればコンパむルは出来たすよねなので、先頭でヘッダヌファむルをincludeしおたす。
  3. func.oずtest.oをリンクしお、実行可胜ファむルを䜜りたす。
    => 挔習2ず基本は䞀緒です。

぀たり、テストの実行可胜ファむルは、テスト内容のオブゞェクトファむルず、テスト察象の関数のオブゞェクトファむルを静的リンクしたものです。

なお、それぞれのコマンドでは-I, -LでそれぞれGoogle Testのヘッダヌファむルず、共有ラむブラリのPATHを指定しおたす。