この記事ではC言語を学び始めた方への第一歩となる記事となります。

変数・型・演算子は、C言語に限らずプログラムの基礎中の基礎です。この記事は、その基礎を7問の練習問題(基礎5問+発展2問)で手を動かし学びます。解答と解説は各問題の「+」を押すと開きます。まず自分で書いてみて、それから答え合わせをしてください。

<自己紹介>
筆者は現役の組込みエンジニアです。
組込みエンジニアを目指すとなるにあたり、最低限の言語知識を未経験向けC言語練習問題を通じて学習していただき、最終的にはAruduinoマイコンプログラムを自作できるのをゴールとして記事を作成しています。

このテーマの要点

変数・型・演算子でやることはこれだけ

  • 値は型(int / float / char など)を決めて変数に入れる。型ごとに入る値の範囲が違う
  • 変わらない値は定数(const / #defineにする
  • 演算子で計算・比較・論理の判断をする
  • 「=(代入)」と「==(比較)」の取り違えの注意点について理解

プログラムは、変数を宣言して値を入れ、計算し、比較して動作を決める。この繰り返しでできています。この基本動作をまずはしっかりと理解していただきたいと思います。

前提知識:printf・型・定数・演算子

問題に入る前に、この記事で使う道具を最小限に確認します。暗記は不要で、「こういうものがある」と分かれば十分です。
※組み込みソフトでは使用しない知識なので学習のためだけに使用すると思ってください。

printf(実行結果を画面に表示する)

プログラムの実行結果は、printf という関数で画面(コンソール)に表示できます。このドリルでも、答え合わせはすべて printf の表示で確認します。表示する値の型に合わせて、フォーマット指定子を使い分けます。

指定子 表示するもの
%d 整数(int) printf("%d", 20);20
%f / %.1f 小数(float / double) printf("%.1f", 3.14);3.1
%c 1文字(char) printf("%c", 'A');A
%s 文字列 printf("%s", "hello");hello
\n 改行(指定子ではなく特殊文字) printf("a\nb"); → aとbが2行になる

指定した型と実際の値がズレると、表示が崩れます。「整数は %d、小数は %f、1文字は %c」とセットで覚えてしまいましょう。

変数と型(値の入れ物と、入る値の範囲)

変数は「値を入れる箱」で、箱を作るとき(宣言するとき)にを決めます。型が決めるのは「何を入れるか」と「どこまで大きい値が入るか(サイズ)」の2つです。よく使う型を一覧にします。

サイズ 入る範囲(符号あり) 入る範囲(符号なし unsigned)
char 1バイト -128 〜 127 0 〜 255
short 2バイト -32,768 〜 32,767 0 〜 65,535
int 4バイト 約 -21億 〜 21億 0 〜 約43億
long long 8バイト 約 ±922京 0 〜 約1,845京
float 4バイト 小数(有効桁 約7桁)
double 8バイト 小数(有効桁 約15桁)

ポイントは2つです。整数を入れる型(char〜long long)と小数を入れる型(float / double)に分かれること。そして同じ整数でも、型によって入る範囲が違うことです。範囲を超える値を入れると、値が壊れます(あふれ)。全部覚える必要はなく、この表に戻ってこられれば十分です。

型ごとに使うメモリの大きさを1マス1バイトの箱で表した図
図:型ごとのメモリサイズ。上の表の「サイズ」を箱の数で表したもの
メモ:サイズは環境で変わることがある
上の表は一般的なPC環境(gcc など)の値です。マイコンによっては int が2バイトのこともあります。そのため実務では、uint8_t(8ビット符号なし)のようにサイズを名前で明示した型を使うのが定番です。まずはこの表の型で練習して大丈夫です。

定数(変わらない値には const)

変数は「あとから値を変えられる箱」ですが、プログラム中で絶対に変わらない値もあります。円周率や、設定として決め打ちにした値などです。こういう値は定数にしておくと、うっかり書き換えるミスをコンパイラが止めてくれます。

  • const を付ける:const int MAX = 100;(型のある定数。宣言時に初期化が必要)
  • #define で置きかえる:#define MAX 100(コンパイル前に文字を置き換える書き方。行末にセミコロンは付けません)

const を付けた変数に MAX = 200; と代入し直そうとすると、コンパイルの時点でエラーになります。エラーと聞くと悪いことのようですが、「変えてはいけない値を守ってくれている」ということです。発展問題で実際にエラーを起こして確かめます。

演算子(計算・比較・論理)

  • 四則+ - * / と、余りを出す %(例:7 % 2 は 1)
  • 比較==(等しい)、!=(等しくない)、> < >= <=
  • 論理&&(かつ)、||(または)、!(否定)

1つだけ先に注意しておくと、整数どうしの割り算は小数を切り捨てます7 / 2 は 3.5 ではなく 3 です。小数の結果がほしいときは、(float)a / b のようにキャスト(型の変換)をしてから割ります。これも問題で確かめます。

最重要:「=」は代入、「==」は比較

C言語で最初につまずくのがここです。見た目が似ているのに、意味はまったく違います。

== は「等しいか比較」

if (x == 5)
xが5と等しいかを調べる。結果は 真(1) か 偽(0)。

= は「代入」

if (x = 5)
xに5を入れてしまう。結果は必ず真になり、気づきにくいバグに。
しかもコンパイルでエラーにもならないので気づきにくいという点も注意が必要。

比較したいのに、うっかり = を1つだけ書いてしまう。これは私もたまにやってしまう、あるあるミスです。同値比較は必ず ==(イコール2つ)です。VSコードなどのエディタならif文内の代入に警告を出してくれるので、書き間違いにも気付きやすくなります(FAQでも触れます)。次の練習問題で手を動かせば、自然と身につきます。

型や演算子のより正確な仕様は、cppreference(C言語リファレンス)で確認できます。

例題(まずは一緒に解いてみる)

練習問題に入る前に、簡単な例題を1つ、答えを見ながら流れを確認しましょう。

例題:整数 a = 5b = 3 を宣言し、a + b の結果と、a > b(aはbより大きいか)の結果を表示してみましょう。

example.c

#include <stdio.h>

int main(void) {
    int a = 5;
    int b = 3;
    printf("%d\n", a + b);   // 8
    printf("%d\n", a > b);   // 1(真)
    return 0;
}

実行結果:81

解説:a + b はそのまま足し算で8。a > b は「5は3より大きいか?」の比較で、正しいので 真=1 が返ります(間違いなら0)。比較の結果が1か0の数値で返る、という感覚をつかめれば十分です。

流れはつかめましたか? ここからは、自分の手で解いてみましょう。

練習問題(基礎5+発展2)

ここからが本番です。基礎5問は必ず解いてください。発展2問は余力があればで大丈夫です。まず自分で書いてから、各問題の「+ 解答・解説を見る」を開いて答え合わせをしましょう。

基礎 Q1

printf を使って、次の2行を表示するプログラムを書いてください。名前は好きな文字列、年齢の 20整数の変数に入れてからフォーマット指定子で表示します。

  • 1行目:私の名前は佐藤です
  • 2行目:私の年齢は20歳です
解答・解説を見る
q1.c

#include <stdio.h>

int main(void) {
    int age = 20;
    printf("私の名前は佐藤です\n");
    printf("私の年齢は%d歳です\n", age);
    return 0;
}

実行結果:2行のメッセージが順に表示されます。

解説:文字列はそのまま printf("...") で表示できます。変数の値を文中に埋め込むときは、%d のようなフォーマット指定子を置いて、カンマの後ろに変数を渡します。行を分けるのは改行の特殊文字 \n です。\n を忘れると2つの文が1行につながってしまうので、表示が崩れたらまず改行を疑ってください。

基礎 Q2

あなたの生年月日を「年」「月」「日」の3つの変数に分けて格納し、1995年7月9日 のような形式で1行に表示してください。型は上の一覧表を見て、それぞれの値が収まる小さめの型を選んでみましょう。

解答・解説を見る
q2.c

#include <stdio.h>

int main(void) {
    short year  = 1995;  // 〜32,767 に収まる
    char  month = 7;     // 1〜12 なら char で十分
    char  day   = 9;     // 1〜31 なら char で十分
    printf("%d年%d月%d日\n", year, month, day);
    return 0;
}

実行結果:1995年7月9日

解説:年(4桁)は char(〜127)に入らないので short、月と日は char で足ります。もちろん全部 int でも動きますが、「この値はどの範囲に収まるか」を考えて型を選ぶのがこの問題の狙いです。メモリの少ないマイコンでは、この型選びがそのまま省メモリにつながります。表示はすべて %d でかまいません(char や short の整数は %d で表示できます)。

基礎 Q3

次の4つの値を、それぞれ適切な型と英語の変数名で宣言して表示してください。

項目 条件
円周率 小数(単精度) 3.14159
車速 整数(最大300km/h) 60
ガソリン残量 整数(0〜100L) 12
気温 整数(-40〜50℃) -40
解答・解説を見る
q3.c

#include <stdio.h>

int main(void) {
    float pi = 3.14159f;        // 小数(単精度)は float
    short speed = 60;           // 最大300 → char(〜127)に入らないので short
    unsigned char fuel = 12;    // 0〜100 → 符号なし char で十分
    char temperature = -40;     // -40〜50 → 符号あり char に収まる
    printf("pi=%.5f speed=%d fuel=%d temp=%d\n",
           pi, speed, fuel, temperature);
    return 0;
}

実行結果:pi=3.14159 speed=60 fuel=12 temp=-40

解説:型選びの実戦練習です。判断の流れはこうです。①小数か整数か(円周率だけ小数なので float)。②整数なら最大値と最小値がどの型に収まるか(車速は最大300なので char では足りず short)。③マイナスがあるか(気温は-40があるので符号あり、ガソリンは0以上なので unsigned が選べる)。この3ステップで、一覧表から型を選べるようになります。

基礎 Q4

int a = 7;int b = 2; のとき、①a / b、②a % b(余り)、③3.5と正しく表示される割り算、の3つを表示してください。③はキャストを使います。

解答・解説を見る
q4.c

#include <stdio.h>

int main(void) {
    int a = 7, b = 2;
    printf("%d\n", a / b);           // 3:整数どうしは切り捨て
    printf("%d\n", a % b);           // 1:余り
    printf("%.1f\n", (float)a / b);  // 3.5:キャストで小数の割り算に
    return 0;
}

実行結果:3 / 1 / 3.5

解説:ここが最初の落とし穴です。整数どうしの割り算は、小数を切り捨てます7 / 23)。余りは % で取り出せます。小数の結果がほしいときは、(float)a のようにキャスト(型の一時変換)をしてから割ります。片方が float になれば、計算全体が小数で行われます。(float)(a / b) と書くと「切り捨てた後」に変換されて 3.0 になってしまう点にも注意してください。

