Cプログラマへの道 #2 - 基本中の基本 続

Last Edited: 7/16/2024

このブログ記事では、C言語の基本的な概念を続けて紹介します。

C Basics 2

Cの基本については読み飽きたと思うかもしれませんが、もう少しお付き合いください。これらの基本を注意深く学ぶことは将来にとって非常に重要です。

If文

たとえば、学生の成績に応じてコメントを与えるとしましょう。これを行うには、以下のようにif文を使用できます。

char grade = 'A';
 
if (grade == 'A') {
    printf("完璧です!");
} else if (grade == 'B') {
    printf("素晴らしい!");
} else if (grade == 'C') {
    printf("素晴らしい!");
} else if (grade == 'D') {
    printf("素晴らしい!");
} else if (grade == 'E') {
    printf("なんとか合格しました。");
} else if (grade == 'F') {
    printf("不合格です。");
} else {
    printf("無効な成績です。");
}

しかし、上記のコードは理想的ではありません。なぜなら、同じことを出力する条件が三つあるからです。これをAND論理演算子&&を使って簡略化できます。

char grade = 'C';
 
if (grade == 'A') {
    printf("完璧です!");
} else if ('B' <= grade && grade <= 'D') {
    printf("素晴らしい!");
} else if (grade == 'E') {
    printf("なんとか合格しました。");
} else if (grade == 'F') {
    printf("不合格です。");
} else {
    printf("無効な成績です。");
}

AND演算子に加えて、OR演算子||とNOT演算子!もあります。if文の中では、<>を使って大小比較を行い、==を使って等しいかどうかを確認し、!=を使って等しくないかどうかを確認できます。

Switch文

しかし、else ifがたくさんあるのは悪いコーディングプラクティスとされています。チェックする条件が多い場合、switch文を使用します。

switch (grade) {
    case 'A':
        printf("完璧です!");
        break;
    case 'B':
        printf("素晴らしい!");
        break;
    case 'C':
        printf("良いです。");
        break;
    case 'D':
        printf("まあまあです。");
        break;
    case 'E':
        printf("なんとか合格しました。");
        break;
    case 'F':
        printf("不合格です。");
        break;
    default:
        printf("無効な成績です。");
}

switch文の実行を停止するためにはbreakを使用する必要があります。そうしないと、次のケースのプログラムも実行されてしまいます。

三項演算子

以下のコードは正しいですが、毎回これを書くのは非常に面倒です。

int a = 4;
int b = 3;
 
int min;
if (a < b) {
    min = a;
} else {
    min = b;
}
 
printf("最小値: %d", min);

代わりに、三項演算子を使ってコードを以下のようにリファクタリングできます。

int a = 4;
int b = 3;
 
// 三項演算子 (条件) ? trueの場合の値 : falseの場合の値;
int min = (a < b) ? a : b; 
 
printf("最小値: %d", min);

これにより、多くのシナリオでコードを簡略化できます。

Forループ

C言語でforループを使いたい場合、以下のように行います。

// Forループ 
// for (開始インデックス; 継続条件; インデックスの増加方法) { 操作 }
for (int i = 1; i <= 10; i++) {
    printf("%d ", i);
} 
// => 1 2 3 4 5 6 7 8 9 10

デクリメントも可能です。

// Forループ, デクリメント
for (int i = 10; i >= 1; i--) {
    printf("%d ", i);
} 
// => 10 9 8 7 6 5 4 3 2 1
 
// Forループ, 2ずつデクリメント
for (int i = 10; i >= 1; i-=2) {
    printf("%d ", i);
} 
// => 10 8 6 4 2

Whileループ

条件が満たされるまでプログラムを無限に実行したい場合、whileループを使用します。

int i = 1;
 
while (i <= 10) {
    printf("%d ", i);
    i++;
} 
// => 1 2 3 4 5 6 7 8 9 10

最初に操作を実行し、その後繰り返すかどうかを決定したい状況もあります。そのような場合、do-whileループを使用します。

int age;
 
do {
    printf("年齢は?: ");
    scanf("%d", &age);
} while (age < 0);
 
printf("あなたの年齢は%d歳です", age);

まず年齢を尋ね、ユーザー入力に応じて再度尋ねるかどうかを決定します。

