【C/C++】MacでC/C++実行環境構築(Clang→GCCの環境構築)
C言語は大学の授業でやったぐらいで10年ぶりの学び直し。今回は、一週間で身につくC言語の基本を教材として使わせていただいた。これが無料公開されているなんて、本当に感謝しかない。
早速、実行しようとした際に、少し戸惑ったので、調べたこともあわせて、備忘録として残しておく。
Visual Studioをインストール...不要
教材の手順に従って、MacにVisual Studio 2019 for Macをインストールした。 早速プロジェクトを作成しようとしたが、C++がサポートされていない。。。そして、HPを確認すると、以下のように明記されていた。
Visual Studio for Mac では、Microsoft C++ はサポートされていませんが、.NET 言語とクロスプラットフォームの開発はサポートされています。
そこで、Macでは、Visual Studio Code をこれまで通り使うことにした。
C/C++環境構築
C/C++をコンパイルするためには、GCC(GNU Compiler Collection)が必要である。 そこで、現在の私のMacの環境を確認した。
$ where gcc /usr/bin/gcc $ gcc -v Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/4.2.1 Apple clang version 13.0.0 (clang-1300.0.29.3) Target: x86_64-apple-darwin20.6.0 Thread model: posix InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
本来のgcc
ではなく、代替コンパイラであるclang
がすでにインストールされていた。(Xcodeをインストールする際に、自動的にデフォルトのコンパイラとしてClangがインストールされているようであった。)
Clangとは
ClangはC、C++、Objective-C、Objective-C++をターゲットとして、マシン語にコンパイルするために使用されるフロントエンド コンパイラで、LLVM上で動作することを意図して設計されている。
フロントエンド コンパイラと呼ばれている意味としては、コンパイラにはフロントエンドとバックエンドが存在し、Clangはそのフロントエンド、すなわちコマンドの入力、ソース解析を担当している。そして、バックエンドとして、対象のアーキテクチャに最適なマシン語へ変換しているのが、LLVMである。
なんとなくであるが、イメージを掴むことができた。LLVMでの最適化などの詳細は以下を参照してほしい。
GCCと比較して、ソース解析の際に、細かいWarningやErrorを出力してくれる。
GCCのインストール
今回のC言語の初歩的な勉強であれば、Clangでも全く問題ないが、あえてGCCの環境構築を行ってみた。
Homebrewを用いるので、事前にインストールしておいてほしい。
$ brew install gcc $ ls /usr/local/bin | grep gcc- gcc-11 gcc-ar-11 gcc-nm-11 gcc-ranlib-11 $ ls /usr/local/bin | grep g++- g++-11
これで、GCCが使えるようになった。ただし、gcc-11
、g++-11
のように末尾にバージョン情報が記載されているので実行時には注意が必要である。以下のコマンドで実行ファイルを生成することができる。
$ /usr/local/bin/gcc-11 test.c $ /usr/local/bin/g++-11 test.cpp
開発環境構築
実行時には、以下のコマンドを実行する。
/usr/local/bin/gcc-11 test.c && ./a.out
毎回、同じコマンドを入力したくなかったので、シェルスクリプトを作成した。ただし、実行ファイルだけは、引数で指定するようにした。
#!/bin/bash /usr/local/bin/gcc-11 $1 && ./a.out
実際に、実行してみる。
$ ./run.sh src/test.c zsh: permission denied: ./run.sh
権限を確認する。
$ ls -l total 8 drwxr-xr-x 3 serip39 staff 96 11 24 01:09 docs -rw-r--r-- 1 serip39 staff 29 11 24 01:22 run.sh drwxr-xr-x 5 serip39 staff 160 11 24 01:09 sample
すべてのユーザーに実行権限を付与する。
$ chmod +x ./run.sh $ ls -l total 8 drwxr-xr-x 3 serip39 staff 96 11 24 01:09 docs -rwxr-xr-x 1 serip39 staff 29 11 24 01:22 run.sh drwxr-xr-x 5 serip39 staff 160 11 24 01:09 sample
これで、再度実行する。
$ ./run.sh src/test.c HelloWorld.
無事に実行することができた。 これで、MacでのC/C++の実行環境ができた。
複数ファイルのコンパイル
ファイル分割を行い、自作のヘッダーファイルを読み込んで実行しようとすると、以下のようなエラーが出力された。(Clangのエラー文のほうがわかりやすかったので、Clangで実行した結果を示す。)
Undefined symbols for architecture x86_64: "_avg", referenced from: _main in main-584f2f.o ld: symbol(s) not found for architecture x86_64 clang: error: linker command failed with exit code 1 (use -v to see invocation)
エラーメッセージをよく見ると、これはコンパイルのエラーではなく、リンカのエラーであった。gccやg++などのフロントエンドコンパイラコマンドは、コンパイルだけじゃなく、リンカまで行って実行可能ファイルを作成する。そのため、以下の方法で実行する必要がある。
-c
オプションなどでリンカを抑止する$ /usr/local/bin/gcc-11 -c src/test.c && ./a.out
すべてのファイルを指定する
リンカ時には、実行可能ファイルに必要な全てのファイルを指定する必要がある。(標準のライブラリは自動的に探索されるので指定不要。)$ /usr/local/bin/gcc-11 src/test.c src/test1.c src/test2.c && ./a.out
ゆえに、シェルスクリプトも以下のように変更を行った。
#!/bin/bash if [ -f $1 ]; then /usr/local/bin/gcc-11 $1 && ./a.out else for pathfile in `find $1/*c`; do allpath+=$pathfile allpath+=" " done /usr/local/bin/gcc-11 $allpath && ./a.out fi
フォルダを引数として指定した場合は、フォルダ内のソースファイルをすべてgccの引数に指定できるようにした。
C言語のコンパイル処理
1. プリプロセッサ(Pre-processor)
コンパイルする前の前処理を行う。コンパイラによるソースコードの字句解析や、構文解析、バイナリコード生成などよりも前に実行される。
- ヘッダファイル(#include)の読み込み
- マクロの展開(#define等)
この段階で処理を止める場合のコンパイラオプションは-E
である。
2.(狭義の)コンパイラ
前処理済みのプログラムファイルをアセンブラ言語に変換する。アセンブリ言語は、機械が理解できるコードをシンボル(記号)で表現したものである。
この段階で処理を止める場合のコンパイラオプションは-S
である。
3. アセンブラ(Assembler)
アセンブリ言語プログラムをアセンブルして、機械語のオブジェクトファイルに変換する。
この段階で処理を止める場合のコンパイラオプションは-c
である。
4. リンカ(Linker)
複数のオブジェクトファイル、標準ライブラリのオブジェクトファイルなども結合して、実行可能形式のファイルを生成する。
まとめ
C言語のコンパイラ周りについて、調べてなんとなく概要を掴むことができた。この領域は本当に奥が深いので、これから少しずつではあるが、CPUなどの低レイヤーの部分まで理解できるようになると、もっと面白いのではないかと思えた。
とりあえずは、環境構築ができたので、Cの勉強に取り掛かっていきたい。