【初心者向け】原点回帰!C言語の開発環境をmacOS Sonoma と Docker Desktop 4.32 で構築してみよう!

記事の概要

この記事では、汎用プログラミング言語であるC言語(以下C)を使って、macOS Sonoma と Docker Desktop 4.32 での開発環境の構築からシンプルなCLIアプリ開発、実行までの流れを紹介します。

この記事を参考に、CやCLIアプリ開発のスタートの一助となれば幸いです。


今回の記事の対象者

この記事は次のような方に向けています:

  • Cの環境を構築してアプリ開発を始めたい方
  • Dockerコンテナを使ってローカルとサーバー(VPSやクラウド)の環境を統一したい方
  • シンプルなCLIアプリでCの実装イメージを理解したい方


はじめに

今回は、Cを使った開発環境の構築と、シンプルなCLIアプリの開発を解説します。

開発環境は、以下2つの環境を構築します。

  • macOS:macOS Sonomaを使ったローカル開発環境
  • Dockerコンテナ:Docker Desktopを使ったサーバー開発環境

CLIアプリは、計算機の機能をステップバイステップで開発し、Cの基礎的な機能を段階的に学べる構成にしています。

  • 四則演算
  • 演算機能の追加
  • データ型の拡張
  • インタフェースの改良
  • エラーハンドリングの追加

また、作成したソースコードをもとに、Cがどのように使われているかを詳細に解説していますので、是非活用してみてください。


目次


C言語(C programming language)とは

Cは、1972年にデニス・リッチーというコンピュータ科学者が開発した汎用プログラミング言語です。

Cは、特にシステムプログラミングや組み込みシステム(例えば、家電や車の制御システムなど)の開発に広く使われています。これはハードウェアと直接やり取りするために使う低レベルの操作を得意としているからです。

Cは、主に以下の特徴があります。

  1. 低レベル言語に近い:
    • Cは、コンピュータのハードウェアに近い操作ができる低レベル言語とされています。これにより、ハードウェアに搭載されているメモリやプロセッサを効率的に操作できます。
  2. 高速:
    • Cで書かれたプログラムは、他の高レベル言語(PythonやJavaなど)に比べて非常に高速に動作します。これは、C言語が直接機械語に近いコードを生成するため実行時のオーバーヘッドが少ないためです。
  3. 移植性:
    • Cは多くの異なるコンピュータシステムで動作します。つまり、あるシステムで書いたCプログラムを、少しの修正で他のシステムでも動かせるという特徴があります。
  4. シンプルさ:
    • Cは、シンプルな構文(文法)を持っているため、基本的なプログラムを作るのは比較的簡単です。しかし、細かい部分までコントロールできるため、学ぶべき内容が多いです。

Cは、以下のようなソフトウェア開発に適しています。

  1. システムプログラミング
    • Cは、オペレーティングシステム(OS)の開発によく使われます。例えば、UNIXやLinuxのカーネル(OSの中核部分)は、主にC言語で書かれています。Cは、メモリやハードウェアに直接アクセスできるため、効率的で高速なシステムソフトウェアを作るのに適しています。
  2. 組み込みシステム開発
    • 家電製品、自動車、スマートフォンなどの組み込みシステムの多くは、Cで開発されています。これらのシステムは、特定のハードウェア上で効率的に動作する必要があるため、Cの低レベルな操作性が非常に役立ちます。
  3. ゲーム開発
    • 多くの古典的なゲームはCで開発されました。ゲームのグラフィックや物理エンジンを効率的に動かすのに適しており、リアルタイム性が重要なゲーム開発においても活用されています。現代のゲームエンジンでも、CやC++が使われていることが多いです。
  4. 高性能なアプリケーション開発
    • Cは、データベースシステム、ネットワークサーバー、コンパイラ(プログラムを実行可能な形式に変換するソフトウェア)など、高性能を要求されるアプリケーションの開発にも使われています。MySQLやPostgreSQLといった有名なデータベースもCで書かれています。
  5. 科学技術計算
    • Cは、科学技術計算にも利用されます。複雑な数値計算やシミュレーション、データ解析を効率的に行うために、Cの高速性と効率性が役立ちます。特に、大規模なデータセットを処理する場合や、精密な計算が求められる分野で使用されます。
  6. ハードウェア制御
    • Cは、ハードウェアの制御にも使われます。例えば、ロボットの制御プログラムや、センサーからのデータ収集といったタスクに使われることが多いです。これにより、ハードウェアを直接操作し、リアルタイムで動作させることが可能です。
  7. ライブラリの開発
    • 多くの他のプログラミング言語で使われるライブラリ(特定の機能を提供するプログラムの部品)もCで書かれています。例えば、PythonやRubyなどのプログラミング言語は、内部でCで書かれたライブラリを使用して、特定の機能を実装しています。


ソフトウェア・ハードウェア

必要なツール、ライブラリ、端末は以下の通りです。

開発ツール

以下、開発ツールとその公式サイトの一覧です。本記事では直接以下のツールは使いませんが、導入しておくと開発作業が楽になります。

ツール名用途
CLionC/C++開発者向けのIDEです。個人非商用利用や学生は無料となっています。
VS CodeオープンソースのIDEです。色々なプラグインで環境を拡張できます。

端末