Break vs Continue

多くの初心者はbreakcontinueの違いに非常に混乱するようなので、ここで説明します。 breakはループを終了しますが、continueはループの1回の反復をスキップします。例を見てみましょう。

// Break
for (int i = 1; i <= 5; i++) {
    if (i == 3) {
        break;
    } else {
        printf("%d ", i);
    }
}
// => 1 2
 
// Continue
for (int i = 1; i <= 5; i++) {
    if (i == 3) {
        continue;
    } else {
        printf("%d ", i);
    }
}
// => 1 2 4 5

上記の例では、breakが使用された場合、出力は1 2です。なぜなら、i==3の時breakがループを終了したからです。 一方、continueが使用された場合、出力は1 2 4 5です。なぜなら、continueが3をスキップしたからです。 breakcontinueは、ネストされたループでも同じレベルのループにのみ作用します。

// Break (i == jの場合、内側のループを終了)
for (int i = 1; i <= 5; i++) {
    for (int j = 1; j <= 5; j++) {
        if (i == j) {
            break;
        } else {
            printf("%d ", j);
        }
    }
    printf("\n");
}
// => 1
//    1 2
//    1 2 3
//    1 2 3 4
 
// Continue (i == jの場合、スキップして内側のループを継続)
for (int i = 1; i <= 5; i++) {
    for (int j = 1; j <= 5; j++) {
        if (i == j) {
            continue;
        } else {
            printf("%d ", j);
        }
    }
    printf("\n");
}
// => 2 3 4 5
//    1 3 4 5
//    1 2 4 5
//    1 2 3 5
//    1 2 3 4

配列

文字列(文字の配列)の初期化方法は見たことがありますが、他のデータ型ではどのように行うのでしょうか?以下のように行います。

int grades[] = {1,2,3,4,5};

配列の要素にアクセスするには、以下のようにします。

grades[0]; // => 1
grades[1]; // => 2
grades[2]; // => 3

配列を反復処理したい場合、sizeofを使って要素の数を計算できます。

// 要素の数を取得
// 配列全体のサイズを最初の要素のサイズで割ります
// 配列のデータ型は一貫しているため、これが可能です
 
int size = sizeof(grades)/sizeof(grades[0]);
 
for (int i = 0; i < size; i++) {
    printf("成績: %d\n", grades[i]);
}

配列は0インデックスである(0から数え始める)ため、<=の代わりに<を使用する必要があります。

関数

全てのコードをmain関数に含めるのは、コードが長くなり、重複が起こりやすいので悪い考えです。 コードを簡略化するために、以下のようにmainの前に他の関数を定義できます。

// 関数  出力タイプ 関数名 (入力) { 操作 }
void happyBirthday (char name[], int age) {
    printf("誕生日おめでとう %s!\n", name);
    printf("あなたは%d歳になりました!\n", age)
}
 
int square (int x) {
    return x*x;
}
 
int main () {
    char name[] = "マイケル・ジョーダン";
    int age = 24;
 
    happyBirthday(name, age);
 
    int ageSquared = square(age);
    printf("あなたの年齢の二乗は%dです",ageSquared);
 
    return 0;
}

可読性のためにmainの後に他の関数を定義し、関数を呼び出す前に関数が定義されていない場合、 多くのコンパイラはパラメータの一致をチェックしないため、予期しない動作が発生する可能性があります。そのため、コンパイラが正しく パラメータの一致をチェックできるように、最初に関数プロトタイプを追加します。

// 関数プロトタイプ
void happyBirthday (char[], int);
int square (int);
 
int main () {
    char name[] = "マイケル・ジョーダン";
    int age = 24;
 
    happyBirthday(name);
 
    int ageSquared = square(age);
    printf("あなたの年齢の二乗は%dです",ageSquared);
 
    return 0;
}
 
void happyBirthday (char name[], int age) {
    printf("誕生日おめでとう、%s!\n", name);
    printf("あなたは%d歳になりました!\n", age)
}
 
int square (int x) {
    return x*x;
}

クイズ

この記事では、学習した内容を確認するためのクイズを設けます。記事のメイン部分を読んだ後に、ぜひ自分で問題を解いてみることを強くお勧めします。各問題をクリックすると答えが表示されます。

リソース