プログラミング言語を整える
中級C言語を整える。
= 新人研修でよく最初に習う C を、ブラウザの中で書いて動かしながら学ぶ
C 言語が初めての方は、先に そもそも C 言語とは・どんな場面で使うか をどうぞ。
C言語とは
メモリやハードウェアに近いところまで自分で扱える、半世紀以上使われ続けているコンパイル言語です。
| 誕生 | 1972年 |
|---|---|
| 作った人・組織 | デニス・リッチー(ベル研究所) |
| 型の付け方 | 静的型付け |
| 動かし方 | コンパイルして機械語の実行ファイルを作る |
| ファイルの拡張子 | .c(ヘッダーファイルは .h) |
| 手元で動かすには | gcc main.c -o main && ./main |
日本での使われ方
- 家電や産業機器に入っているマイコンの組み込みソフトウェア開発で、広く使われています。
- 自動車の電子制御ユニット(ECU)など車載ソフトウェアの開発でも使われています。
- センサーや小型の通信機器など、メモリの少ない IoT 機器のプログラムにも使われています。
- Linux カーネルをはじめ、OS やデバイスドライバの多くは C言語で書かれています。
- Python や Ruby など、ほかの言語の処理系(インタプリタ)自体も C言語で書かれているものがあります。
新人研修での位置づけ
C言語は変数の型・メモリ・ポインタといった、コンピュータが内部でどう動くかを意識しながら書く言語です。そのため、ほかの言語を学ぶときの土台になるとして、情報系の授業や新人研修の最初に取り上げられることがあります。文法の要素が比較的少なく、教科書や教材が長年にわたって蓄積されていることも理由の一つです。
関わる資格・試験
- 基本情報技術者試験(かつて午後試験の選択言語の一つでした。2023年度からの科目B試験は擬似言語のみです)
- C言語プログラミング能力認定試験(サーティファイ)
ブラウザで C を動かす
C のインタプリタ PicoC をこのページの中で動かします。書いたコードも入力も、端末の外には出ません。下のお題やクイズの「練習場で動かす」を押すと、そのコードがここに入ります。
サンプルを押すと、エディタと入力欄に入ります。
実行結果
「実行」を押すと、ここに printf の出力が出ます。
この練習場でできること・できないこと
- 動かしているのは gcc ではなく、C をその場で読みながら実行する PicoC です。授業や研修で書く程度のコード(printf・scanf・配列・ポインタ・構造体・malloc・ファイルの読み書き)は動きます。
- ファイルはこのページの中だけにある仮のものです。実行のたびに消え、端末のファイルには触れません。
- NULL の参照のように、gcc で作った実行ファイルなら黙って落ちる間違いを、PicoC は行と桁を示して止めます。逆に、PicoC には無い機能(assert.h など)もあります。
- 3秒を超えると自動で止めます(終わらないループを書いてもブラウザは固まりません)。
- main の引数 argc は 0 になります(gcc で作った実行ファイルは 1 以上)。
- 最終的な確認は gcc や clang などのコンパイラで行ってください。上の「概要」の表にある手順で手元でも動かせます。
そもそも C 言語とは
機械に近いところまで自分で扱える、ソフトウェアの土台になってきた言語。
C 言語は、1972年にベル研究所のデニス・リッチーが作った言語です。書いたプログラムをコンパイラで機械語の実行ファイルに変換してから動かします。メモリの使い方を自分で細かく指定できるので、小さく速いプログラムが作れる一方、書き手が気をつけることも増えます。
多くの言語の土台になってきた言語で、C++・Java・C# の書き方の多くは C に似ています。実行前にコンパイルが必要な点は、書いたそばから動かせる Python のような言語との大きな違いです。
どこで・どんな場面で使うか
-
組み込み機器の制御ソフト
家電・産業機器・車載機器の中で動くプログラムです。メモリの少ないマイコンでも動かせるため、今も広く使われています。
-
OS やドライバー
パソコンやサーバーの土台になるソフトウェアです。たとえば Linux のカーネルは C で書かれています。
-
IoT 機器やセンサー
小さく省電力な機器のプログラムに向いています。
-
ほかの言語の裏側
Python など、別の言語を動かす仕組み(処理系)が C で書かれていることがあります。
-
求人・研修での位置づけ
組み込み・制御系の求人で経験を問われることがあります。また、メモリの仕組みを学ぶ題材として、新人研修や学校の授業で取り上げられます。
なぜ覚えておくと得か
- ほかの言語の理解が深まる。メモリや配列、型といった考え方を C で一度つかむと、C++・Java・C# だけでなく、ほかの言語の動きも想像しやすくなります。
- 組み込み・制御の仕事への入口になる。機器に近い分野では、今も C が基本の言語です。
- エラーメッセージを読む力がつく。コンパイルエラーと向き合うことで、プログラムのどこが間違っているかを読み解く習慣が身につきます。
上の練習場で短いプログラムを実際に動かして、書いたとおりに結果が出ることを確かめてみてください。
5つのお題をC言語で書く
C・C++・Java・C#・COBOL・VBA の6ページで、同じ5つのお題を同じ順に並べています。言語を切り替えると、書き方の違いがそのまま比べられます。
このページではコードを実行できません(上の練習場を除く)。表示している出力とエラーのメッセージは、gcc 13.3.0 で実際にコンパイル・実行して、一字一句同じになることを確かめたものです。
コード(.c)
#include <stdio.h>
int main(void) {
printf("Hello, World!\n");
return 0;
}
実行すると
Hello, World!
画面に文字を出すには stdio.h を読み込んで printf を使います。改行は自動では付かないので、\n を自分で書きます。
コード(.c)
#include <stdio.h>
int main(void) {
for (int i = 1; i <= 15; i++) {
if (i % 15 == 0) {
printf("FizzBuzz\n");
} else if (i % 3 == 0) {
printf("Fizz\n");
} else if (i % 5 == 0) {
printf("Buzz\n");
} else {
printf("%d\n", i);
}
}
return 0;
}
実行すると
1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz
数値を出すときは %d のような書式指定子を使います。for 文の中で変数を宣言する書き方は C99 から使えます。
コード(.c)
#include <stdio.h>
int main(void) {
int nums[] = {3, 1, 4, 1, 5, 9, 2, 6};
int n = sizeof(nums) / sizeof(nums[0]); // 要素の数
int sum = 0;
for (int i = 0; i < n; i++) {
sum += nums[i];
}
double avg = (double)sum / n; // int どうしの割り算だと小数が切り捨てられる
printf("合計: %d\n", sum);
printf("平均: %.2f\n", avg);
return 0;
}
実行すると
合計: 31 平均: 3.88
配列は自分の長さを持たないため、sizeof で要素数を計算します。int どうしの割り算は小数が切り捨てられるので、平均は (double) に変換してから割ります。
コード(.c)
#include <stdio.h>
int main(void) {
FILE *fp = fopen("memo.txt", "w");
if (fp == NULL) {
printf("書き込み用に開けませんでした\n");
return 1;
}
fprintf(fp, "1行目: りんご\n");
fprintf(fp, "2行目: みかん\n");
fclose(fp);
fp = fopen("memo.txt", "r");
if (fp == NULL) {
printf("読み込み用に開けませんでした\n");
return 1;
}
char line[256];
while (fgets(line, sizeof(line), fp) != NULL) {
printf("%s", line); // fgets は改行も読み込む
}
fclose(fp);
return 0;
}
実行すると
1行目: りんご 2行目: みかん
fopen が失敗すると NULL が返るので、使う前に必ず確かめます。開いたファイルは fclose で自分で閉じます。
コード(.c)
#include <stdio.h>
int divide(int a, int b, int *result) {
if (b == 0) {
return 0; // 失敗
}
*result = a / b;
return 1; // 成功
}
int main(void) {
int result;
if (divide(10, 2, &result)) {
printf("10 / 2 = %d\n", result);
}
if (!divide(10, 0, &result)) {
printf("0 では割れません\n");
}
return 0;
}
実行すると
10 / 2 = 5 0 では割れません
C言語には例外の仕組みがないため、割る前に自分で 0 かどうかを確かめ、成功・失敗を戻り値で知らせます。整数を 0 で割ると、プログラムが異常終了することがあります。
出力当てクイズ
コードを読んで、画面に出るものを選んでください。選ぶと正解と理由が出ます(答えはどこにも送られず、記録もしません)。
問1
#include <stdio.h>
int main(void) {
printf("%d\n", 7 / 2);
return 0;
}
画面に出るのは?
int どうしの割り算の結果は int になり、小数点以下は切り捨てられます。3.5 を得たいときは 7.0 / 2 のように片方を小数にします。
問2
#include <stdio.h>
int main(void) {
int i = 5;
int a = i++;
int b = ++i;
printf("%d %d\n", a, b);
return 0;
}
画面に出るのは?
i++ は「今の値を使ってから 1 増やす」ので a は 5 です。++i は「1 増やしてから使う」ので、6 から 7 になった値が b に入ります。
問3
#include <stdio.h>
int main(void) {
char c = 'A' + 2;
printf("%c %d\n", c, c);
return 0;
}
画面に出るのは?
char は小さな整数として扱われ、ASCII では 'A' が 65 です。65 + 2 = 67 は 'C' なので、%c では C、%d では 67 と表示されます。
問4
#include <stdio.h>
int main(void) {
int a[] = {10, 20, 30, 40};
printf("%d\n", a[1] + a[3]);
return 0;
}
画面に出るのは?
配列の添字は 0 から始まります。a[1] は 2 番目の 20、a[3] は 4 番目の 40 なので、合計は 60 です。
よくあるエラーと直し方
初めて書く人が出会いやすいエラーです。メッセージは英語のまま出ることが多いので、どの言葉を手がかりにすればよいかもあわせて載せています。
コンパイル時 セミコロンの付け忘れ
エラーになるコード
#include <stdio.h>
int main(void) {
printf("Hello\n")
return 0;
}
画面に出るメッセージ(抜粋)
error: expected ‘;’ before ‘return’
何が起きているか:文の終わりに付けるセミコロンがないため、コンパイラが次の行の return を同じ文の続きとして読もうとして止まっています。
直し方:エラーが指す行の 1 つ前の行末を確かめ、printf("Hello\n"); のようにセミコロンを付けます。
コンパイル時 宣言していない変数を使った
エラーになるコード
#include <stdio.h>
int main(void) {
int total = 10;
printf("%d\n", totl);
return 0;
}
画面に出るメッセージ(抜粋)
error: ‘totl’ undeclared (first use in this function); did you mean ‘total’?
何が起きているか:C言語では変数を使う前に宣言が必要です。ここでは total と書くつもりが totl と打ち間違えたため、宣言されていない名前として扱われています。
直し方:変数名のつづりを宣言と合わせます。新しい変数を使う場合は、先に int totl = 0; のように宣言します。
実行時 assert の失敗(NULL ポインタのチェック)
エラーになるコード
#include <assert.h>
#include <stdio.h>
void print_name(const char *name) {
assert(name != NULL); // NULL が渡されたらここで止める
printf("%s\n", name);
}
int main(void) {
print_name("Taro");
print_name(NULL);
return 0;
}
画面に出るメッセージ(抜粋)
main: main.c:5: print_name: Assertion `name != NULL' failed.
何が起きているか:assert は「ここではこの条件が成り立つはず」という前提を書く仕組みで、条件が偽になると標準エラーにメッセージを出してプログラムを止めます。NULL ポインタをそのまま使うと Segmentation fault で落ちますが、assert を置いておくと止まった場所と理由が分かります。
直し方:関数を呼ぶ側で NULL を渡さないようにするか、関数の中で if (name == NULL) のように確かめて処理を分けます。
実行時 同じメモリを 2 回 free した
エラーになるコード
#include <stdlib.h>
int main(void) {
int *p = malloc(sizeof(int) * 4);
free(p);
free(p); // すでに解放したメモリをもう一度解放している
return 0;
}
画面に出るメッセージ(抜粋)
free(): double free detected in tcache 2
何が起きているか:malloc で確保したメモリは free で 1 回だけ解放します。同じポインタを 2 回 free すると、C ライブラリ(glibc)が異常を見つけてプログラムを止めます。
直し方:free した直後に p = NULL; と代入しておくと、もう一度 free(p) を呼んでも何も起きません。どこで解放するかを 1 か所に決めておくことも有効です。
ほかの言語の解説
- C++ 1983年(1979年に「C with Classes」として開発が始まりました)・静的型付け
- Java 1995年・静的型付け
- C# 2000年(発表)・静的型付け
- COBOL 1959年・静的型付け(PIC 句で桁数と種類を宣言する)
- VBA 1993年(Excel 5.0 に搭載)・型を宣言しなくても動きます(Variant 型になる)。Option Explicit を書くと変数の宣言を強制できます
6言語の並べての比較は プログラミング言語を整える にあります。 書いたコードはどこにも送られません(このページに入力欄はクイズの選択だけで、選んだ答えも記録しません)。
このツールについて
メモリやハードウェアに近いところまで自分で扱える、半世紀以上使われ続けているコンパイル言語です。
C言語の概要と、同じ5つのお題・出力当てクイズ・よくあるエラー。ページ内で C が動く練習場つき(PicoC・3秒で自動停止)。
主な機能
- 概要(誕生・作った人・型・動かし方)と日本での使われ方・新人研修・資格
- 5つのお題のコード例(C++・Java・C#・COBOL・VBA の同じお題へ移れる)
- 出力当てクイズ4問(正解と理由)
- よくあるエラー4件(gcc の実際のメッセージ・原因・直し方)
- PicoC の練習場(scanf の入力・サンプル6本・お題とクイズをそのまま実行)
- 出力は gcc 13.3.0 で照合済み・外部通信なし・送信ゼロ
よくある質問
Q. 練習場では gcc が動いているのですか?
A. いいえ。C のコードをその場で読みながら実行するインタプリタの PicoC(picoc-js 1.0.12・MIT ライセンス)を、このサイトに置いた写しから Web Worker で動かしています。授業や研修で書く程度のコードは動きますが、assert.h のように PicoC に無い機能もあるので、最終的な確認は gcc や clang で行ってください。
Q. scanf で入力を受け取るにはどうすればよいですか?
A. エディタの下の「入力」欄に、キーボードから打つつもりの内容を書いてから実行します。scanf・fgets・getchar はその内容を前から順に読み、使い切るとファイルの終わり(EOF)になります。
Q. 書いたコードはどこかに送られますか?
A. 送られません。PicoC の本体はこのサイト自身から読み込み、外部の配信元(CDN)は使っていないので、「実行」を押しても外への通信は起きません。終わらないループは3秒で自動的に止めます。
入力値はURLの「#」以降に入るためサーバーには送信されません。リンクを開くと同じ状態を復元します。