以下、今回の環境を構築する対象の端末スペックです。

項目詳細
ハードウェアApple Silicon M3, RAM 24GB
OSmacOS Sonoma 14.6

本記事で紹介するソフトウェアおよびツールは、筆者の個人的な使用経験に基づくものであり、公式のサポート外の設定や使用方法を含む場合があります。利用に際しては、公式サイトの指示およびガイドラインを参照し、自己責任で行ってください。


C開発環境の構築

コンパイラのインストール

macOSでC開発環境を構築する場合、GCCとClangの2つの選択肢があります。どちらも強力なコンパイラですが、用途や環境によって適切な選択が異なります。Clangはモダンな開発環境やクロスプラットフォーム開発に強みがあり、GCCは長い歴史と幅広いプラットフォームへの対応が特徴です。プロジェクトやニーズに応じて、最適なコンパイラを選んでください。以下に、それぞれのコンパイラの特徴を記載します。

今回の記事では、安定性やDockerコンテナでの実績があるGCCに統一します。また、参考としてClangの導入方法も紹介します。

Clang:

特にmacOSやiOSの開発環境において標準的なコンパイラとして使われます。また、開発者向けツールや統合開発環境(IDE)に組み込まれることが多いです。

ClangはモダンなC++標準やC言語の拡張を早期にサポートすることが多く、新しい言語仕様やライブラリへの対応が迅速です。

クロスプラットフォーム対応が進んでおり、macOS、Linux、Windowsなどで使用できます。

GCC:

Linuxや多くのUnix系システムにおいて標準のコンパイラです。オープンソースプロジェクトやサーバー、組み込みシステムの開発に広く利用されています。

GCCは非常に広範なプラットフォームをサポートしており、特にLinux系システムでのデファクトスタンダードとなっています。

GCCは多くの古いコードベースやアーキテクチャに対して高い互換性を持っているため、レガシーシステムでの使用に適しています。


macOS標準のClangを使う場合

macOSでC言語のアプリをコンパイルする方法を説明します。

macOSには、C言語をコンパイルするためのツールがすでにインストールされているか、簡単にインストールすることができます。以下の手順に従ってください。

Xcode Command Line Toolsのインストール

macOSでC言語のコンパイルを行うためには、gccやclangといったコンパイラが必要です。これらは通常、Xcode Command Line Toolsの一部として提供されています。もしまだインストールしていない場合は、次の手順でインストールできます。

手順:

  1. ターミナルを開く:
    • Launchpadで「その他」 > 「ターミナル」を開きます。
  2. Xcode Command Line Toolsのインストール:
    • ターミナルに以下のコマンドを入力して、Xcode Command Line Toolsをインストールします。
    • インストールを促すダイアログが表示されるので、指示に従ってインストールします。すでにインストールされている場合は、その旨が表示されます。
xcode-select --install

Clangが正しくインストールされたか確認します。以下のコマンドを実行して、インストールされたClangのバージョンを確認します。

macOSでは通常、gccコマンドはClangを指します。clangコマンド、gccコマンドでバージョンを確認出来ます。

clang --version
gcc --version

それぞれのコマンドで「Apple clang version・・・」が表示されているので、正しくclangが使われていることが確認出来ます。

(base) xxxxx@xxxxx calculator_app % clang --version
Apple clang version 15.0.0 (clang-1500.3.9.4)
Target: arm64-apple-darwin23.6.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
(base) xxxxx@xxxxx calculator_app % 

(base) xxxxx@xxxxx calculator_app % gcc --version
Apple clang version 15.0.0 (clang-1500.3.9.4)
Target: arm64-apple-darwin23.6.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
(base) xxxxx@xxxxx calculator_app % 


GCCを使う場合

1.Homebrewのインストール

  • まず、Homebrewがインストールされているか確認します。ターミナルを開いて以下のコマンドを入力します。
brew --version
  • もしHomebrewがインストールされていなければ、以下のコマンドをターミナルに入力してインストールします。
  • インストールが完了すると、メッセージが表示されます。指示に従って、Homebrewを使用するために必要な設定を行ってください。
/bin/bash -c "$(curl -fsSL <https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh>)"

2.Xcode Command Line Toolsのインストール

  • GCCをインストールする前に、Xcode Command Line Toolsがインストールされている必要があります。以下のコマンドでインストールします。
  • インストールを促すダイアログが表示されるので、指示に従ってインストールします。すでにインストールされている場合は、その旨が表示されます。
xcode-select --install

3.HomebrewでGCCをインストール

  • Homebrewを使用してGCCをインストールします。以下のコマンドをターミナルに入力します。
  • このコマンドは、最新バージョンのGCCをインストールします。インストールには数分かかることがあります。
brew install gcc

以下のコマンドで、brewでインストールされたgccの情報を取得することが出来ます。

brew info gcc

以下、筆者の環境での出力結果です。

(base) xxxxx@xxxxx calculator_app % brew info gcc
==> gcc: stable 14.1.0 (bottled), HEAD
GNU compiler collection
<https://gcc.gnu.org/>
Installed
/opt/homebrew/Cellar/gcc/14.1.0_2 (1,913 files, 473.3MB) *
  Poured from bottle using the formulae.brew.sh API on 2024-08-25 at 08:48:23