基礎 Q5

int x = 5; のとき、①x == 5x == 3 の結果(真偽)を表示してください。さらに②気温 temp = 2825以上 かつ 30以下なら1になる判定を、&& を使って表示してください。

解答・解説を見る
q5.c

#include <stdio.h>

int main(void) {
    int x = 5;
    printf("%d\n", x == 5);  // 1:等しい→真
    printf("%d\n", x == 3);  // 0:等しくない→偽

    int temp = 28;
    int comfortable = (temp >= 25) && (temp <= 30);
    printf("%d\n", comfortable);  // 1:25〜30の範囲内
    return 0;
}

実行結果:1 / 0 / 1

解説:== は「等しいか」を調べる比較で、結果は真=1 / 偽=0 になります。もし if (x = 3) と書くと、xに3を代入してその結果(3=真)で必ず条件が成立してしまうバグになります。②の「25以上 かつ 30以下」は temp >= 25 && temp <= 30 と書きます。&& は「両方が真なら真」。比較の結果はそのまま int の 1/0 なので、変数に入れて後で使えるのがポイントです。

発展 Q6

定数 const int MAX_SPEED = 180; を宣言して表示した後、わざと MAX_SPEED = 200; と書き換えてみて、コンパイルエラーになることを確認してください。確認できたら、その行を消して(またはコメントにして)動かします。

