2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

clangでC言語をコンパイル・フォーマット・静的解析

Last updated at Posted at 2023-08-07

2023/08/15 clang-tidyの説明のtypoを修正

やること

  • dockerコンテナ上にclangを使った環境を作成
  • Hello worldプログラムの作成とコンパイル、実行
  • clang-formatを用いたコードの整形
  • clang-tidyを用いた静的解析

clangとは

クランと読みます。
CやC++向けのコンパイラフロントエンドです。
バックエンドのLLVMと合わせて、C言語で書かれたプログラムから実行ファイルを生成します。

公式ページ
Clang(wikipedia)

環境を作る

Dockerはすでにインストール済みの前提で説明します。
まだの人はこちらを参照。
https://docs.docker.jp/engine/installation/

docker containerの作成

以下のコマンドを実行して開発用コンテナを作成し接続します。

# 適当な開発用ディレクトリに移動
cd "path/to/working/dir"
# ベースイメージはなんでもいい。今回はdevianのslim版
docker pull debian:bookworm-slim
# コンテナを作成
docker run -dit -v $(pwd):/work -w /work --name clang-dev debian:bookworm-slim
# コンテナに入る
docker exec -it clang-dev bash

コンテナに入ったらパッケージをインストールします。
必要なのはこれ。

  • clang
    • コンパイル時に使います
  • clang-format
    • フォーマッタを使って整形します
  • clang-tidy
    • 静的解析に使います
apt update
apt install -y clang clang-format clang-tidy

最後にインストールできているかを確認するためにバージョン確認しましょう。

clang -v
clang-format --version
clang-tidy --version

以上で環境構築は完了です。

Hello worldの実行

作業ディレクトリに以下のファイルを作成します。
hello_worldしていないのは内緒。

hello_world.c
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>

int main(void) {
int i;
char input;
printf("数字を入力してください\n>> ");
input=getchar();

if(isdigit(input)){
i=input-'0';
}

printf("入力されたのは %d です。\n", i);
return 0;
}

コンテナ内のコマンドラインから以下のコマンドでコンパイルを実施します。

clang hello_world.c -o hello_world.out

hello_world.outというファイルができればOK。
最後にこのhello_world.outを実行します。

./hello_world.out

# 動作例
# 数字を入力してください
# >> 1
#入力されたのは 1 です。

以上でコンパイルと実行は完了です。 

clang-formatを用いたコードの整形

ここで先ほど使用したコードをもう一度見て見ましょう。

hello_world.c
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>

int main(void) {
int i;
char input;
printf("数字を入力してください\n>> ");
input=getchar();

if(isdigit(input)){
i=input-'0';
}

printf("入力されたのは %d です。\n", i);
return 0;
}

インデントもされていないし、ちょっと見づらいですね。
ここでclang-formatの出番です。
次のコマンドを実行して、もう一度hello_world.cを開いてみます。

clang-format -i hello_world.c 
hello_world.c
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>

int main(void) {
  int i;
  char input;
  printf("数字を入力してください\n>> ");
  input = getchar();

  if (isdigit(input)) {
    i = input - '0';
  }

  printf("入力されたのは %d です。\n", i);
  return 0;
}

このように適切にインデントされ、見やすくなったと思います。
.clang-formatファイルに設定を記述すると{}の位置やタブの文字数などを好きに設定することができます。
参考:clang-formatをイイ感じに設定する

clang-tidyを用いた静的解析

上のコード、ちゃんと動いたと思うのですがバグが混入されています。気づきましたか?
入力に数字以外を入れると、初期化されていない変数iを出力しようとするので表示がバグります。 

./hello_world.out 
# 数字を入力してください
# >> a
# 入力されたのは -1401456832です。

こういう問題を未然に防ぐために、静的解析ツールが役立ちます。
以下のように実行します。

clang-tidy hello_world.c
clang-tidy hello_world.c
Error while trying to load a compilation database:
Could not auto-detect compilation database for file "hello_world.c"
No compilation database found in /work or any parent directory
fixed-compilation-database: Error while opening fixed database: No such file or directory
json-compilation-database: Error while opening JSON database: No such file or directory
Running without flags.
1 warning generated.
/work/hello_world.c:15:3: warning: 2nd function call argument is an uninitialized value [clang-analyzer-core.CallAndMessage]
  printf("入力されたのは %d です。\n", i);
  ^                                    ~
/work/hello_world.c:6:3: note: 'i' declared without an initial value
  int i;
  ^~~~~
/work/hello_world.c:11:7: note: Assuming the condition is false
  if (isdigit(input)) {
      ^
/usr/include/ctype.h:192:21: note: expanded from macro 'isdigit'
# define isdigit(c)     __isctype((c), _ISdigit)
                        ^~~~~~~~~~~~~~~~~~~~~~~~
/usr/include/ctype.h:89:4: note: expanded from macro '__isctype'
  ((*__ctype_b_loc ())[(int) (c)] & (unsigned short int) type)
   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/work/hello_world.c:11:3: note: Taking false branch
  if (isdigit(input)) {
  ^
/work/hello_world.c:15:3: note: 2nd function call argument is an uninitialized value
  printf("入力されたのは %d です。\n", i);
  ^                                    ~

warningが残っていても動きはしますが、バグを引き起こす可能性があるので基本的には消しておきましょう。
 
以上。

2
0
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?