From: <https://github.com/Homebrew/homebrew-core/blob/HEAD/Formula/g/gcc.rb>
License: GPL-3.0-or-later with GCC-exception-3.1
==> Dependencies
Build: make ✘
Required: gmp ✔, isl ✔, libmpc ✔, mpfr ✔, zstd ✔
==> Options
--HEAD
	Install HEAD version
==> Analytics
install: 132,624 (30 days), 457,313 (90 days), 1,491,015 (365 days)
install-on-request: 55,390 (30 days), 195,833 (90 days), 622,618 (365 days)
build-error: 453 (30 days)
(base) xxxxx@xxxxx calculator_app % 

インストールされているgccがバージョン14ですので、以下のコマンドでコンパイラを呼び出すことが出来ます。

gcc-14 --version

以下、筆者の環境での出力結果です。

(base) xxxxx@xxxxx calculator_app % gcc-14 --version
gcc-14 (Homebrew GCC 14.1.0_2) 14.1.0
Copyright (C) 2024 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.

(base) xxxxx@xxxxx calculator_app % 

gccコマンドで呼び出せるようにgccコマンドのシンボリックリンクを作成する。

(base) xxxxx@xxxxx ~ % which gcc-14                   
/opt/homebrew/bin/gcc-14
(base) xxxxx@xxxxx ~ % sudo ln -s /opt/homebrew/bin/gcc-14 /usr/local/bin/gcc

作成したコマンドを認識できるように、PATH変数に以下を追加します。筆者の環境は、zshを使って居るので、以下のファイルに追記して、ターミナルを再起動します。

.zshrc

export "PATH=/usr/local/bin:$PATH"

以下、筆者の環境での出力結果です。コンパイラが gccに変更出来たのが確認出来ます。

(base) xxxxx@xxxxx ~ % gcc --version
gcc (Homebrew GCC 14.1.0_2) 14.1.0
Copyright (C) 2024 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.

gccからClangに戻す場合は、シンボリックリンクを削除します。

% sudo unlink /usr/local/bin/gcc


C言語のソースコードを作成する

ディレクトリ構成

プロジェクトのディレクトリ構成は次のようになります。任意のディレクトリにプロジェクトディレクトリ「hello」を作成してください。

/hello
│
└── hello.c

次に、コンパイルしたいC言語のプログラムを作成します。テキストエディタ(Vim)や、Visual Studio Code、Jetbrains CLion等のIDE(統合開発環境)を使用して、以下のCファイルを作成します。

手順:

  1. プロジェクトディレクトリを作成
    • ターミナルでディレクトリ作成コマンドを実行し、プロジェクトディレクトリ「hello」を作成します。
    • 作成したディレクトリに移動します。
    mkdir hello cd hello
  2. 新しいCファイルを作成:
    • エディタを開き、以下の簡単なCプログラムを入力します。
#include <stdio.h>

int main() {
    printf("Hello, World!\\n");
    return 0;
}
  • このファイルを「hello.c」という名前でプロジェクトディレクトリに保存します。

macOS環境用

C言語のプログラムをコンパイルする

ターミナルでコンパイルコマンドを実行し、C言語のプログラムをコンパイルします。

手順:

  1. ターミナルを開く:
    • 再度、ターミナルを開きます。
  2. ソースファイルのあるディレクトリに移動:
    • cdコマンドを使って、Cファイルを保存したディレクトリに移動します。
  3. コンパイルを実行:
    • 以下のコマンドを使って、Cプログラムをコンパイルします。ここでは、gccを使用していますが、clangでも同様にコンパイルできます。
    • このコマンドは、hello.cというソースファイルをコンパイルして、helloという名前の実行ファイルを生成します。-oは出力ファイルの名前を指定するオプションです。
gcc hello.c -o hello


コンパイルされたプログラムを実行する

コンパイルが成功すると、生成された実行ファイルをターミナルで実行できます。

手順:

  1. 実行:
    • 以下のコマンドを入力して、コンパイルされたプログラムを実行します。
    • これで、ターミナルに「Hello, World!」と表示されます。
./hello

以下に、上記手順を実行したコマンド実行例を示します。

(base) xxxxx@xxxxx test-pj % mkdir hello
(base) xxxxx@xxxxx test-pj % cd hello 
(base) xxxxx@xxxxx hello % vi hello.c
(base) xxxxx@xxxxx hello % gcc hello.c -o hello
(base) xxxxx@xxxxx hello % ls 
hello	hello.c
(base) xxxxx@xxxxx hello % ./hello 
Hello, World!
(base) xxxxx@xxxxx hello % 


Dockerコンテナ用

ディレクトリ構成

プロジェクトのディレクトリ構成は次のようになります。先ほどのmacOSの手順で作成したディレクトリとCファイルを使います。

/hello
│
├── hello.c
└── Dockerfile

Docker Desktopのインストール

Dockerコンテナは、アプリケーションをコンテナと呼ばれる仮想的な環境に入れて動かすためのツールです。Docker Composeは、複数のコンテナを一度に管理するためのツールです。

Docker Desktopのインストールは、以下の記事を参考にしてください。

Dockerfileの作成

次に、Dockerfileを作成して、アプリをコンテナ化します。このファイルでは、Cコンパイラをインストールし、アプリをコンパイルして実行します。