解答・解説を見る
q6.c

#include <stdio.h>

int main(void) {
    const int MAX_SPEED = 180;  // constは宣言時に初期化する
    // MAX_SPEED = 200;  ← この行を生かすとコンパイルエラー
    //   error: assignment of read-only variable 'MAX_SPEED'
    printf("%d\n", MAX_SPEED);
    return 0;
}

実行結果:180(書き換え行を生かすとコンパイルエラー)

解説:エラーメッセージは「読み取り専用の変数に代入した」という意味です。わざとエラーを起こして、メッセージを読む。この練習はC言語の上達にかなり役立ちます。constの定数は「変えてはいけない値」をコンパイラに守らせる仕組みなので、上限値・設定値・円周率のような値に付けておくと、未来の自分のうっかりを防げます。なお #define MAX_SPEED 180 でも同じ定数を作れます(こちらはコンパイル前の文字置き換え)。

発展 Q7

int x = 5;4倍した値を表示してください。ただし *(掛け算)ではなくシフト演算子 << を使うこと。ヒント:左に1ビットずらすと値は2倍になります。

解答・解説を見る
q7.c

#include <stdio.h>

int main(void) {
    int x = 5;
    printf("%d\n", x << 2);  // 20:左に2ビット=×2×2=4倍
    return 0;
}

