最初のプログラム - Hello World
最初に作るプログラムは、コンソールに「hello, world」というテキストを表示するプログラムです。
どういうことか説明するよりも先にまずコードを載せておきます。
#include <iostream>
int main()
{
std::cout << "hello, world!\n";
}
このコードを実行できれば「hello, world!」というテキストがコンソールに表示されることになります。
「コンソールに表示する」と言ってますがそもそもコンソールとはなんなのかを一応説明しておきます。
デスクトップPCでユーザーが直接操作するプログラムは、大雑把に分けて2種類あります(この分け方は本当に大雑把です)。Windowsを例に取ると、メモ帳や電卓やマインスイーパーのようなGUI (グラフィカルユーザーインターフェイス)を備えたプログラムと、コマンドプロンプトと呼ばれるものからテキストのコマンドを入力して操作するCLI (コマンドラインインターフェイス)のプログラムがあります。Windowsでコンソールと言った場合このコマンドプロンプトのこととだいたい同じです。macOSではターミナル、Linuxにはxtermやgnome-terminal (GNOME)、konsole (KDE)など他にもたくさんあります。「コンソールに表示する」と言ったとき、こういったコマンドプロンプトやターミナルなどの中に表示させることになります。
ウィンドウも持たずマウスも使えないプログラムが役に立つのかと思われるかもしれませんが、プログラミングを続けていくと役に立つことがそのうち出てくると思います。
プログラムを実行するまでの手順
先に書いたソースコードを実行するまでの手順説明します。
1. 作業する場所の用意
まず作業する場所を用意します。場所とはディレクトリ(フォルダ)のことです。
例えば、
/home/username/playground/helloworld
(Linux, etc)/Users/username/playground/helloworld
(macOS)C:\Users\username\playground\helloworld
(Windows)
と言った場所です。どこでも好きな場所で好きな名前でいいのですが、システム関連のファイルが配置されている場所(C:\Windowsとか)は絶対に避けて下さい。場所を作成するのはエクスプローラやファイルマネージャから行っても、コマンドラインで行ってもどちらでもいいです。ここではLinuxでコマンドラインで行う場合を書いておきます。
cd ~
mkdir playground
cd playground
mkdir helloworld
cd helloworld
pwd
/home/userx/playground/helloworld
エクスプローラなどのファイルマネージャで操作する場合はフォルダを作成してその中に移動するだけです。
2. テキストエディタでソースコードを書く
用意したhelloworldの中に新規でソースコードを記述するファイルを作成します。
ソースコードを記述するにはテキストエディタを使います。Windowsだったらメモ帳でもいいです。新しくインストールする時間があればVisual Studio Codeとかいいかもしれないです。テキストエディタの選択は作業効率に大きく関わってくるのいつかはちゃんと考えないといけないのですが、今の段階では何でもいいです。また別の機会にテキストエディタについて書きたいと思います。私はKDEを使っているので、今回は最初から入っているKateというテキストエディタを使うことにします。
こんなかんじになります。
Kateは最初から入っているエディタにしてはかなり高機能でC++を始めとして色々な言語用に色付けしてくれるので上のような表示になります。メモ帳だと色付けしてくれないのでもっとそっけない印象になるかと思います。
ファイル名は「hello.cpp」とします。拡張子を「.cpp」とする点に注意して下さい。
Windowsではデフォルトでエクスプローラで拡張子が表示されないようになっています。プログラミングをするときには不便なことの方が多いので設定を変更して表示させるようにしておくのがおすすめです。またメモ帳を使うときは、保存時に自動で「.txt」という拡張子をつけてしまいます。保存時にファイル名を「"hello.cpp"」のように二重引用符で囲むか、選択肢で「全てのファイル」にしておく必要があります。
空白や記号は全て半角英数でなければいけません。
バックスラッシュ\についてですが、Windowsの日本語キーボードでは¥をタイプします。フォントによってそのまま¥と表示されたり\と表示されたりします。これは表示の違いだけで同じ文字コードなので¥と表示されてもそのままでOKです。
一方macOSの日本語キーボードで¥をタイプすると¥が入力されますがこれはダメです。ちゃんと\を入力する必要があります。optionを押しながら¥をタイプすると\になります。
ソースコードを保存したらこのようになります。
3. コンパイラのインストール
C++のソースコードはそのままでは実行できません。コンパイル(とリンクなど)というプロセスを経てバイナリコードを生成する必要があります。そのために必要になるのがコンパイラです。
利用できるコンパイラはプラットフォームによってさまざまです。残念ながらWindowsとmacOSについては実験できる環境がなくて詳しく書けないので、簡単に紹介だけしておきます。
WindowsではVisual Studio Communityというのが個人利用では無償で利用出来ます(会社で使うには制限があるので注意です)。これにはC++の開発環境も含まれて、C++コンパイラもインストールされます。別の選択肢として、Mingw-w64というGCCのWindows移植版を利用することも出来ます。
macOSではXcodeをインストールするとC++の環境もついてきて、Clangというコンパイラが使えます。
LinuxではGCCあるいはLLVMベースのコンパイラClangが利用出来ます。DebianやUbuntuでGCCをインストールするには
sudo apt install g++
とします。Clangをインストールするには
sudo apt install clang
とします。
コンパイラコンパイラといってますが、正確にはコンパイラ単体でソースコードから実行可能なプログラムを生成できるわけではなく、アセンブラやリンカなどのツールが必要になります。apt install gcc
とした場合そういったツール群も一緒にインストールされます。
正しくインストールされたか確認するには、GCCの場合、
g++ --version
g++ (Debian 8.3.0-6) 8.3.0
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
のように表示されればOKです。Clangの場合、
clang++ --version
clang version 7.0.1-8+deb10u2 (tags/RELEASE_701/final)
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
のようになります。バージョンはインストールされたバージョンにより異なります。2020年11月時点ではGCCの最新バージョンは10、Clangは11なのですが、私の使っているディストリビューションDebian 10で提供されているのは上記のとおりやや古いです。
4. コンパイルする
コンパイラが用意できたので実際にコンパイルしていきます。これからはGCCの方を使っていきます。Clangを使う場合はg++をclang++に置き換えてください。Visual Sutudioを使う場合はこちらを参考にしてみて下さい。
コンパイルは以下のコマンドで行います。
g++ hello.cpp
すると、同じディレクトリに新しくa.outというファイルが作成されます。
ls -l
合計 24
-rwxr-xr-x 1 userx userx 17096 11月 23 18:30 a.out
-rw-r--r-- 1 userx userx 72 11月 23 17:52 hello.cpp
このa.outというファイルがコンパイラによって作成された実行可能ファイルです。
5. 実行する
以下のコマンドでa.outを実行します。
./a.out
hello, world!
これで最初のプログラムは完成です。おつかれさまでした!
コンパイルについて補足
生成されるファイル名について
作成されるプログラムの名前はデフォルトでa.outとなっています。これは「-o」オプションで変更出来ます。
g++ -o hello hello.cpp
ls -l
合計 24
-rwxr-xr-x 1 userx userx 17096 11月 23 23:02 hello
-rw-r--r-- 1 userx userx 71 11月 23 19:37 hello.cpp
こうするとa.outではなくhelloという実行可能ファイルが生成されます。実験用の使い捨てプログラムはともかく、通常は-o ファイル名
は指定するようにしたほうがいいかもしれません。
コンパイラが実際にやっていること
先に述べたとおり、実行可能ファイルの生成にはコンパイラだけではなくいくつかのプログラムが関わっています。GCCに「-v」オプションを付けることで、コンパイラが実際に何をやっているか表示させることが出来ます。かなり長いのですが実行したときの出力を省略せずに乗せておきます。
g++ -v hello.cpp
Using built-in specs.
COLLECT_GCC=g++
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/8/lto-wrapper
OFFLOAD_TARGET_NAMES=nvptx-none
OFFLOAD_TARGET_DEFAULT=1
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Debian 8.3.0-6' --with-bugurl=file:///usr/share/doc/gcc-8/README.Bugs --enable-languages=c,ada,c++,go,brig,d,fortran,objc,obj-c++ --prefix=/usr --with-gcc-major-version-only --program-suffix=-8 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-bootstrap --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-libmpx --enable-plugin --enable-default-pie --with-system-zlib --with-target-system-zlib --enable-objc-gc=auto --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 8.3.0 (Debian 8.3.0-6)
COLLECT_GCC_OPTIONS='-v' '-shared-libgcc' '-mtune=generic' '-march=x86-64'
/usr/lib/gcc/x86_64-linux-gnu/8/cc1plus -quiet -v -imultiarch x86_64-linux-gnu -D_GNU_SOURCE hello.cpp -quiet -dumpbase hello.cpp -mtune=generic -march=x86-64 -auxbase hello -version -o /tmp/ccZ5GhIW.s
GNU C++14 (Debian 8.3.0-6) version 8.3.0 (x86_64-linux-gnu)
compiled by GNU C version 8.3.0, GMP version 6.1.2, MPFR version 4.0.2, MPC version 1.1.0, isl version isl-0.20-GMP
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
ignoring duplicate directory "/usr/include/x86_64-linux-gnu/c++/8 "
ignoring nonexistent directory "/usr/local/include/x86_64-linux-gnu "
ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/8/../../../../x86_64-linux-gnu/include "
#include "... " search starts here:
#include <...> search starts here:
/usr/include/c++/8
/usr/include/x86_64-linux-gnu/c++/8
/usr/include/c++/8/backward
/usr/lib/gcc/x86_64-linux-gnu/8/include
/usr/local/include
/usr/lib/gcc/x86_64-linux-gnu/8/include-fixed
/usr/include/x86_64-linux-gnu
/usr/include
End of search list.
GNU C++14 (Debian 8.3.0-6) version 8.3.0 (x86_64-linux-gnu)
compiled by GNU C version 8.3.0, GMP version 6.1.2, MPFR version 4.0.2, MPC version 1.1.0, isl version isl-0.20-GMP
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
Compiler executable checksum: 3c854693d01dc9a844a56a0b1ab1c0f4
COLLECT_GCC_OPTIONS='-v' '-shared-libgcc' '-mtune=generic' '-march=x86-64'
as -v --64 -o /tmp/ccJxnMgZ.o /tmp/ccZ5GhIW.s
GNU アセンブラ バージョン 2.31.1 (x86_64-linux-gnu)、BFD バージョン (GNU Binutils for Debian) 2.31.1 を使用
COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/8/:/usr/lib/gcc/x86_64-linux-gnu/8/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/8/:/usr/lib/gcc/x86_64-linux-gnu/
LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/8/:/usr/lib/gcc/x86_64-linux-gnu/8/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/8/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/8/../../../:/lib/:/usr/lib/
COLLECT_GCC_OPTIONS='-v' '-shared-libgcc' '-mtune=generic' '-march=x86-64'
/usr/lib/gcc/x86_64-linux-gnu/8/collect2 -plugin /usr/lib/gcc/x86_64-linux-gnu/8/liblto_plugin.so -plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/8/lto-wrapper -plugin-opt=-fresolution=/tmp/cccMMcQ1.res -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu -dynamic-linker /lib64/ld-linux-x86-64.so.2 -pie /usr/lib/gcc/x86_64-linux-gnu/8/../../../x86_64-linux-gnu/Scrt1.o /usr/lib/gcc/x86_64-linux-gnu/8/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/8/crtbeginS.o -L/usr/lib/gcc/x86_64-linux-gnu/8 -L/usr/lib/gcc/x86_64-linux-gnu/8/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/8/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/8/../../.. /tmp/ccJxnMgZ.o -lstdc++ -lm -lgcc_s -lgcc -lc -lgcc_s -lgcc /usr/lib/gcc/x86_64-linux-gnu/8/crtendS.o /usr/lib/gcc/x86_64-linux-gnu/8/../../../x86_64-linux-gnu/crtn.o
COLLECT_GCC_OPTIONS='-v' '-shared-libgcc' '-mtune=generic' '-march=x86-64'
ポイントはハイライトされている行で、cc1plus、as、collect2という別のプログラムを呼び出していることです。これを理解する必要も意識することも当面必要ないと思われますが、今回のHello Worldのような単純なプログラムをコンパイルするだけでもこれだけの工程を経ていることを知っておくことは意味があるかと思い載せておきました。
Visual Studioを使う場合
WindowsでVisual Studioを利用してコマンドラインでコンパイルする場合、こちらに詳しく書かれています。簡単に書くと、
- スタートメニューから「開発者コマンド プロンプト for VS 2019」を開く。
- ソースコードを保存したディレクトリに移動する。
cl /EHsc hello.cpp
とタイプする。hello.exe
とタイプして実行する。
今後の方針
WindowsやmacOSにも対応したいのですが開発環境がないため、これからしばらくはLinuxでGCCを使うことを前提に進めていきます。