# ベースイメージとしてAlpine Linuxを使用
FROM alpine:latest

# Cコンパイラをインストール
RUN apk add --no-cache gcc musl-dev

# アプリの作業ディレクトリを作成
WORKDIR /app

# Cコードをコンテナにコピー
COPY hello.c .

# Cコードをコンパイル
RUN gcc -o hello hello.c

# コンテナが起動したときに実行するコマンド
CMD ["./hello"]

アプリのビルドと実行

コンテナイメージのビルドコマンド

ビルドしたコンテナイメージ名に「hello」を指定しています。

docker build . -t hello

ビルドしたコンテナイメージの実行コマンド

docker run -it hello

helloコンテナが起動して、ターミナルに「Hello, World!」と表示されます。

以下に、上記手順を実行したコマンド実行例を示します。

(base) xxxxx@xxxxx hello % docker build . -t hello
[+] Building 4.8s (10/10) FINISHED                         docker:desktop-linux
 => [internal] load build definition from Dockerfile                       0.0s
 => => transferring dockerfile: 444B                                       0.0s
 => [internal] load metadata for docker.io/library/alpine:latest           2.5s
 => [internal] load .dockerignore                                          0.0s
 => => transferring context: 2B                                            0.0s
 => [1/5] FROM docker.io/library/alpine:latest@sha256:0a4eaa0eecf5f8c050e  0.0s
 => => resolve docker.io/library/alpine:latest@sha256:0a4eaa0eecf5f8c050e  0.0s
 => => sha256:0b4426ad4bf25e13fb09112b9dcb5d5b09b3c568459 1.49kB / 1.49kB  0.0s
 => => sha256:0a4eaa0eecf5f8c050e5bba433f58c052be7587ee8a 1.85kB / 1.85kB  0.0s
 => => sha256:24ba417e25e780ff13c888ccb1badec5b027944666ff695 528B / 528B  0.0s
 => [internal] load build context                                          0.0s
 => => transferring context: 114B                                          0.0s
 => [2/5] RUN apk add --no-cache gcc musl-dev                              2.0s
 => [3/5] WORKDIR /app                                                     0.0s 
 => [4/5] COPY hello.c .                                                   0.0s 
 => [5/5] RUN gcc -o hello hello.c                                         0.1s 
 => exporting to image                                                     0.2s 
 => => exporting layers                                                    0.2s 
 => => writing image sha256:589e47e590842d146abbc409f1294395ed034f018950c  0.0s 
 => => naming to docker.io/library/hello                                   0.0s

View build details: docker-desktop://dashboard/build/desktop-linux/desktop-linux/3zsdsa2xzd181o3b6nwkiqpkq

What's next:
    View a summary of image vulnerabilities and recommendations → docker scout quickview 
(base) xxxxx@xxxxx hello % docker run -it hello
Hello, World!
(base) xxxxx@xxxxx hello % 


CLIアプリの概要と学習のポイント

計算機アプリ(四則演算)

最初のアプリは、ユーザーが入力した2つの数値に対して、加算、減算、乗算、除算の4つの基本的な数学演算を行い、演算の結果を表示することが目的です。

プログラムの概要:

  1. プログラム開始時に、利用可能な操作を説明するメニューを表示します。
  2. ユーザーに1つ目の数値を入力させます。
  3. ユーザーに演算子(+, -, *, /)を入力させます。
  4. ユーザーに2つ目の数値を入力させます。
  5. 入力された数値と演算子に基づいて、計算を行います。
  6. 計算結果を表示します。
  7. ユーザーに再度計算を行うか、プログラムを終了するかを選択させます。

学習ポイント①:

  • 基本的な入出力:ンピュータがユーザーからデータを受け取る(入力)こと、そしてその結果をユーザーに伝える(出力)方法。
  • 条件分岐:プログラムが条件に応じて異なる処理を行う方法。
  • 四則演算の処理:加算(足し算)、減算(引き算)、乗算(掛け算)、除算(割り算)の方法。


計算機アプリ(演算機能の追加)

新しい演算機能(累乗、平方根、余り演算)を追加して、計算機の機能を拡張します。これにより、math.hライブラリを活用した高度な演算を学びます。

学習ポイント②:

  • 数学関数の利用: math.hを使った累乗や平方根の計算方法。
  • 条件分岐の拡張: 新しい演算子の追加と、それに伴う処理の実装。
  • データ型の注意: 演算に使うデータ型が適切かどうかを検討すること。


計算機アプリ(データ型の拡張)

計算機アプリが整数だけでなく、浮動小数点数(小数)もサポートするように拡張します。これにより、数値の精度や異なるデータ型の扱い方を学びます。

学習ポイント③:

  • データ型の違い: intとfloat/doubleの違いと、それぞれの適用場面。
  • 型変換: 必要に応じた型のキャストや、型変換を正しく行う方法。
  • 計算精度: 小数点以下の精度をどのように管理し、適切に結果を表示するか。


計算機アプリ(インタフェースの改良)

メニューインターフェースを改良し、より直感的で使いやすいUIを提供します。これにより、ユーザーインターフェースの設計と文字列操作を学びます。