実行結果:20

解説:<< は値のビット(2進数の各桁)を左にずらす演算子で、1ビット左にずらすごとに値は2倍になります。4倍なら2ビットです。C言語には四則演算のほかに、こうしたビット演算という組込みで大活躍する演算があります。「なぜずらすと2倍なのか」は、#8 ビット演算のドリルで2進数の仕組みから図解します。ここでは「そういう演算子がある」と知っていれば十分です。

この発展問題は余力がある人向けです。基礎5問ができていれば、次のテーマ(if文)に進んで大丈夫です。

例:実務で実際に書くコード

ここまでの変数・型・演算子は、組込みソフトでは以下のように使ったりします。

例:センサー値のしきい値判定

const int THRESHOLD = 300;   // しきい値は定数にしておく
int light = read_sensor();   // センサーから明るさを読む(0〜1023)

if (light < THRESHOLD) {     // 比較:暗いか?
    led_on();                // 点灯
} else {
    led_off();               // 消灯
}
  • しきい値のような「変えない値」は const定数にして、うっかり書き換えを防ぎます
  • light < THRESHOLD はセンサー値の比較。ここで = を使うとバグになるので、必ず比較演算子を使います
  • マイコンはメモリが少ないので、0〜255しか入らない値は uint8_t(符号なし1バイト)にするなど、Q2・Q3でやった「範囲で型を選ぶ」がそのまま省メモリにつながります

車載の現場では、この「範囲で型を選ぶ」がさらに厳密になります。たとえば車速やガソリン残量のような信号は、仕様書に「この信号は0〜250」と範囲が決まっていて、それに合う型を選ぶところから設計が始まります。Q3で車速や気温の型を考えたのは、より実務に近い形での練習問題を作成したいという思いで例題に加えてみました。

最後につまずきポイントをおさらい

「=」と「==」の取り違えは、エラーが出ないことがある
if (x = 5) は文法として正しいので、コンパイラがそのまま通してしまうことがあります。プログラムは動くのに結果がおかしい、という一番やっかいなバグになりがちです。動作が変なときは、「比較のつもりで = を1つだけ書いていないか」を最初に疑ってください。

もう一つ、初心者がやりがちなのが整数どうしの割り算です(基礎Q4)。5 / 22.5 ではなく 2 になります。平均値を出すときなどにズレの原因になるので、小数がほしいときはキャストを思い出してください。
※C言語では小数点は切り捨てになります。

正直に言うと、私も === の書き間違いや整数割り算の切り捨ては、たまにやって「あれ?」となります。まあたまにやらかす程度ですし、実務では実装後に検査もするので、その時になんでこの条件に入らないんだ?となった時にはこの === の書き間違いを疑いますね。
だからこそ、動きがおかしいときに、まずこの2つを疑うクセを身に着けておくとバグ解決がより早くなるかと思います。

よくある質問(FAQ)

int と float は、どう使い分ける?

整数だけなら int、小数点以下が必要なら float です。個数・回数・0/1のフラグは int、温度・電圧のような測定値は float、と考えると迷いません。

型の一覧表は暗記しないとダメ?

いいえ。「整数と小数で型が分かれる」「型ごとに入る範囲が違う」の2点が分かっていれば、あとは表に戻って調べれば十分です。何度か問題を解くうちに、よく使う int / float / char は自然に覚えます。

「= と ==」を間違えないコツは?

コード作成時はやはりVSコードなどのエディタを使うのが確実だと思います。
VSコードではif文の判定内に比較を書くと勝手に補完や警告(赤線を引く)をしてくれるので間違いに気付きやすいです。

動画:実際に = と == を書き間違えたコードをVSコードで動かした様子。警告が出て、条件が常に真になってしまうのが確認できる

次に読む

変数・型・演算子が手になじんだら、次は「条件分岐」で動作を分ける練習に進みましょう。

次のドリル(#2)C言語 if文の練習問題|else・switch・三項演算子まで固める
目次へ戻るC言語 基礎ドリル|組込みに必要な8テーマ