学習ポイント④

  • ユーザーインターフェースの設計: 直感的で使いやすいメニューの設計方法。
  • 文字列操作: strchr関数を使って、ユーザーの入力を効率的に処理する方法。
  • ユーザビリティの向上: ユーザビリティを意識したUIの改良。


計算機アプリ(四則演算)

ディレクトリ構成

プロジェクトのディレクトリ構成は次のようになります。任意のディレクトリにプロジェクトディレクトリ「calculator_app」を作成してください。

/calculator_app
│
├── calculator.c
└── Dockerfile

計算機アプリの基本コード

まず、基本となるシンプルな計算機アプリのコードを以下に示します。これは、各拡張機能を追加していくベースとなるものです。

このファイルを「calculator.c」という名前でプロジェクトディレクトリに保存します。

calculator.c

#include <stdio.h>

int main() {
    float num1, num2, result;
    char operator;
    char choice;

    do {
        printf("Enter first number: \\n");
        scanf("%f", &num1);

        printf("Enter an operator (+, -, *, /): \\n");
        scanf(" %c", &operator);

        printf("Enter second number: \\n");
        scanf("%f", &num2);

        switch(operator) {
            case '+':
                result = num1 + num2;
                break;
            case '-':
                result = num1 - num2;
                break;
            case '*':
                result = num1 * num2;
                break;
            case '/':
                if (num2 != 0) {
                    result = num1 / num2;
                } else {
                    printf("Error: Division by zero is not allowed.\\n");
                    continue;
                }
                break;
            default:
                printf("Invalid operator.\\n");
                continue;
        }

        printf("Result: %.2f\\n", result);

        printf("Do you want to perform another calculation? (y/n): \\n");
        scanf(" %c", &choice);
    } while (choice != 'n');

    printf("Calculator closed.\\n");
    return 0;
}

学習ポイント: 基本的な入出力、条件分岐、四則演算の処理

基本的な入出力

入出力とは: コンピュータがユーザーからデータを受け取る(入力)こと、そしてその結果をユーザーに伝える(出力)ことを指します。

  • C言語での入力: scanf関数を使って、ユーザーから数値や文字を入力します。
    • 例: scanf(“%d”, &num1); は、ユーザーが入力した整数を変数num1に保存します。
  • C言語での出力: printf関数を使って、計算結果などを画面に表示します。
    • 例: printf(“Result: %d\n”, result); は、resultに格納された計算結果を表示します。

条件分岐

条件分岐とは: プログラムが条件に応じて異なる処理を行うことです。

  • C言語での条件分岐: if文やswitch文を使います。
    • 例: ユーザーが入力した演算子に応じて異なる計算を行います。

コード例:条件分岐

if (operator == '+') {
    result = num1 + num2;
} else if (operator == '-') {
    result = num1 - num2;
}

四則演算の処理

四則演算とは: 加算(足し算)、減算(引き算)、乗算(掛け算)、除算(割り算)のことです。

  • C言語での四則演算: +, -, *, /の演算子を使います。
    • 例: result = num1 + num2; は、num1とnum2を足し算して、その結果をresultに保存します。

C言語のプログラムをコンパイルする

ターミナルでコンパイルコマンドを実行し、C言語のプログラムをコンパイルします。

手順:

  1. ターミナルを開く:
    • 再度、ターミナルを開きます。
  2. ソースファイルのあるディレクトリに移動:
    • cdコマンドを使って、Cファイルを保存したディレクトリに移動します。
  3. コンパイルを実行:
    • 以下のコマンドを使って、Cプログラムをコンパイルします。ここでは、gccを使用していますが、clangでも同様にコンパイルできます。
    • このコマンドは、calculator.cというソースファイルをコンパイルして、calculatorという名前の実行ファイルを生成します。-oは出力ファイルの名前を指定するオプションです。
gcc calculator.c -o calculator


コンパイルされたプログラムを実行する

コンパイルが成功すると、生成された実行ファイルをターミナルで実行できます。

手順①:ローカル環境(macOS上)のターミナルでの実行

  1. 実行:
    • 以下のコマンドを入力して、コンパイルされたプログラムを実行します。
./calculator

以下に、上記手順を実行したコマンド実行例を示します。

(base) xxxxx@xxxxx test-pj % mkdir calculator_app
(base) xxxxx@xxxxx test-pj % vi calculator.c
(base) xxxxx@xxxxx test-pj % gcc calculator.c -o calculator
(base) xxxxx@xxxxx test-pj % ./calculator                  
Enter first number: 
2
Enter an operator (+, -, *, /): 
+
Enter second number: 
3
Result: 5.00
Do you want to perform another calculation? (y/n): 
n
Calculator closed.
(base) xxxxx@xxxxx test-pj % 

手順②:Dockerコンテナでの実行

次に、Dockerfileを作成して、アプリをコンテナ化します。このファイルでは、Cコンパイラをインストールし、アプリをコンパイルして実行します。

# ベースイメージとしてAlpine Linuxを使用
FROM alpine:latest

# Cコンパイラをインストール
RUN apk add --no-cache gcc musl-dev

# アプリの作業ディレクトリを作成
WORKDIR /app

# Cコードをコンテナにコピー
COPY calculator.c .

# Cコードをコンパイル
RUN gcc calculator.c -o calculator

# コンテナが起動したときに実行するコマンド
CMD ["./calculator"]

コンテナイメージのビルドコマンド

ビルドしたコンテナイメージ名に「calculator」を指定しています。

docker build . -t calculator

ビルドしたコンテナイメージの実行コマンド

docker run -it calculator

calculatorアプリが起動して、入力待ちになります。

以下に、上記アプリの実行例を示します。

(base) xxxxx@xxxxx calculator_app % docker run -it calculator
Enter first number: 
5
Enter an operator (+, -, *, /): 
/
Enter second number: 
0
Error: Division by zero is not allowed.
Enter first number: 
3
Enter an operator (+, -, *, /): 
/
Enter second number: 
4
Result: 0.75
Do you want to perform another calculation? (y/n): 
n
Calculator closed.
(base) xxxxx@xxxxx calculator_app % 


計算機アプリ(演算機能の追加)

機能拡張内容

このステップでは、基本的な四則演算に加え、累乗、平方根、余り演算などの高度な数学演算を追加します。

ステップ 1: math.hライブラリのインクルード

まず、C言語の標準数学ライブラリを使えるようにします。これにより、累乗や平方根などの関数を利用できるようになります。

#include <math.h>  // これを追加します

ステップ 2: 累乗演算の追加

累乗演算を行うために^演算子を追加し、pow関数を使って計算します。

case '^':
    result = pow(num1, num2);  // num1 を num2 乗します
    break;

ステップ 3: 平方根演算の追加

平方根は単一の値を取るため、特殊な処理を追加します。ここではsキーを押すことで平方根を計算します。

case 's':
    if (num1 >= 0) {
        result = sqrt(num1);  // num1 の平方根を計算
    } else {
        printf("Error: Cannot calculate square root of a negative number.\\n");
        continue;
    }
    break;

ステップ 4: 余り演算の追加

整数の余りを計算するために%演算子を追加します。

case '%':
    if (num2 != 0) {
        result = (int)num1 % (int)num2;  // 整数として扱い余りを計算
    } else {
        printf("Error: Division by zero is not allowed.\\n");
        continue;
    }
    break;

追加後の完全なコード

#include <stdio.h>
#include <math.h>  // 追加

int main() {
    float num1, num2, result;
    char operator;
    char choice;

    do {
        printf("Enter first number: \\n");
        scanf("%f", &num1);

        printf("Enter an operator (+, -, *, /, ^, s, %%): \\n");  // 演算子の選択肢を拡張
        scanf(" %c", &operator);

        if (operator != 's') {
            printf("Enter second number: \\n");
            scanf("%f", &num2);
        }

        switch(operator) {
            case '+':
                result = num1 + num2;
                break;
            case '-':
                result = num1 - num2;
                break;
            case '*':
                result = num1 * num2;
                break;
            case '/':
                if (num2 != 0) {
                    result = num1 / num2;
                } else {
                    printf("Error: Division by zero is not allowed.\\n");
                    continue;
                }
                break;
            case '^':
                result = pow(num1, num2);
                break;
            case 's':
                if (num1 >= 0) {
                    result = sqrt(num1);
                } else {
                    printf("Error: Cannot calculate square root of a negative number.\\n");
                    continue;
                }
                break;
            case '%':
                if (num2 != 0) {
                    result = (int)num1 % (int)num2;
                } else {
                    printf("Error: Division by zero is not allowed.\\n");
                    continue;
                }
                break;
            default:
                printf("Invalid operator.\\n");
                continue;
        }

        printf("Result: %.2f\\n", result);

        printf("Do you want to perform another calculation? (y/n): \\n");
        scanf(" %c", &choice);
    } while (choice != 'n');

    printf("Calculator closed.\\n");
    return 0;
}

学習ポイント: 数学関数の利用、条件分岐の拡張

数学関数の利用

  • 数学関数とは: より高度な数学的計算を行うために使う関数です。C言語ではmath.hというライブラリに含まれています。
  • 累乗計算(べき乗): pow関数を使って、ある数値を別の数値で累乗します。
    • 例: pow(2, 3)は、2の3乗(2×2×2)を計算し、結果は8になります。
  • 平方根の計算: sqrt関数を使って、ある数値の平方根を計算します。
    • 例: sqrt(16)は、16の平方根(4)を計算します。
  • 条件分岐の拡張
  • 複数の条件を処理する: 基本の条件分岐に、新しい条件を追加して機能を拡張します。
    • 例:
switch(operator) {
    case '+':
        result = num1 + num2;
        break;
    case '^':
        result = pow(num1, num2);
        break;
    case 's':
        result = sqrt(num1);
        break;
    // さらに条件を追加可能
}

ここでは、switch文に新しいケース(条件)として累乗と平方根の計算を追加しています。


アプリのコンパイル(ビルド)と実行

手順①:ローカル環境(macOS上)のターミナルでの実行

gcc calculator.c -o calculator
./calculator

手順②:Dockerコンテナでの実行

docker build . -t calculator
docker run -it calculator

calculatorアプリが起動して、入力待ちになります。

以下に、上記アプリの実行例を示します。

(base) xxxxx@xxxxx calculator_app % docker run -it calculator
Enter first number: 
5
Enter an operator (+, -, *, /): 
/
Enter second number: 
0
Error: Division by zero is not allowed.
Enter first number: 
3
Enter an operator (+, -, *, /): 
/
Enter second number: 
4
Result: 0.75
Do you want to perform another calculation? (y/n): 
n
Calculator closed.
(base) xxxxx@xxxxx calculator_app % 


計算機アプリ(データ型の拡張)

機能拡張内容

このステップでは、整数型の変数を浮動小数点型(floatまたはdouble)に変更して、小数点を含む数値を計算できるようにします。

ステップ 1: 変数の型を変更

すべての数値を扱う変数をint型からfloatまたはdouble型に変更します。これにより、小数点を含む数値を扱えるようになります。

float num1, num2, result;  // すべての数値変数を float 型に変更

ステップ 2: 結果の表示を改良

小数点以下の桁数を指定して表示するため、printfのフォーマット指定子を使用します。

printf("Result: %.2f\\n", result);  // 小数点以下2桁まで表示

完全なコード(データ型サポート拡張後)

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

int main() {
    float num1, num2, result;
    char operator;
    char choice;

    do {
        printf("Enter first number: \\n");
        scanf("%f", &num1);

        printf("Enter an operator (+, -, *, /, ^, s, %%): \\n");
        scanf(" %c", &operator);

        if (operator != 's') {
            printf("Enter second number: \\n");
            scanf("%f", &num2);
        }

        switch(operator) {
            case '+':
                result = num1 + num2;
                break;
            case '-':
                result = num1 - num2;
                break;
            case '*':
                result = num1 * num2;
                break;
            case '/':
                if (num2 != 0) {
                    result = num1 / num2;
                } else {
                    printf("Error: Division by zero is not allowed.\\n");
                    continue;
                }
                break;
            case '^':
                result = pow(num1, num2);
                break;
            case 's':
                if (num1 >= 0) {
                    result = sqrt(num1);
                } else {
                    printf("Error: Cannot calculate square root of a negative number.\\n");
                    continue;
                }
                break;
            case '%':
                if (num2 != 0) {
                    result = (int)num1 % (int)num2;
                } else {
                    printf("Error: Division by zero is not allowed.\\n");
                    continue;
                }
                break;
            default:
                printf("Invalid operator.\\n");
                continue;
        }

        printf("Result: %.6f\\n", result);

        printf("Do you want to perform another calculation? (y/n): \\n");
        scanf(" %c", &choice);
    } while (choice != 'n');

    printf("Calculator closed.\\n");
    return 0;
}

学習ポイント: データ型の理解、精度の管理

データ型の理解

データ型とは: プログラム内で扱うデータの種類を指定するものです。数値には整数型(int)と浮動小数点型(float, double)があります。

  • 整数型と浮動小数点型の違い:
    • int: 整数だけを扱います。例: 1, 42, -5
    • floatやdouble: 小数点を含む数値を扱います。例: 3.14, -0.001

精度の管理

精度とは: 数値をどれだけ正確に表現できるかを示します。小数点以下の桁数が多いほど、精度が高くなります。

  • C言語での精度: floatは小数点以下7桁程度、doubleは15桁程度の精度があります。
  • 結果の表示: printfのフォーマット指定子を使って、小数点以下の表示桁数を制御します。
    • 例: printf(“%.6f”, result);は、小数点以下2桁まで表示します。

以下に、上記アプリの実行例を示します。

(base) xxxxx@xxxxx calculator_app % docker run -it calculator   
Enter first number: 
2
Enter an operator (+, -, *, /, ^, s, %): 
s
Result: 1.414214
Do you want to perform another calculation? (y/n): 
n
Calculator closed.
(base) xxxxx@xxxxx calculator_app % 


計算機アプリ(インタフェースの改良)

機能拡張内容

ユーザーが操作しやすいメニューインターフェースを作成し、計算機を直感的に使えるようにします。

ステップ 1: メニューオプションの整理

メニューオプションを明確に表示し、ユーザーに対して操作方法を説明します。

printf("Select an operation:\\n");
printf(" + : Addition\\n");
printf(" - : Subtraction\\n");
printf(" * : Multiplication\\n");
printf(" / : Division\\n");
printf(" ^ : Power\\n");
printf(" s : Square Root\\n");
printf(" %% : Modulus\\n");
printf(" q : Quit\\n");

ステップ 2: ユーザー入力の処理

ユーザーが選択した演算子が有効かどうかを確認し、無効な場合は再入力を促します。

do {
    printf("Enter an operator: ");
    scanf(" %c", &operator);
    if (strchr("+-*/^s%q", operator) == NULL) {
        printf("Invalid operator. Please try again.\\n");
    }
} while (strchr("+-*/^s%q", operator) == NULL);

ステップ 3: ヘルプオプションの追加

ユーザーがhキーを押すと、利用可能なオプションを表示するヘルプメニューを追加します。

case 'h':
    printf("Help Menu:\\n");
    printf(" + : Addition\\n");
    printf(" - : Subtraction\\n");
    printf(" * : Multiplication\\n");
    printf(" / : Division\\n");
    printf(" ^ : Power\\n");
    printf(" s : Square Root\\n");
    printf(" %% : Modulus\\n");
    printf(" q : Quit\\n");
    break;

完全なコード(メニューインターフェース改良後)

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

int main() {
    float num1, num2, result;
    char operator;
    char choice;

    do {
        printf("Select an operation:\\n");
        printf(" + : Addition\\n");
        printf(" - : Subtraction\\n");
        printf(" * : Multiplication\\n");
        printf(" / : Division\\n");
        printf(" ^ : Power\\n");
        printf(" s : Square Root\\n");
        printf(" %% : Modulus\\n");
        printf(" h : Help\\n");
        printf(" q : Quit\\n");

        do {
            printf("Enter an operator: \\n");
            scanf(" %c", &operator);
            if (strchr("+-*/^s%hq", operator) == NULL) {
                printf("Invalid operator. Please try again.\\n");
            }
        } while (strchr("+-*/^s%hq", operator) == NULL);

        if (operator != 's' && operator != 'q') {
            printf("Enter first number: \\n");
            scanf("%f", &num1);

            printf("Enter second number: \\n");
            scanf("%f", &num2);
        } else if (operator == 's') {
            printf("Enter a number: \\n");
            scanf("%f", &num1);
        }

        switch(operator) {
            case '+':
                result = num1 + num2;
                break;
            case '-':
                result = num1 - num2;
                break;
            case '*':
                result = num1 * num2;
                break;
            case '/':
                if (num2 != 0) {
                    result = num1 / num2;
                } else {
                    printf("Error: Division by zero is not allowed.\\n");
                    continue;
                }
                break;
            case '^':
                result = pow(num1, num2);
                break;
            case 's':
                if (num1 >= 0) {
                    result = sqrt(num1);
                } else {
                    printf("Error: Cannot calculate square root of a negative number.\\n");
                    continue;
                }
                break;
            case '%':
                if (num2 != 0) {
                    result = (int)num1 % (int)num2;
                } else {
                    printf("Error: Division by zero is not allowed.\\n");
                    continue;
                }
                break;
            case 'h':
                printf("Help Menu:\\n");
                printf(" + : Addition\\n");
                printf(" - : Subtraction\\n");
                printf(" * : Multiplication\\n");
                printf(" / : Division\\n");
                printf(" ^ : Power\\n");
                printf(" s : Square Root\\n");
                printf(" %% : Modulus\\n");
                printf(" q : Quit\\n");
                break;
            case 'q':
                printf("Calculator closed.\\n");
                return 0;
						default:
                printf("Invalid operator.\\n");
                continue;
        }

        printf("Result: %.6f\\n", result);

        printf("Do you want to perform another calculation? (y/n): \\n");
        scanf(" %c", &choice);
    } while (choice != 'n');

    printf("Calculator closed.\\n");
    return 0;
}

学習ポイント: ユーザーインターフェースの設計、文字列操作

ユーザーインターフェースの設計

  • ユーザーインターフェースとは: ユーザーとプログラムがやり取りするための画面や入力方法を指します。プログラムがどのようにユーザーに操作を促すかを設計します。
    • メニュー表示: ユーザーが選択しやすいように、操作の選択肢を画面にわかりやすく表示します。
    • 例:
printf("Select an operation:\\n");
printf(" + : Addition\\n");
printf(" - : Subtraction\\n");
printf(" q : Quit\\n");

文字列操作

文字列とは: 一連の文字のことです。C言語では、文字列はchar型の配列で扱われます。

  • 文字の検索: strchr関数を使って、特定の文字が文字列に含まれているかをチェックします。
  • 例:
if (strchr("+-*/^s%hq", operator) == NULL) {
    printf("Invalid operator.\\n");
}

ここでは、ユーザーが入力した演算子が有効かどうかをチェックしています。


終了とクリーンアップ

作業が終わった後、開発環境を終了し、不要なリソースをクリーンアップします。Docerコンテナはイメージをビルドする度に、データを消費しますので、利用しないイメージはこまめに削除するのがお薦めです。

クリーンアップ

不要になったDockerのリソースを削除します。

docker system prune

削除前

(base) xxxxx@xxxxx hello % docker images
REPOSITORY   TAG       IMAGE ID       CREATED       SIZE
calculator   latest    433bf9bb7082   2 hours ago   154MB
<none>       <none>    dd5ddd3f072a   2 hours ago   154MB
<none>       <none>    b6d0f27293fc   2 hours ago   154MB
<none>       <none>    1062aaecac05   2 hours ago   154MB
<none>       <none>    3b8aef630702   2 hours ago   154MB
<none>       <none>    89abdaf891d9   2 hours ago   154MB
<none>       <none>    82da1fda6d3d   2 hours ago   154MB
<none>       <none>    645997418ea6   2 hours ago   154MB
<none>       <none>    c01c7309f1ae   3 hours ago   154MB


まとめ

C言語は、ハードウェアに近いレベルでの操作が可能であり、高速かつ効率的なプログラムを作ることができるため、非常に広範な分野で利用されています。

システムソフトウェアからゲーム開発、科学技術計算、ハードウェア制御まで、さまざまな場面でC言語の力を活用することができます。

これからもCとmacOS、Dockerコンテナを活用して、より高度なアプリケーション開発に挑戦してみてください!

最後に、この記事がCの学習やアプリ開発に役立つことを願っています。

SNSでもご購読できます。

コメントを残す

*


reCaptcha の認証期間が終了しました。ページを再読み込みしてください。