From 7c16490ff61cc6c71edc2a61a07bf35ef998db99 Mon Sep 17 00:00:00 2001 From: Yukito-Aomi Date: Thu, 13 Mar 2025 04:42:46 +0900 Subject: [PATCH 01/10] add: Files --- src/content/docs/textbook/c-lang/beginner/16--array.mdx | 4 ++++ .../docs/textbook/c-lang/beginner/17--character-string.mdx | 4 ++++ .../textbook/c-lang/beginner/18--multidimensional-array.mdx | 4 ++++ .../textbook/c-lang/beginner/19--user-defined-function.mdx | 4 ++++ .../textbook/c-lang/beginner/20--recursive-functio-call.mdx | 4 ++++ src/content/docs/textbook/c-lang/beginner/21--tic-tac-toe.mdx | 4 ++++ 6 files changed, 24 insertions(+) create mode 100644 src/content/docs/textbook/c-lang/beginner/16--array.mdx create mode 100644 src/content/docs/textbook/c-lang/beginner/17--character-string.mdx create mode 100644 src/content/docs/textbook/c-lang/beginner/18--multidimensional-array.mdx create mode 100644 src/content/docs/textbook/c-lang/beginner/19--user-defined-function.mdx create mode 100644 src/content/docs/textbook/c-lang/beginner/20--recursive-functio-call.mdx create mode 100644 src/content/docs/textbook/c-lang/beginner/21--tic-tac-toe.mdx diff --git a/src/content/docs/textbook/c-lang/beginner/16--array.mdx b/src/content/docs/textbook/c-lang/beginner/16--array.mdx new file mode 100644 index 0000000..0b24bff --- /dev/null +++ b/src/content/docs/textbook/c-lang/beginner/16--array.mdx @@ -0,0 +1,4 @@ +--- +title : 配列 +slug: http://localhost:4321/textbook/c-lang/beginner/array +--- diff --git a/src/content/docs/textbook/c-lang/beginner/17--character-string.mdx b/src/content/docs/textbook/c-lang/beginner/17--character-string.mdx new file mode 100644 index 0000000..8e927e3 --- /dev/null +++ b/src/content/docs/textbook/c-lang/beginner/17--character-string.mdx @@ -0,0 +1,4 @@ +--- +title : 文字と文字列 +slug: http://localhost:4321/textbook/c-lang/beginner/character-string +--- diff --git a/src/content/docs/textbook/c-lang/beginner/18--multidimensional-array.mdx b/src/content/docs/textbook/c-lang/beginner/18--multidimensional-array.mdx new file mode 100644 index 0000000..4bdc7e8 --- /dev/null +++ b/src/content/docs/textbook/c-lang/beginner/18--multidimensional-array.mdx @@ -0,0 +1,4 @@ +--- +title : 多次元配列 +slug: http://localhost:4321/textbook/c-lang/beginner/multidimensional-array +--- diff --git a/src/content/docs/textbook/c-lang/beginner/19--user-defined-function.mdx b/src/content/docs/textbook/c-lang/beginner/19--user-defined-function.mdx new file mode 100644 index 0000000..fab7b98 --- /dev/null +++ b/src/content/docs/textbook/c-lang/beginner/19--user-defined-function.mdx @@ -0,0 +1,4 @@ +--- +title : 関数の設計 +slug: http://localhost:4321/textbook/c-lang/beginner/user-defined-function +--- diff --git a/src/content/docs/textbook/c-lang/beginner/20--recursive-functio-call.mdx b/src/content/docs/textbook/c-lang/beginner/20--recursive-functio-call.mdx new file mode 100644 index 0000000..cad00f9 --- /dev/null +++ b/src/content/docs/textbook/c-lang/beginner/20--recursive-functio-call.mdx @@ -0,0 +1,4 @@ +--- +title : 関数の再帰呼出し +slug: http://localhost:4321/textbook/c-lang/beginner/recursive-functio-call +--- diff --git a/src/content/docs/textbook/c-lang/beginner/21--tic-tac-toe.mdx b/src/content/docs/textbook/c-lang/beginner/21--tic-tac-toe.mdx new file mode 100644 index 0000000..e8a80fb --- /dev/null +++ b/src/content/docs/textbook/c-lang/beginner/21--tic-tac-toe.mdx @@ -0,0 +1,4 @@ +--- +title : 「まるばつゲーム」の作成 +slug: http://localhost:4321/textbook/c-lang/beginner/tic-tac-toe +--- From 98f4988e899e36305041bc5430b3745c59c81382 Mon Sep 17 00:00:00 2001 From: Yukito-Aomi Date: Thu, 20 Mar 2025 15:43:05 +0900 Subject: [PATCH 02/10] edit: 16-21 --- .../textbook/c-lang/beginner/16--array.mdx | 384 +++++++++++++ .../c-lang/beginner/17--character-string.mdx | 185 ++++++ .../beginner/18--multidimensional-array.mdx | 213 +++++++ .../beginner/19--function-definition.mdx | 303 ++++++++++ .../beginner/19--user-defined-function.mdx | 4 - .../beginner/20--recursive-functio-call.mdx | 168 +++++- .../c-lang/beginner/21--tic-tac-toe.mdx | 532 ++++++++++++++++++ 7 files changed, 1784 insertions(+), 5 deletions(-) create mode 100644 src/content/docs/textbook/c-lang/beginner/19--function-definition.mdx delete mode 100644 src/content/docs/textbook/c-lang/beginner/19--user-defined-function.mdx diff --git a/src/content/docs/textbook/c-lang/beginner/16--array.mdx b/src/content/docs/textbook/c-lang/beginner/16--array.mdx index 0b24bff..39eb0be 100644 --- a/src/content/docs/textbook/c-lang/beginner/16--array.mdx +++ b/src/content/docs/textbook/c-lang/beginner/16--array.mdx @@ -2,3 +2,387 @@ title : 配列 slug: http://localhost:4321/textbook/c-lang/beginner/array --- + +import { Aside } from '@astrojs/starlight/components'; +import { Tabs, TabItem } from '@astrojs/starlight/components'; + +同じ型の変数の集まりは、ひとまとめにして表現することができます。 +そのために利用する**配列**の基礎について学んでいきましょう。 + +## 配列とは + +まずは、3人の学生の点数について、その合計値と平均値を算出するプログラムを作成してみましょう。 + + + +```c +#include + +int main(int argc, const char * argv[]) { + int score1 = 70; // 1 人目の点数 + int score2 = 100; // 2 人目の点数 + int score3 = 85; // 3 人目の点数 + + int sum = 0; // 合計点(初期値:0) + + printf("1人目の点数: %3d\n", score1); sum += score1; + printf("2人目の点数: %3d\n", score2); sum += score2; + printf("3人目の点数: %3d\n", score3); sum += score3; + + printf("合計点: %d\n", sum); + printf("平均点: %.1f\n", (double)sum / 3); + + return 0; +} +``` + + +``` +1人目の点数: 70 +2人目の点数: 100 +3人目の点数: 85 +合計点: 255 +平均点: 85.0 +``` + + + +このプログラムでは、3人の点数がそれぞれ`int`型の3つの変数(`score1`・`score2`・`score3`)として表されています。 + +さて、今後プログラムを組んでいくうえで、学生の人数が100人に増えた場合を考えましょう。 +このプログラムのようなやり方では、点数を格納するための変数を100つも用意することになるでしょう。 +100もの変数を管理する必要がありますし、なにより変数の定義が大変に思われるかもしれません。 + +そこで利用するのが**配列**(array)になります。 +**配列**は、同じ型の値の集合を1つの変数として表現することができます。 +また、配列に格納されるひとつひとつの値を**要素**(element)と呼びます。 + + + +## 配列の宣言 + +まずは配列を宣言していきましょう。 +配列の宣言では、次のように、**要素型(element type)**、**配列名**(変数名)、**要素数**を与えることで実現します。 + +```c +要素型 配列名[要素数]; +``` + +例として、要素型が`int`型で要素数が`5`の配列を宣言してみます。 + +```c +int a[5]; +``` + +これによって、`int`型の5つの値を格納することができる1つの変数を、配列名`a`として宣言することができました。 + + + +## 要素と添字 + +配列が持つ個々の要素への**アクセス**は、**添字演算子**(subscript operator)を用いることで実現します。 +扱う配列の配列名が`a`である場合、添字演算子は`a[b]`として表されます。 +これは、配列`a`の先頭から`b`個後ろの要素を意味します。 + +また、演算子`[]`内のオペランドは、**添字**(subscript)と呼ばれます。 +これは、“先頭要素から何個後ろの要素なのか”を表す整数値になります。 +そのため、要素数が`n`である配列の添字の範囲が、`0`から`n - 1`までの整数値となることに注意しましょう。 + +実際に、冒頭のプログラムを、配列を用いて表現してみましょう。 + + + +```c +#include + +int main(int argc, const char * argv[]) { + // 配列の宣言 + int scores[3]; // int型の 3 つの値を格納する配列 + + // 要素へのアクセス + scores[0] = 70; // 1 人目の点数 + scores[1] = 100; // 2 人目の点数 + scores[2] = 85; // 3 人目の点数 + + int sum = 0; // 合計点(初期値:0) + + printf("1人目の点数: %3d\n", scores[0]); sum += scores[0]; + printf("2人目の点数: %3d\n", scores[1]); sum += scores[1]; + printf("3人目の点数: %3d\n", scores[2]); sum += scores[2]; + + printf("合計点: %d\n", sum); + printf("平均点: %.1f\n", (double)sum / 3); + + return 0; +} +``` + + +``` +1人目の点数: 70 +2人目の点数: 100 +3人目の点数: 85 +合計点: 255 +平均点: 85.0 +``` + + + + + +## 配列の初期化 + +配列の各要素の値の設定を、代入ではなく初期化によって実現してみましょう。 +配列の初期化子は、各要素に対する初期化子をコンマ(`,`)で区切って順に並べたものを`{}`で囲んだ形式で与えます。 + +まずは、冒頭のプログラムを、配列の初期化を用いて表現してみましょう。 + + + +```c +#include + +int main(int argc, const char * argv[]) { + int scores[3] = {70, 100, 85}; // 配列の初期化 + + int sum = 0; // 合計点(初期値:0) + + printf("1人目の点数: %3d\n", scores[0]); sum += scores[0]; + printf("2人目の点数: %3d\n", scores[1]); sum += scores[1]; + printf("3人目の点数: %3d\n", scores[2]); sum += scores[2]; + + printf("合計点: %d\n", sum); + printf("平均点: %.1f\n", (double)sum / 3); + + return 0; +} +``` + + +``` +1人目の点数: 70 +2人目の点数: 100 +3人目の点数: 85 +合計点: 255 +平均点: 85.0 +``` + + + +配列の宣言と各要素への代入を1行で表現することができました。 + +また、配列に与える初期化子の規則を確認していきましょう。 + +配列に与える初期化子内の、最後の初期化子の後ろのコンマ(`,`)は省略可能です。 +最後の初期化子の後ろにコンマを記述する形式には、初期化の追加や削除にともなって、コンマを記述したり削除したりしなくてよくなるというメリットがあります。 + +```c +int a[3] = { + 1, + 2, + 3, // 最後の初期化子の後ろのコンマ(,)は省略可能 +}; +/** + * a[0]: 1 + * a[1]: 2 + * a[2]: 3 + */ + ``` + +次の配列`b`のような、要素数を指定しない場合は、`{}`内の初期化子の個数に基づいて、配列の要素数が自動的に決定されます。 + +```c +int b[] = {1, 2, 3}; // 要素数は自動的に 3 になる +/** + * b[0]: 1 + * b[1]: 2 + * b[2]: 3 + */ +``` + +次の配列`c`を確認すると、`{}`内に初期化子が与えられていない要素は、`0`で初期化されることがわかります。 + +```c +int c[3] = {1, 2}; // int c[] = {1, 2, 0}; と同じ +/** + * c[0]: 1 + * c[1]: 2 + * c[2]: 0 + */ +``` + +次の配列`d`の場合では、`d[0]`が`0`で初期化され、つづく`d[1]`、`d[2]`は初期化子が与えられないため`0`で初期化されています。 + +```c +int d[3] = {0}; // 全要素を 0 で初期化 +/** + * d[0]: 0 + * d[1]: 0 + * d[2]: 0 + */ +``` + + + +## 配列の走査 + +**走査**(traverse)とは、配列の要素をひとつずつ順番になぞっていくことを意味します。 +`for`文を用いた配列の走査について確認しましょう。 + +配列を先頭から順に走査するには、`for`文のカウンタ用変数を、配列の添字として利用することで実現できます。 +冒頭のプログラムを、配列と`for`文を用いて表現してみましょう。 + + + +```c +#include + +int main(int argc, const char * argv[]) { + int scores[3] = {70, 100, 85}; // 配列の初期化 + + int sum = 0; // 合計点(初期値:0) + + // 配列の走査 + for (int i = 0; i < 3; i++) { + printf("%d人目の点数: %3d\n", i + 1, scores[i]); + sum += scores[i]; + } + + printf("合計点: %d\n", sum); + printf("平均点: %.1f\n", (double)sum / 3); + + return 0; +} +``` + + +``` +1人目の点数: 70 +2人目の点数: 100 +3人目の点数: 85 +合計点: 255 +平均点: 85.0 +``` + + + +`for`文を用いることで、添字のみが異なるような命令を、一括で記述することができました。 +命令や繰返し処理を工夫することで、さまざまなプログラムに応用することができます。 + +さて、実際に練習問題を解きながら、配列を学んでいきましょう。 + +## 練習問題 + + + +キーボードからの入力により、5人の身長(`double`型)を配列`heights`に格納してください。 +また、閾値`th = 170.0`を超える値の一覧を出力してください。 +また、最大値と最小値を求めて出力してください。 + + +``` +heights[0]? 170.0(注:標準入力) +heights[1]? 168.2(注:標準入力) +heights[2]? 186.9(注:標準入力) +heights[3]? 152.0(注:標準入力) +heights[4]? 170.5(注:標準入力) +--------------- +# heights>170.0 +heights[2]: 186.9 +heights[4]: 170.5 +--------------- +max: 186.9 +min: 152.0 +``` + + +配列の要素への標準入力は、変数の標準入力と同様のやり方で実現できます。 +繰返し処理を活用することで、すべての要素へアクセスしてみましょう。 + +```c +scanf("%lf", &heights[i]); +``` + + +繰返し処理のなかで、条件に合致する要素のみを標準出力してみましょう。 +標準入力用の繰返し構文とは別の繰返し構文を用意する必要があります。 + +```c +// 配列の要素が閾値を超える場合 +if (heights[i] > th) printf("heights[%d]: %.1f\n", i, heights[i]); +``` + + +最大値を求めるには、次のようなやり方があります。 + +- 配列内の適当な要素を持つ変数`max`を用意する +- 配列を走査するなかで、`max`を超える値がみつかった場合、その値を`max`に代入することで更新する + +```c +// 適当な要素の値を設定しておく +double max = heights[0]; + +for (int i = 0; i < 5; i++) { + // 最大値の更新 + if (max < heights[i]) max = heights[i]; +} +``` + + +```c +#include + +int main(int argc, const char * argv[]) { + double heights[5]; + double th = 170.0; + + // 整数値の入力 + for (int i = 0; i < 5; i++) { + printf("heights[%d]? ", i); + scanf("%lf", &heights[i]); + } + + // 最大値および最小値の宣言(適当な要素の値で初期化しておく) + double max, min; + max = min = heights[0]; + + puts("---------------"); + + printf("# heights>%.1f\n", th); + + for (int i = 0; i < 5; i++) { + // 閾値を超える身長の出力 + if (heights[i] > th) printf("heights[%d]: %.1f\n", i, heights[i]); + + // 最大値および最小値の更新 + if (max < heights[i]) max = heights[i]; + if (min > heights[i]) min = heights[i]; + } + + puts("---------------"); + + printf("max: %.1f\n", max); + printf("min: %.1f\n", min); + + return 0; +} +``` + + + +## 確認クイズ + + diff --git a/src/content/docs/textbook/c-lang/beginner/17--character-string.mdx b/src/content/docs/textbook/c-lang/beginner/17--character-string.mdx index 8e927e3..56766a9 100644 --- a/src/content/docs/textbook/c-lang/beginner/17--character-string.mdx +++ b/src/content/docs/textbook/c-lang/beginner/17--character-string.mdx @@ -2,3 +2,188 @@ title : 文字と文字列 slug: http://localhost:4321/textbook/c-lang/beginner/character-string --- + +import { Aside } from '@astrojs/starlight/components'; +import { Tabs, TabItem } from '@astrojs/starlight/components'; + +一連の文字の並びを表すものを文字列と呼びます。 +そんな文字列の基本を学んでいきましょう。 + +## 文字列リテラル + +`"hoge"`のような、文字の並びを二重引用符`"`で囲んだものを**文字列リテラル**(string literal)と呼びます。 + +文字列リテラルの末尾には、**ヌル文字**(null character)という値`0`(8進拡張表記では`\0`)の文字が付いています。 +そのため、`"hoge"`のような見かけ上4文字の文字列リテラルは、ヌル文字を含めた5文字分の記憶域を占有していることに注意しましょう。 +また、見かけ上0文字の文字列リテラル`""`も同様に、ヌル文字を含めた1文字分の記憶域を占有しています。 + +## 文字列 + +**文字列**(string)とは、複数の文字を並べたデータのようなものです。 + +文字列をオブジェクトとして格納する場合、要素型が`char`である配列を利用します。 + +```c +// 配列の宣言 +char str[6]; +``` +```c +// 初期化をともなう配列の宣言 +char str[] = "abcde"; // {'a', 'b', 'c', 'd', 'e', '\0'} と同じ +``` + + + +## 文字列の入出力 + +文字列の出力(印字)は次のようにして実現します。 + + + +```c +#include + +int main(int argc, const char * argv[]) { + // "hoge" + char str[] = {'h', 'o', 'g', 'e', '\0'}; + + // 文字列の出力(%s) + printf("str: %s\n", str); + // 文字の出力(%c) + printf("str[0]: %c\n", str[0]); + // putchar を用いた文字の出力 + putchar(str[1]); + + putchar('\n'); // 改行文字 + + return 0; +} +``` + + +``` +str: hoge +str[0]: h +o +``` + + + +また、文字列の入力は次のようにして実現します。 + +```c +char str[128]; + +// 文字列の入力(%s) +scanf("%s", str); +``` +```c +// 文字の入力(%c) +scanf("%c", &str[0]); +``` + +## 文字列の代入 + +代入演算子`=`による文字列の代入ができないことに注意しましょう。 + +```c +char str[6]; +str = "abcde"; // エラー +``` + +面倒ではありますが、1文字ごとに代入するようにしましょう。 +なお、1文字を扱う場合は、文字を一重引用符`'`で囲みます。 + +```c +char str[6]; +str[0] = 'a'; +str[1] = 'b'; +str[2] = 'c'; +str[3] = 'd'; +str[4] = 'e'; +str[5] = '\0'; +``` + + + +文字列リテラル`"the quick brown fox jumps over the lazy dog"`について、全体の文字数`len`および空白(`" "`)を除いた文字数`cnt`をカウントして求めてください。 +また、`for`文を用いて、文字列リテラル内の文字を逆順に印字してください。 + +```c +char str[] = "the quick brown fox jumps over the lazy dog"; + +int len = 0; +int cnt = 0; +``` + + +``` +len: 43 +cnt: 35 +god yzal eht revo spmuj xof nworb kciuq eht +``` + + + +文字列の長さがわからない場合は、`for`文を用いて`'\0'`が現れるまで繰返し処理してみましょう。 +```c +for (int i = 0; str[i] != '\0'; i++) { + len++; + + // TODO +} +``` + + +配列を逆方向に走査することで、文字を逆順に出力することができます。 +`for`文のカウンタはいくつから始まるでしょうか。 +`len`を用いるやり方を考えてみましょう。 + +```c +for (int i = ******; ******; i--) { + putchar(str[i]); +} +``` + + +```c +#include + +int main(int argc, const char * argv[]) { + char str[] = "the quick brown fox jumps over the lazy dog"; + + // 文字数 + int len = 0; + int cnt = 0; + + // ヌル文字になるまで文字列リテラルを走査 + for (int i = 0; str[i] != '\0'; i++) { + len++; + if (str[i] != ' ') { + // 空白ではない場合 + cnt++; + } + } + + printf("len: %d\n", len); + printf("cnt: %d\n", cnt); + + // 逆順に印字 + for (int i = len - 1; i >= 0; i--) { + putchar(str[i]); + } + + putchar('\n'); // 改行 + + return 0; +} +``` + + + +## 確認クイズ + + diff --git a/src/content/docs/textbook/c-lang/beginner/18--multidimensional-array.mdx b/src/content/docs/textbook/c-lang/beginner/18--multidimensional-array.mdx index 4bdc7e8..71e1e55 100644 --- a/src/content/docs/textbook/c-lang/beginner/18--multidimensional-array.mdx +++ b/src/content/docs/textbook/c-lang/beginner/18--multidimensional-array.mdx @@ -2,3 +2,216 @@ title : 多次元配列 slug: http://localhost:4321/textbook/c-lang/beginner/multidimensional-array --- + +import { Aside } from '@astrojs/starlight/components'; +import { Tabs, TabItem } from '@astrojs/starlight/components'; + +`int`や`double`などの単一型の変数を集めたものを配列といいました。 +実は、その配列を集めることで『配列の配列』をつくることができます。 +実際に、『配列の配列』について学んでいきましょう。 + +## 多次元配列 + +配列の考え方を応用することで、配列の要素自体が《配列》である配列をつくることができます。 + +配列を要素型とする配列を**2次元配列**と呼び、この2次元配列を要素型とする配列を**3次元配列**と呼びます。 +2次元以上の配列を総称したものが**多次元配列**(multidimensional array)となります。 + +## 2次元配列 + +例として、3つの要素をもつ2つの配列を考えてみましょう。 +1次元配列の宣言は、次のようにして実現することができました。 + +```c +int a[3] = {1, 2, 3}; +int b[3] = {4, 5, 6}; +``` + +これら2つの配列を要素にもつ配列(二次元配列)を宣言する場合、次のようになります。 + +```c +int c[2][3] = { + {1, 2, 3}, + {4, 5, 6} +}; +``` + + + +## 2次元配列とその走査 + +多次元配列の走査では、多重ループを用いて実現します。 + +`for`文による二重ループを用いた、二次元配列の走査を確認してみましょう。 +次のプログラムは、二次元配列によって九九の表を再現したものになります。 + + + +```c +#include + +#define ROWS 9 // 行数 +#define COLS 9 // 列数 + +int main(int argc, const char * argv[]) { + int table[9][9]; + + // 二次元配列の初期化 + for (int i = 0; i < ROWS; i++) { + for (int j = 0; j < COLS; j++) { + table[i][j] = (i + 1) * (j + 1); + } + } + + // 九九表の出力 + for (int i = 0; i < ROWS; i++) { + for (int j = 0; j < COLS; j++) { + printf("%3d", table[i][j]); + } + putchar('\n'); // 改行 + } + + return 0; +} +``` + + +``` + 1 2 3 4 5 6 7 8 9 + 2 4 6 8 10 12 14 16 18 + 3 6 9 12 15 18 21 24 27 + 4 8 12 16 20 24 28 32 36 + 5 10 15 20 25 30 35 40 45 + 6 12 18 24 30 36 42 48 54 + 7 14 21 28 35 42 49 56 63 + 8 16 24 32 40 48 56 64 72 + 9 18 27 36 45 54 63 72 81 +``` + + + +プログラムの冒頭に、`#define`から始まる命令文が記述されています。 +これは、**オブジェクト形式マクロ**(object-like macro)を定義するための特殊な宣言であり、**`#define`指令**(#define directive)と呼びます。 + +## オブジェクト形式マクロ + +```c +#define A b // この指令以降の A を b とせよ +``` + +この指令文以降に記述された`A`は、`b`に置換された上でプログラムが翻訳・実行されることになります。 +また、**マクロ名**(macro name)`A`は、一般的な変数と区別するために、大文字で定義する慣習があります。 + +上記の九九表を出力するプログラムでは、行数`ROWS`および列数`COLS`に対して、`9`という数値が割り当てられています。 +この値を変更することで、出力される九九表のサイズを変えることができます。 + +プログラム中に直接記述された数値を**マジックナンバー**と呼びます(何を表す数値なのかが不明なため)。 +オブジェクト形式マクロを活用することで、値の管理を1文に集約させたり、マジックナンバーを減らしたりすることに役立ちます。 + +オブジェクト形式マクロを活用することで、プログラムの品質を向上させましょう。 + + + +## 練習問題 + + + +次の表は、3人の学生のテストの点数をまとめたものになります。 +この表の値を二次元配列`scores`で初期化し、再現してください。 +また、ひとりひとりの5教科におけるテストの平均点を格納する配列`avg`を用いることで、平均点数を算出して、結果を出力してください。 + +| 学籍番号 | 国語 | 社会 | 算数 | 理科 | 英語 | +| -------- | ---- | ---- | ---- | ---- | ---- | +| 0001 | 58 | 46 | 100 | 89 | 64 | +| 0002 | 98 | 96 | 32 | 38 | 72 | +| 0003 | 34 | 62 | 28 | 88 | 25 | + + +``` +0001: 71.4 +0002: 67.2 +0003: 47.4 +``` + + +表の値を用いることで、次のようにな二次元配列を作成しましょう。 + +```c +int scores[3][5] = { + {58, 46, 100, 89, 64}, // 学籍番号 0001 + {98, 96, 32, 38, 72}, // 学籍番号 0002 + {34, 62, 28, 88, 25} // 学籍番号 0003 +}; +``` + + +平均値は、次のような二次元配列の走査によって算出します。 +配列`avg`の要素型が`double`型であることに注意しましょう。 + +```c +double avg[3] = {0}; + +// 平均値の算出 +for (int i = 0; i < 3; i++) { + for (int j = 0; j < 5; j++) { + avg[***] += scores[***][***]; // 合計点数を算出 + } + avg[***] /= 5; // 平均点数を算出 +} +``` + + +```c +#include + +int main(int argc, const char * argv[]) { + int scores[3][5] = { + {58, 46, 100, 89, 64}, // 学籍番号 0001 + {98, 96, 32, 38, 72}, // 学籍番号 0002 + {34, 62, 28, 88, 25} // 学籍番号 0003 + }; + + double avg[3] = {0}; + + // 平均値の算出 + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 5; j++) { + avg[i] += scores[i][j]; // 合計点数を算出 + } + avg[i] /= 5; // 平均点数を算出 + } + + // 結果の出力 + for (int i = 0; i < 3; i++) { + printf("%04d: %.1f\n", i + 1, avg[i]); + } + + return 0; +} +``` + + + +## 確認クイズ + + diff --git a/src/content/docs/textbook/c-lang/beginner/19--function-definition.mdx b/src/content/docs/textbook/c-lang/beginner/19--function-definition.mdx new file mode 100644 index 0000000..c17664a --- /dev/null +++ b/src/content/docs/textbook/c-lang/beginner/19--function-definition.mdx @@ -0,0 +1,303 @@ +--- +title : 自作関数の設計 +slug: http://localhost:4321/textbook/c-lang/beginner/function-definition +--- + +import { Aside } from '@astrojs/starlight/components'; +import { Tabs, TabItem } from '@astrojs/starlight/components'; + +プログラムは、多くの機能の組合わせによって構成されます。 +ひとつひとつの機能を提供する基本単位が関数になります。 +自作関数の設計について学んでいきましょう。 + +## 関数定義と関数呼出し + +**関数**(function)とは、値を引数として受け取り、何らかの処理を行う命令のことを指します。 +特に、今まで扱ってきた`printf`関数や`puts`関数、`scanf`関数などは、C言語によって標準で提供されており、これを**ライブラリ関数**(library function)と呼びます。 + +実は、関数は自作することができます。 +まずは、関数定義について確認していきましょう。 + +関数定義は次のようにして実現することができます。 + +```c +返却値型 関数名(仮引数型並び) { + 処理の内容 +} +``` + +例として、2つの整数値を受け取り、最大値を返す関数を作成してみましょう。 + + + +```c +#include + +int max2(int n1, int n2) { + if (n1 > n2) + return n1; + else + return n2; +} + +int main(int argc, const char * argv[]) { + int a = 3; + int b = 7; + + printf("max: %d\n", max2(a, b)); + + return 0; +} +``` + + +``` +max: 7 +``` + + + +`main`関数上に、`max2`関数を定義しました。 + +`main`関数内にて、実引数`a, b`を与えて`max2`関数を呼び出しています。 +次に、仮引数`n1, n2`には実引数の値が代入され、自作関数内の処理へと進みます。 +最後に、関数呼出しの式`max2(a, b)`は、2値の比較によって返却された値に置換され、翻訳・実行されます。 + +また、`return`文は関数の実行を終了して、プログラムの流れを呼出し元に戻すとともに値を返却する仕組みであることを覚えておきましょう。 + + + +## 自作関数内での関数呼出し + +関数を組み合わせることで、プログラムを少ない記述量で実現することができます。 + +例として、次の4つの整数値の最大値を求めるプログラムをみてみましょう。 + + + +```c +#include + +int max2(int n1, int n2) { + if (n1 > n2) + return n1; + else + return n2; +} + +int max4(int n1, int n2, int n3, int n4) { + return max2(max2(n1, n2), max2(n3, n4)); +} + +int main(int argc, const char * argv[]) { + int a = 3; + int b = 7; + int c = 2; + int d = 5; + + printf("max: %d\n", max4(a, b, c, d)); + + return 0; +} +``` + + +``` +max: 7 +``` + + + +プログラムの処理の流れは、次のとおりです。 + +1. `main`関数から処理が始まり、変数`a, b, c, d`を定義 +2. `max4(a, b, c, d)`を呼び出す +3. `max2(n1, n2)`と`max2(n3, n4)`を呼び出す +4. `max2(n1, n2)`と`max2(n3, n4)`の戻り値を実引数として、`max2(max2(n1, n2), max2(n3, n4))`が呼び出される +5. 4値の最大値を戻り値として、`max4(a, b, c, d)`に置換され、結果を出力する + +プログラムの処理の流れと戻り値に注意して、関数を活用していきましょう。 + +## 値を返さない関数・引数を受け取らない関数 + +値を返さない関数を定義する場合、関数型を`void`(『空の』を意味します)とします。 + +また、受け取る仮引数がない関数を定義する場合も、仮引数型並びを`void`とします。 + +実際に、次のプログラムを確認して、`void`の使い方を確認しましょう。 + + + +```c +#include + +// 自然数を標準入力によって受け取り返却する関数 +int scanN(void) { + int n; + + do { + printf("natural number? "); + scanf("%d", &n); + + if (n <= 0) + puts("error!"); + } while (n <= 0); + + return n; +} + +// 与えた整数値だけ * を出力する関数 +void putStars(int n) { + for (int i = 0; i < n; i++) + putchar('*'); + putchar('\n'); // 改行 +} + +int main(int argc, const char * argv[]) { + putStars(scanN()); + + return 0; +} +``` + + +``` +natural number? 0(注:標準入力) +error! +natural number? -3(注:標準入力) +error! +natural number? 5(注:標準入力) +***** +``` + + + +`printStars`関数は、与えた整数値だけ`*`を出力する関数になります。 +そのため、戻り値は存在しません。 + +`scanN`関数は、主に自然数を標準入力によって受け取り返却する関数になります。 +そのため、仮引数を必要としません。 + +上記のような関数を設計する場合、`void`を記述することを忘れないようにしましょう。 + +## 練習問題 + + + +自然数`n`を受け取り、その階乗`n!`を算出する関数`factorial`を作成してください。 +また、自然数の受け取りは、前述の`scanN`関数を利用してください。 + +なお、階乗`n!`とは、`1`から`n`までの自然数の積のことを指します。 + +```c +#include + +int scanN(void) { + int n; + + do { + printf("natural number? "); + scanf("%d", &n); + + if (n <= 0) + puts("error!"); + } while (n <= 0); + + return n; +} + +*** factorial(*** ***) { + // TODO + + return ***; +} + +int main(int argc, const char * argv[]) { + int n = scanN(); + + printf("%d! = %d\n", n, factorial(n)); + + return 0; +} +``` + + +``` +natural number? 4(注:標準入力) +4! = 24 +``` + + +返却値型を`int`、仮引数を`int n`として、関数`factorial`を設計しましょう。 + +```c +int factorial(int n) { + int ans = ***; + + // TODO + + return ans; +} +``` + + +`n!`を、繰返し処理によって実現しましょう。 +返却値`ans`の初期値として、`1`を与えることに注意しましょう。 + +```c +int ans = 1; // 初期値を 1 とする + +// すべての自然数の積を算出する +for (int i = 1; i <= n; i++) + ans *= i; +``` + + +```c +#include + +// 自然数を標準入力によって受け取り返却する関数 +int scanN(void) { + int n; + + do { + printf("natural number? "); + scanf("%d", &n); + + if (n <= 0) + puts("error!"); + } while (n <= 0); + + return n; +} + +// 与えられた整数値の階乗を求める関数 +int factorial(int n) { + int ans = 1; // 初期値を 1 とする + + for (int i = 1; i <= n; i++) + ans *= i; + + return ans; +} + +int main(int argc, const char * argv[]) { + int n = scanN(); + + printf("%d! = %d\n", n, factorial(n)); + + return 0; +} +``` + + + +## 確認クイズ + + diff --git a/src/content/docs/textbook/c-lang/beginner/19--user-defined-function.mdx b/src/content/docs/textbook/c-lang/beginner/19--user-defined-function.mdx deleted file mode 100644 index fab7b98..0000000 --- a/src/content/docs/textbook/c-lang/beginner/19--user-defined-function.mdx +++ /dev/null @@ -1,4 +0,0 @@ ---- -title : 関数の設計 -slug: http://localhost:4321/textbook/c-lang/beginner/user-defined-function ---- diff --git a/src/content/docs/textbook/c-lang/beginner/20--recursive-functio-call.mdx b/src/content/docs/textbook/c-lang/beginner/20--recursive-functio-call.mdx index cad00f9..d966dc5 100644 --- a/src/content/docs/textbook/c-lang/beginner/20--recursive-functio-call.mdx +++ b/src/content/docs/textbook/c-lang/beginner/20--recursive-functio-call.mdx @@ -1,4 +1,170 @@ --- -title : 関数の再帰呼出し +title : ex. 再帰関数呼出し slug: http://localhost:4321/textbook/c-lang/beginner/recursive-functio-call --- + +import { Aside } from '@astrojs/starlight/components'; +import { Tabs, TabItem } from '@astrojs/starlight/components'; + +関数は、その関数内で自身と同じ関数を呼び出すことができます。 +この呼出しは再帰関数呼出しと呼ばれ、プログラムの簡略化に役立ちます。 +再帰関数呼出しの基本を学んでいきましょう。 + +## 再帰 + +自分自身を含んでいたり、自分自身によって定義されたりする事象は、**再帰的**(recursive)であるといえます。 + +再帰的な現象の一例として、合わせ鏡があります。 +合わせ鏡では、鏡に映る鏡の中に鏡が映り、さらにその中の鏡にもまた鏡が映る……、といった具合に果てしなく続いていきます。 + +再帰の考え方を効率的に活用することで、繰返し処理のともなうプログラムを簡潔かつ効率的に記述することができます。 + +## 関数の再帰的定義 + +再帰を扱う例として、階乗値を求める関数を作成してみましょう。 + +まずは、非負の整数`n`の階乗を、再帰的に定義してみます。 + +- 階乗`n!`の定義(`n`は非負の整数であるとする) + - `0! = 1` + - `n > 0`ならば、`n! = n * (n - 1)!` + +たとえば、`3`の階乗は`3 * 2!`で表され、この計算式の過程に使われる式`2!`は`2 * 1!`で表されます。 +したがって、`3! = 3 * 2! = 3 * (2 * 1!) = 3 * 2 * (1 * 0!)`となります。 +ここで、`0!`の定義`0! = 1`より、`3!`は最終的に`3! = 3 * 2 * 1 * 1 = 6`として算出されることになります。 + +この定義を実現したものが、次のプログラムになります。 + + + +```c +#include + +int factorial(int n) { + if (n > 0) + return n * factorial(n - 1); + else + return 1; +} + +int main(int argc, const char * argv[]) { + int n = 3; + + printf("%d! = %d\n", n, factorial(n)); + + return 0; +} +``` + + +``` +3! = 6 +``` + + + +このプログラムは、次の手順によって実行されていきます。 + +1. 関数呼出し式`factorial(3)`によって、関数`factorial`を呼び出します。 +この関数は、仮引数`n`に`3`を受け取ることで、返却値`3 * factorial(2)`を返します。 +この乗算を行うためには、`factorial(2)`の値が必要になるため、実引数`2`を渡して関数`factorial`を呼び出します。 + +2. 呼び出された関数`factorial`は、仮引数`n`に`2`を受け取ります。 +この関数の返却値`2 * factorial(1)`の乗算を行うために、実引数`1`を渡して関数`factorial`を呼び出します。 + +3. 呼び出された関数`factorial`は、仮引数`n`に`1`を受け取ります。 +この関数の返却値`1 * factorial(0)`の乗算を行うために、実引数`0`を渡して関数`factorial`を呼び出します。 + +4. 呼び出された関数`factorial`は、仮引数`n`に`0`を受け取ります。 +そのため、返却値として`1`を返します。 +なお、この時点で初めて`return`文が実行されます。 + +5. 返却値`1`を受け取った関数`factorial`は、`1 * factorial(0)`すなわち`1 * 1`を返却値として返します。 + +6. 返却値`1`を受け取った関数`factorial`は、`2 * factorial(1)`すなわち`2 * 1`を返却値として返します。 + +7. 返却値`2`を受け取った関数`factorial`は、`3 * factorial(2)`すなわち`3 * 2`を返却値として返します。 + +この手順によって、`3`の階乗値`6`を得ることができます。 + +このような関数の再帰的な呼び出しを、**再帰関数呼出し**(recursive functio call)と呼びます。 + +## 練習問題 + + + +異なる`n`個の整数から、`r`個の整数を取り出す組合わせの数`C(n, r)`を求める関数を作成してください。 + +```c +int combination(int n, int r) { + // TODO +} + +int main(int argc, const char * argv[]) { + int n = ***, r = ***; + + printf("C(%d, %d) = %d\n", n, r, combination(n, r)); + + return 0; +} +``` + +なお、`C(n, r)`は次のように定義されます。 + +`C(n, r) = C(n - 1, r - 1) + C(n - 1, r)`(ただし、`C(n, 0) = C(n, n) = 1`、`C(n, 1) = n`) + + +``` +C(6, 3) = 20 +``` +``` +C(6, 1) = 6 +``` +``` +C(6, 0) = 1 +``` + + +`C(n, r)`の定義より、基本的には`C(n - 1, r - 1) + C(n - 1, r)`を返却しましょう。 + +```c +return combination(n - 1, r - 1) + combination(n - 1, r); +``` + + +`C(n, 0)`および`C(n, n)`の定義から、`r = 0`または`r = n`の場合は、`1`を返却しましょう。 + +```c +if (r == 0 || r == n) + return 1; +``` + + +`C(n, 1)`の定義から、`r = 1`の場合は、`n`を返却しましょう。 + +```c +if (r == 1) + return n; +``` + + +```c +int combination(int n, int r) { + if (r == 0 || r == n) + return 1; // C(n, 0) = C(n, n) = 1 + else if (r == 1) + return n; // C(n, 1) = n + else + return combination(n - 1, r - 1) + combination(n - 1, r); +} + +int main(int argc, const char * argv[]) { + int n = 6, r = 3; + + printf("C(%d, %d) = %d\n", n, r, combination(n, r)); + + return 0; +} +``` + + diff --git a/src/content/docs/textbook/c-lang/beginner/21--tic-tac-toe.mdx b/src/content/docs/textbook/c-lang/beginner/21--tic-tac-toe.mdx index e8a80fb..8395bb1 100644 --- a/src/content/docs/textbook/c-lang/beginner/21--tic-tac-toe.mdx +++ b/src/content/docs/textbook/c-lang/beginner/21--tic-tac-toe.mdx @@ -2,3 +2,535 @@ title : 「まるばつゲーム」の作成 slug: http://localhost:4321/textbook/c-lang/beginner/tic-tac-toe --- + +import { Aside } from '@astrojs/starlight/components'; +import { Tabs, TabItem } from '@astrojs/starlight/components'; +import { Steps } from '@astrojs/starlight/components'; + +これまで学習してきたことを踏まえて、簡単な「まるばつゲーム」を作成していきましょう。 + +## アルゴリズム + +まずは、「まるばつゲーム」の簡単なアルゴリズムを確認しましょう。 + + +1. ゲーム開始 + +2. 『まる』(または『ばつ』)の人が、自身のマークを9マスのグリッド内の空欄に刻む + ``` + X . . + . O X + O . . + ``` +3. 刻んだマークが三目並んだ場合、『まる』(または『ばつ』)の人の勝利として、Step.6へ + ``` + X . O + . O X + O O X + ``` +4. グリッド内に空欄が存在しない場合、勝負は引き分けとして、Step.6へ + ``` + X O X + O O X + O X O + ``` +5. 『ばつ』(または『まる』)の人にターンを移して、Step.2へ + +6. ゲーム終了 + + +## プログラムの設計 + +上記のアルゴリズムから、必要になりそうな機能(関数)や変数などを考えていきましょう。 + +- 盤面を標準出力する関数 +- 盤上のマークをカウントする関数 +- 勝利を判定する関数 + +- 盤を表す2次元配列 +- 現在のターンを示す変数 +- 勝利フラグ + +アルゴリズムにのっとり、これらを組み合わせていくことで、「まるばつゲーム」を作成していきましょう。 + +## プログラムの作成 + + +1. `main`関数内に、必要になりそうな変数を宣言します。 + + なお、「まるばつゲーム」の盤を2次元配列で宣言するにあたって、`0`ならば空欄、`1`ならば『まる』が、`2`ならば『ばつ』が刻まれていることにします。 + オブジェクト形式マクロを用いることで、これらの定数にあらかじめ名前をつけておきましょう。 + + ```c + #include + + #define BLANK 0 // から + #define CIRCLE 1 // まる('O') + #define CROSS 2 // ばつ('X') + + #define BOARD_SIZE 3 // 盤の行列数 + + // 盤面を表示する関数 + /* Step.3 にて記述 */ + + // 盤面を表す2次元配列にアクセスする関数 + /* Step.4 にて記述 */ + + // 任意のマークをカウントする関数 + /* Step.5 にて記述 */ + + // + /* Step.6 にて記述 */ + + int main(int argc, const char * argv[]) { + // 盤を表す2次元配列 + int board[BOARD_SIZE][BOARD_SIZE] = { + {BLANK, BLANK, BLANK}, + {BLANK, BLANK, BLANK}, + {BLANK, BLANK, BLANK} + }; + // 現在のターンを示す変数(1: まる, 2: ばつ) + int currPlayer = CIRCLE; + // 勝利フラグ(0: 引き分け, 1: まるの勝利, 2: ばつの勝利) + int isWin = 0; + + /* Step.2 にて記述 */ + + return 0; + } + ``` + + 勝利フラグ`isWin`は、`0`で初期化しています。 + ゲームの過程で勝者が決まったときに、勝者に対応する整数値で更新する予定です。 + +2. 繰返し構文を用いて、ゲームの一連の流れを実現していきます。 + + ```c + while ( /* 繰返し処理の条件式(Step.5 にて記述) */ ) { + if (currPlayer == CIRCLE) + printf("【まるのターン】"); + else + printf("【ばつのターン】"); + + // 設置する座標の入力受付け + int pos; + printf("設置する座標を入力してください(1〜9):"); + scanf("%d", &pos); + + // 対応する座標にマークを設置 + /* Step.4 にて記述 */ + + // 勝利判定 + /* Step.6 にて記述 */ + + // 現在のターンの更新 + currPlayer = currPlayer % 2 + 1; // 1 -> 2, 2 -> 1 + } + + // 勝敗結果の表示 + printf("【結果】"); + if (isWin == CIRCLE) { + // isWin == 1 + puts("まるの勝利!"); + } else if (isWin == CROSS) { + // isWin == 2 + puts("ばつの勝利!"); + } else { + // isWin == 0 + puts("引き分け……"); + } + ``` + + マークを刻むときに標準入力されるグリッドの座標番号は、`1`から`9`の整数値で指定することにします。 + + ``` + 【ばつのターン】設置する座標を入力してください(1〜9):8 + ====== + 1 2 3 + 4 5 6 + 7 X 9 + ====== + ``` + + おおまかなアルゴリズムの流れに沿ったプログラムを組むことができました。 + あとは、自作関数を定義することで、必要な機能を補っていきましょう。 + +3. 盤面を表示する関数を作成します。 + + ```c + // 盤面を表示する関数 + void printBoard(int board[BOARD_SIZE][BOARD_SIZE]) { + puts("======"); + for (int i = 0; i < BOARD_SIZE; i++) { + for(int j = 0; j < BOARD_SIZE; j++) { + switch (board[i][j]) { + case CIRCLE: // まる + printf(" O"); + break; + case CROSS: // ばつ + printf(" X"); + break; + default: // 空欄(座標番号を表示) + printf("%2d", i * BOARD_SIZE + j + 1); + break; + } + } + putchar('\n'); + } + puts("======"); + } + ``` + + 盤面を表す2次元配列を仮引数として受け取り、その内容を標準出力しています。 + マークが空欄の場合は、座標番号を表示するようにしています。 + +4. 入力された座標番号に対応する配列の要素にアクセスして、値を書き換えられるようにします。 + + そのために、配列の要素にアクセスするための関数を作成します。 + + ```c + // 座標番号に対応する要素(マーク)を返す関数 + int getMark(int board[BOARD_SIZE][BOARD_SIZE], int pos) { + return board[(pos - 1) / BOARD_SIZE][(pos - 1) % BOARD_SIZE]; + } + + // 座標番号に対応する要素にマークを代入する関数 + void setMark(int board[BOARD_SIZE][BOARD_SIZE], int pos, int mark) { + board[(pos - 1) / BOARD_SIZE][(pos - 1) % BOARD_SIZE] = mark; + } + ``` + + `[(pos - 1) / BOARD_SIZE][(pos - 1) % BOARD_SIZE]`では、与えられた座標番号(`1`から`9`までの整数値)に対して、対応する2次元配列の添字を算出しています。 + なお、対応関係は次のようになっています。 + + ``` + [0][0],[0][1],[0][2] // 1,2,3 + [1][0],[1][1],[1][2] // 4,5,6 + [2][0],[2][1],[2][2] // 7,8,9 + ``` + + この関数を利用することで、実際にマークを設定していきましょう。 + + ```c + if (pos >= 1 && pos <= 9 && getMark(board, pos) == BLANK) { + // 入力された座標番号が正常、かつ対応するマークが空欄である場合 + setMark(board, pos, currPlayer); + + // 盤面の表示 + printBoard(board); + } else { + puts("【エラー】プログラムを終了します."); + + return 0; // 強制終了 + } + ``` + + なお、今回は簡略化のため、正しい入力が認められなかった場合には、プログラムを強制終了するようにしています。 + +5. 盤上のマークをカウントする関数を作成します。 + + ```c + // 任意のマークをカウントする関数 + int countMark(int board[BOARD_SIZE][BOARD_SIZE], int target) { + int cnt = 0; + + for (int i = 0; i < BOARD_SIZE; i++) { + for(int j = 0; j < BOARD_SIZE; j++) { + if (board[i][j] == target) + cnt++; + } + } + + return cnt; + } + ``` + + 盤面を表す2次元配列と任意のマークを表す整数値を仮引数として受け取り、そのマークが配列内にいくつ存在するのかをカウントして返す関数になります。 + + この関数を`while`文の条件式内で呼び出すことで、空欄(`BLANK`)の数をカウントして、その値が`0`であるときに繰返し処理を終了する仕組みにしましょう。 + + ```c + while(countMark(board, BLANK) != 0) { + /* 省略 */ + } + ``` + +6. 勝利を判定する関数を作成します。 + + ```c + // 3目の並びを判定する関数 + int checkLine(int board[BOARD_SIZE][BOARD_SIZE], int mark, int pattern[]) { + // 1パターン分の座標番号の走査 + for (int i = 0; i < BOARD_SIZE; i++) { + if (getMark(board, pattern[i]) != mark) { + // mark と合致しない場合 + return 0; + } + } + + // 1パターン分のすべて座標番号が mark と合致した場合 + return 1; + } + + // 勝利を判定する関数 + int checkWin(int board[BOARD_SIZE][BOARD_SIZE], int mark) { + // 3目の並びのパターン + int patterns[8][BOARD_SIZE] = { + {1, 2, 3}, + {4, 5, 6}, + {7, 8, 9}, + {1, 4, 7}, + {2, 5, 8}, + {3, 6, 9}, + {1, 5, 9}, + {3, 5, 7} + }; + + // すべてのパターンの走査 + for (int i = 0; i < 8; i++) { + if (checkLine(board, mark, patterns[i]) == 1) { + // 3目の並びに合致するパターンをみつけた場合 + return 1; + } + } + + return 0; + } + ``` + + 関数呼出しに関しては、`main`関数内から`checkWin`関数を呼出し、`checkWin`関数内から`checkLine`関数を呼び出すことになります。 + + 仮引数の`mark`には、現在のターンとなるプレイヤーのマークが渡されます。 + + 2次元配列`patterns`には、3目の並びとなるすべてのパターンが格納されています。 + `board`の要素の並びがいずれかのパターンに合致するかどうかを、`checkLine`関数を呼び出すことで判定します。 + + `checkLine`関数では、仮引数として渡された`pattern`(1パターン分)に合致しない場合は即座に`0`を、合致する場合は`1`を返します。 + また、`checkWin`関数では、最大で8回呼び出される`checkLine`関数が`1`を返した場合は即座に`1`を、そうでない場合は`0`を返します。 + + この仕組みによって、勝利の判定を実現しています。 + + では、`checkWin`関数を`main`関数内から呼び出してみましょう。 + + ```c + if (checkWin(board, currPlayer) == 1) { + // 勝利フラグの更新 + isWin = currPlayer; + break; + } + ``` + + `checkWin`関数が`1`を返した場合、勝利フラグ`isWin`を、現在ターンのプレイヤーのマークを代入することで塗り替えています。 + また、`break`文によって`while`文を脱出して、勝敗結果を表示するフェーズまでプログラムを進行させます。 + + +## 完成品 + + + +```c +#include + +#define BLANK 0 // から +#define CIRCLE 1 // まる('O') +#define CROSS 2 // ばつ('X') + +#define BOARD_SIZE 3 // 盤の行列数 + +// 盤面を表示する関数 +void printBoard(int board[BOARD_SIZE][BOARD_SIZE]) { + puts("======"); + for (int i = 0; i < BOARD_SIZE; i++) { + for(int j = 0; j < BOARD_SIZE; j++) { + switch (board[i][j]) { + case CIRCLE: // まる + printf(" O"); + break; + case CROSS: // ばつ + printf(" X"); + break; + default: // 空欄 + printf("%2d", i * BOARD_SIZE + j + 1); + break; + } + } + putchar('\n'); + } + puts("======"); +} + +// 座標番号に対応する要素(マーク)を返す関数 +int getMark(int board[BOARD_SIZE][BOARD_SIZE], int pos) { + return board[(pos - 1) / BOARD_SIZE][(pos - 1) % BOARD_SIZE]; +} + +// 座標番号に対応する要素にマークを代入する関数 +void setMark(int board[BOARD_SIZE][BOARD_SIZE], int pos, int mark) { + board[(pos - 1) / BOARD_SIZE][(pos - 1) % BOARD_SIZE] = mark; +} + +// 任意のマークをカウントする関数 +int countMark(int board[BOARD_SIZE][BOARD_SIZE], int target) { + int cnt = 0; + + for (int i = 0; i < BOARD_SIZE; i++) { + for(int j = 0; j < BOARD_SIZE; j++) { + if (board[i][j] == target) + cnt++; + } + } + + return cnt; +} + +// 3目の並びを判定する関数 +int checkLine(int board[BOARD_SIZE][BOARD_SIZE], int mark, int pattern[]) { + // 1パターン分の座標番号の走査 + for (int i = 0; i < BOARD_SIZE; i++) { + if (getMark(board, pattern[i]) != mark) { + // mark と合致しない場合 + return 0; + } + } + + // 1パターン分のすべて座標番号が mark と合致した場合 + return 1; +} + +// 勝利を判定する関数 +int checkWin(int board[BOARD_SIZE][BOARD_SIZE], int mark) { + // 3目の並びのパターン + int patterns[8][BOARD_SIZE] = { + {1, 2, 3}, + {4, 5, 6}, + {7, 8, 9}, + {1, 4, 7}, + {2, 5, 8}, + {3, 6, 9}, + {1, 5, 9}, + {3, 5, 7} + }; + + // すべてのパターンの走査 + for (int i = 0; i < 8; i++) { + if (checkLine(board, mark, patterns[i]) == 1) { + // 3目の並びに合致するパターンをみつけた場合 + return 1; + } + } + + return 0; +} + +int main(int argc, const char * argv[]) { + // 盤を表す2次元配列 + int board[BOARD_SIZE][BOARD_SIZE] = { + {BLANK, BLANK, BLANK}, + {BLANK, BLANK, BLANK}, + {BLANK, BLANK, BLANK} + }; + // 現在のターンを示す変数(1: まる, 2: ばつ) + int currPlayer = CIRCLE; + // 勝利フラグ(0: 引き分け, 1: まるの勝利, 2: ばつの勝利) + int isWin = 0; + + while (countMark(board, BLANK) != 0) { + if (currPlayer == CIRCLE) + printf("【まるのターン】"); + else + printf("【ばつのターン】"); + + // 設置する座標の入力受付け + int pos; + printf("設置する座標を入力してください(1〜9):"); + scanf("%d", &pos); + + if (pos >= 1 && pos <= 9 && getMark(board, pos) == BLANK) { + // 入力された座標番号が正常、かつ対応するマークが空欄である場合 + setMark(board, pos, currPlayer); + + // 盤面の表示 + printBoard(board); + } else { + puts("【エラー】プログラムを終了します."); + + return 0; // 強制終了 + } + + if (checkWin(board, currPlayer) == 1) { + // 勝利フラグの更新 + isWin = currPlayer; + break; + } + + // 現在のターンの更新 + currPlayer = currPlayer % 2 + 1; // 1 -> 2, 2 -> 1 + } + + // 勝敗結果の表示 + printf("【結果】"); + if (isWin == CIRCLE) { + // isWin == 1 + puts("まるの勝利!"); + } else if (isWin == CROSS) { + // isWin == 2 + puts("ばつの勝利!"); + } else { + // isWin == 0 + puts("引き分け……"); + } + + return 0; +} +``` + + +``` +【まるのターン】設置する座標を入力してください(1〜9):1 +====== + O 2 3 + 4 5 6 + 7 8 9 +====== +【ばつのターン】設置する座標を入力してください(1〜9):2 +====== + O X 3 + 4 5 6 + 7 8 9 +====== +【まるのターン】設置する座標を入力してください(1〜9):3 +====== + O X O + 4 5 6 + 7 8 9 +====== +【ばつのターン】設置する座標を入力してください(1〜9):4 +====== + O X O + X 5 6 + 7 8 9 +====== +【まるのターン】設置する座標を入力してください(1〜9):5 +====== + O X O + X O 6 + 7 8 9 +====== +【ばつのターン】設置する座標を入力してください(1〜9):6 +====== + O X O + X O X + 7 8 9 +====== +【まるのターン】設置する座標を入力してください(1〜9):7 +====== + O X O + X O X + O 8 9 +====== +【結果】まるの勝利! +``` + + + +これにて、「まるばつゲーム」のプログラム作成は終了になります。 From 30b7af14400727921b17dd328931dc5d7523c92e Mon Sep 17 00:00:00 2001 From: Yukito-Aomi Date: Thu, 20 Mar 2025 15:45:32 +0900 Subject: [PATCH 03/10] delete: Quiz Section --- src/content/docs/textbook/c-lang/beginner/16--array.mdx | 4 ---- .../docs/textbook/c-lang/beginner/17--character-string.mdx | 4 ---- .../textbook/c-lang/beginner/18--multidimensional-array.mdx | 4 ---- .../docs/textbook/c-lang/beginner/19--function-definition.mdx | 4 ---- 4 files changed, 16 deletions(-) diff --git a/src/content/docs/textbook/c-lang/beginner/16--array.mdx b/src/content/docs/textbook/c-lang/beginner/16--array.mdx index 39eb0be..1f81726 100644 --- a/src/content/docs/textbook/c-lang/beginner/16--array.mdx +++ b/src/content/docs/textbook/c-lang/beginner/16--array.mdx @@ -382,7 +382,3 @@ int main(int argc, const char * argv[]) { ``` - -## 確認クイズ - - diff --git a/src/content/docs/textbook/c-lang/beginner/17--character-string.mdx b/src/content/docs/textbook/c-lang/beginner/17--character-string.mdx index 56766a9..ca0cee2 100644 --- a/src/content/docs/textbook/c-lang/beginner/17--character-string.mdx +++ b/src/content/docs/textbook/c-lang/beginner/17--character-string.mdx @@ -183,7 +183,3 @@ int main(int argc, const char * argv[]) { ``` - -## 確認クイズ - - diff --git a/src/content/docs/textbook/c-lang/beginner/18--multidimensional-array.mdx b/src/content/docs/textbook/c-lang/beginner/18--multidimensional-array.mdx index 71e1e55..a0f3986 100644 --- a/src/content/docs/textbook/c-lang/beginner/18--multidimensional-array.mdx +++ b/src/content/docs/textbook/c-lang/beginner/18--multidimensional-array.mdx @@ -211,7 +211,3 @@ int main(int argc, const char * argv[]) { ``` - -## 確認クイズ - - diff --git a/src/content/docs/textbook/c-lang/beginner/19--function-definition.mdx b/src/content/docs/textbook/c-lang/beginner/19--function-definition.mdx index c17664a..0df16a3 100644 --- a/src/content/docs/textbook/c-lang/beginner/19--function-definition.mdx +++ b/src/content/docs/textbook/c-lang/beginner/19--function-definition.mdx @@ -297,7 +297,3 @@ int main(int argc, const char * argv[]) { ``` - -## 確認クイズ - - From 014a19b6c438a0641b28f719770e4d62e62297d4 Mon Sep 17 00:00:00 2001 From: Yukito-Aomi Date: Sat, 17 May 2025 18:59:41 +0900 Subject: [PATCH 04/10] update: Chapter16 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Chapter 16 - 配列の図の追加 - 練習問題の修正 - プログラム番号の追加 ## Chapter 18 - オブジェクト形式マクロに関する記述の削除 ## 全体 - 小見出しの下線(`---`)追加 --- public/chap16_array.drawio.svg | 4 + public/chap16_array_with_subscript.drawio.svg | 4 + .../textbook/c-lang/beginner/16--array.mdx | 278 ++++++++++-------- .../c-lang/beginner/17--character-string.mdx | 26 ++ .../beginner/18--multidimensional-array.mdx | 57 ++-- .../beginner/19--function-definition.mdx | 27 ++ .../beginner/20--recursive-functio-call.mdx | 26 ++ .../c-lang/beginner/21--tic-tac-toe.mdx | 4 + 8 files changed, 285 insertions(+), 141 deletions(-) create mode 100644 public/chap16_array.drawio.svg create mode 100644 public/chap16_array_with_subscript.drawio.svg diff --git a/public/chap16_array.drawio.svg b/public/chap16_array.drawio.svg new file mode 100644 index 0000000..5f0c47c --- /dev/null +++ b/public/chap16_array.drawio.svg @@ -0,0 +1,4 @@ + + + +
a [ 4 ]
a [ 4 ]
a [ 3 ]
a [ 3 ]
a [ 2 ]
a [ 2 ]
a [ 1 ]
a [ 1 ]
a [ 0 ]
a [ 0 ]
先頭要素
先頭要素
末尾要素
末尾要素
Text is not SVG - cannot display
\ No newline at end of file diff --git a/public/chap16_array_with_subscript.drawio.svg b/public/chap16_array_with_subscript.drawio.svg new file mode 100644 index 0000000..2fb534e --- /dev/null +++ b/public/chap16_array_with_subscript.drawio.svg @@ -0,0 +1,4 @@ + + + +
a [ 4 ]
a [ 4 ]
先頭要素(添字:0
先頭要素(添字:0)
末尾要素(添字:要素数 - 1
末尾要素(添字:要素数 - 1)
a [ 3 ]
a [ 3 ]
a [ 2 ]
a [ 2 ]
a [ 1 ]
a [ 1 ]
a [ 0 ]
a [ 0 ]
Text is not SVG - cannot display
\ No newline at end of file diff --git a/src/content/docs/textbook/c-lang/beginner/16--array.mdx b/src/content/docs/textbook/c-lang/beginner/16--array.mdx index 1f81726..e09e586 100644 --- a/src/content/docs/textbook/c-lang/beginner/16--array.mdx +++ b/src/content/docs/textbook/c-lang/beginner/16--array.mdx @@ -5,17 +5,20 @@ slug: http://localhost:4321/textbook/c-lang/beginner/array import { Aside } from '@astrojs/starlight/components'; import { Tabs, TabItem } from '@astrojs/starlight/components'; +import { Card } from '@astrojs/starlight/components'; + 同じ型の変数の集まりは、ひとまとめにして表現することができます。 そのために利用する**配列**の基礎について学んでいきましょう。 + ## 配列とは +--- -まずは、3人の学生の点数について、その合計値と平均値を算出するプログラムを作成してみましょう。 - - -```c +まずは、3人の学生の点数について、その合計値と平均値を算出するプログラム(`list16_01.c`)を作成してみましょう。 + +```c title="list16_01.c" #include int main(int argc, const char * argv[]) { @@ -23,11 +26,14 @@ int main(int argc, const char * argv[]) { int score2 = 100; // 2 人目の点数 int score3 = 85; // 3 人目の点数 + printf("1人目の点数: %3d\n", score1); + printf("2人目の点数: %3d\n", score2); + printf("3人目の点数: %3d\n", score3); + int sum = 0; // 合計点(初期値:0) - - printf("1人目の点数: %3d\n", score1); sum += score1; - printf("2人目の点数: %3d\n", score2); sum += score2; - printf("3人目の点数: %3d\n", score3); sum += score3; + sum += score1; + sum += score2; + sum += score3; printf("合計点: %d\n", sum); printf("平均点: %.1f\n", (double)sum / 3); @@ -35,8 +41,8 @@ int main(int argc, const char * argv[]) { return 0; } ``` - - + +**実行結果** ``` 1人目の点数: 70 2人目の点数: 100 @@ -44,10 +50,8 @@ int main(int argc, const char * argv[]) { 合計点: 255 平均点: 85.0 ``` - - -このプログラムでは、3人の点数がそれぞれ`int`型の3つの変数(`score1`・`score2`・`score3`)として表されています。 +このプログラム`list16_01.c`では、3人の点数がそれぞれ`int`型の3つの変数(`score1`・`score2`・`score3`)として表されています。 さて、今後プログラムを組んでいくうえで、学生の人数が100人に増えた場合を考えましょう。 このプログラムのようなやり方では、点数を格納するための変数を100つも用意することになるでしょう。 @@ -61,7 +65,9 @@ int main(int argc, const char * argv[]) { **配列**とは、同一型の変数(**要素**)が線型かつ連続的に並んだものである。 + ## 配列の宣言 +--- まずは配列を宣言していきましょう。 配列の宣言では、次のように、**要素型(element type)**、**配列名**(変数名)、**要素数**を与えることで実現します。 @@ -78,13 +84,17 @@ int a[5]; これによって、`int`型の5つの値を格納することができる1つの変数を、配列名`a`として宣言することができました。 +![](/public/chap16_array.drawio.svg) + + ## 要素と添字 +--- 配列が持つ個々の要素への**アクセス**は、**添字演算子**(subscript operator)を用いることで実現します。 扱う配列の配列名が`a`である場合、添字演算子は`a[b]`として表されます。 @@ -94,11 +104,11 @@ int a[5]; これは、“先頭要素から何個後ろの要素なのか”を表す整数値になります。 そのため、要素数が`n`である配列の添字の範囲が、`0`から`n - 1`までの整数値となることに注意しましょう。 -実際に、冒頭のプログラムを、配列を用いて表現してみましょう。 +![](/public/chap16_array_with_subscript.drawio.svg) - - -```c +実際に、冒頭のプログラム`list16_01.c`を、配列を用いて表現してみましょう。 + +```c title="list16_02.c" #include int main(int argc, const char * argv[]) { @@ -110,20 +120,23 @@ int main(int argc, const char * argv[]) { scores[1] = 100; // 2 人目の点数 scores[2] = 85; // 3 人目の点数 + printf("1人目の点数: %3d\n", scores[0]); + printf("2人目の点数: %3d\n", scores[1]); + printf("3人目の点数: %3d\n", scores[2]); + int sum = 0; // 合計点(初期値:0) - - printf("1人目の点数: %3d\n", scores[0]); sum += scores[0]; - printf("2人目の点数: %3d\n", scores[1]); sum += scores[1]; - printf("3人目の点数: %3d\n", scores[2]); sum += scores[2]; - + sum += scores[0]; + sum += scores[1]; + sum += scores[2]; + printf("合計点: %d\n", sum); printf("平均点: %.1f\n", (double)sum / 3); return 0; } ``` - - + +**実行結果** ``` 1人目の点数: 70 2人目の点数: 100 @@ -131,33 +144,34 @@ int main(int argc, const char * argv[]) { 合計点: 255 平均点: 85.0 ``` - - + ## 配列の初期化 +--- 配列の各要素の値の設定を、代入ではなく初期化によって実現してみましょう。 配列の初期化子は、各要素に対する初期化子をコンマ(`,`)で区切って順に並べたものを`{}`で囲んだ形式で与えます。 -まずは、冒頭のプログラムを、配列の初期化を用いて表現してみましょう。 +まずは、プログラム`list16_02.c`を、配列の初期化を用いて表現してみましょう。 - - -```c +```c title="list16_03.c" #include int main(int argc, const char * argv[]) { int scores[3] = {70, 100, 85}; // 配列の初期化 + printf("1人目の点数: %3d\n", scores[0]); + printf("2人目の点数: %3d\n", scores[1]); + printf("3人目の点数: %3d\n", scores[2]); + int sum = 0; // 合計点(初期値:0) - - printf("1人目の点数: %3d\n", scores[0]); sum += scores[0]; - printf("2人目の点数: %3d\n", scores[1]); sum += scores[1]; - printf("3人目の点数: %3d\n", scores[2]); sum += scores[2]; + sum += scores[0]; + sum += scores[1]; + sum += scores[2]; printf("合計点: %d\n", sum); printf("平均点: %.1f\n", (double)sum / 3); @@ -165,8 +179,8 @@ int main(int argc, const char * argv[]) { return 0; } ``` - - + +**実行結果** ``` 1人目の点数: 70 2人目の点数: 100 @@ -174,8 +188,6 @@ int main(int argc, const char * argv[]) { 合計点: 255 平均点: 85.0 ``` - - 配列の宣言と各要素への代入を1行で表現することができました。 @@ -237,17 +249,17 @@ int d[3] = {0}; // 全要素を 0 で初期化 ``` + ## 配列の走査 +--- **走査**(traverse)とは、配列の要素をひとつずつ順番になぞっていくことを意味します。 `for`文を用いた配列の走査について確認しましょう。 配列を先頭から順に走査するには、`for`文のカウンタ用変数を、配列の添字として利用することで実現できます。 -冒頭のプログラムを、配列と`for`文を用いて表現してみましょう。 +プログラム`list16_03.c`を、配列と`for`文を用いて表現してみましょう。 - - -```c +```c title="list16_04.c" #include int main(int argc, const char * argv[]) { @@ -267,8 +279,8 @@ int main(int argc, const char * argv[]) { return 0; } ``` - - + +**実行結果** ``` 1人目の点数: 70 2人目の点数: 100 @@ -276,109 +288,143 @@ int main(int argc, const char * argv[]) { 合計点: 255 平均点: 85.0 ``` - - `for`文を用いることで、添字のみが異なるような命令を、一括で記述することができました。 命令や繰返し処理を工夫することで、さまざまなプログラムに応用することができます。 さて、実際に練習問題を解きながら、配列を学んでいきましょう。 -## 練習問題 - - + +### 練習問題1 + キーボードからの入力により、5人の身長(`double`型)を配列`heights`に格納してください。 -また、閾値`th = 170.0`を超える値の一覧を出力してください。 -また、最大値と最小値を求めて出力してください。 - - -``` -heights[0]? 170.0(注:標準入力) -heights[1]? 168.2(注:標準入力) -heights[2]? 186.9(注:標準入力) -heights[3]? 152.0(注:標準入力) -heights[4]? 170.5(注:標準入力) ---------------- -# heights>170.0 -heights[2]: 186.9 -heights[4]: 170.5 ---------------- -max: 186.9 -min: 152.0 -``` - - -配列の要素への標準入力は、変数の標準入力と同様のやり方で実現できます。 -繰返し処理を活用することで、すべての要素へアクセスしてみましょう。 +また、入力された身長の平均値を算出して出力してください. -```c -scanf("%lf", &heights[i]); +**実行結果** ``` - - -繰返し処理のなかで、条件に合致する要素のみを標準出力してみましょう。 -標準入力用の繰返し構文とは別の繰返し構文を用意する必要があります。 - -```c -// 配列の要素が閾値を超える場合 -if (heights[i] > th) printf("heights[%d]: %.1f\n", i, heights[i]); +heights[0]? 170.0 +heights[1]? 168.2 +heights[2]? 186.9 +heights[3]? 152.0 +heights[4]? 170.5 +average: 169.520000 ``` - - -最大値を求めるには、次のようなやり方があります。 - -- 配列内の適当な要素を持つ変数`max`を用意する -- 配列を走査するなかで、`max`を超える値がみつかった場合、その値を`max`に代入することで更新する - +ファイル名: `16_issue1.c` +
+ヒント(キーボードによる入力受付けについて) + +
+
+ヒント(配列の要素の平均値について) + +
+
+模範解答 +```c title="16_issue1.c" #include int main(int argc, const char * argv[]) { double heights[5]; - double th = 170.0; - // 整数値の入力 + // 身長の入力 for (int i = 0; i < 5; i++) { printf("heights[%d]? ", i); scanf("%lf", &heights[i]); } - // 最大値および最小値の宣言(適当な要素の値で初期化しておく) - double max, min; - max = min = heights[0]; + double sum = 0; // 身長の合計値(初期値:0) - puts("---------------"); + // 配列の走査による合計値の算出 + for (int i = 0; i < 5; i++) { + sum += heights[i]; + } + + // 平均値の出力 + printf("average: %f\n", sum / 5); + + return 0; +} +``` +
+ + + + +### 練習問題2 + +以下の配列`arr`が与えられているとき、この配列の要素の最小値と最大値をそれぞれ求めなさい。 +```c +int arr[] = {170, 168, 186, 152, 171}; +``` + +**実行結果** +``` +min: 152 +max: 186 +``` +ファイル名: `16_issue2.c` +
+ヒント + +
+
+模範解答 +```c title="16_issue2.c" +#include + +int main(int argc, const char * argv[]) { + int arr[] = {170, 168, 186, 152, 171}; - printf("# heights>%.1f\n", th); + // 最小値と最大値(初期値として適当な要素を格納しておく) + int min = arr[0]; + int max = arr[0]; for (int i = 0; i < 5; i++) { - // 閾値を超える身長の出力 - if (heights[i] > th) printf("heights[%d]: %.1f\n", i, heights[i]); - - // 最大値および最小値の更新 - if (max < heights[i]) max = heights[i]; - if (min > heights[i]) min = heights[i]; + if (min > arr[i]) { + // min よりも小さな値を見つけた場合、min を更新 + min = arr[i]; + } + if (max < arr[i]) { + // max よりも大きな値を見つけた場合、max を更新 + max = arr[i]; + } } - puts("---------------"); - - printf("max: %.1f\n", max); - printf("min: %.1f\n", min); + printf("min: %d\n", min); + printf("max: %d\n", max); return 0; } ``` - - +
+
diff --git a/src/content/docs/textbook/c-lang/beginner/17--character-string.mdx b/src/content/docs/textbook/c-lang/beginner/17--character-string.mdx index ca0cee2..2bf2153 100644 --- a/src/content/docs/textbook/c-lang/beginner/17--character-string.mdx +++ b/src/content/docs/textbook/c-lang/beginner/17--character-string.mdx @@ -10,6 +10,7 @@ import { Tabs, TabItem } from '@astrojs/starlight/components'; そんな文字列の基本を学んでいきましょう。 ## 文字列リテラル +--- `"hoge"`のような、文字の並びを二重引用符`"`で囲んだものを**文字列リテラル**(string literal)と呼びます。 @@ -18,6 +19,7 @@ import { Tabs, TabItem } from '@astrojs/starlight/components'; また、見かけ上0文字の文字列リテラル`""`も同様に、ヌル文字を含めた1文字分の記憶域を占有しています。 ## 文字列 +--- **文字列**(string)とは、複数の文字を並べたデータのようなものです。 @@ -86,6 +88,7 @@ scanf("%c", &str[0]); ``` ## 文字列の代入 +--- 代入演算子`=`による文字列の代入ができないことに注意しましょう。 @@ -183,3 +186,26 @@ int main(int argc, const char * argv[]) { ```
+{/* +### 練習問題******(問題番号) + +******(問題文) + +**実行結果** +``` +******(実行結果) +``` +ファイル名: `17_issue******(問題番号).c` +
+ヒント + +
+
+模範解答 +```c title="17_issue******(問題番号).c" +******(模範解答) +``` +
+
*/} \ No newline at end of file diff --git a/src/content/docs/textbook/c-lang/beginner/18--multidimensional-array.mdx b/src/content/docs/textbook/c-lang/beginner/18--multidimensional-array.mdx index a0f3986..303c225 100644 --- a/src/content/docs/textbook/c-lang/beginner/18--multidimensional-array.mdx +++ b/src/content/docs/textbook/c-lang/beginner/18--multidimensional-array.mdx @@ -5,19 +5,25 @@ slug: http://localhost:4321/textbook/c-lang/beginner/multidimensional-array import { Aside } from '@astrojs/starlight/components'; import { Tabs, TabItem } from '@astrojs/starlight/components'; +import { Card } from '@astrojs/starlight/components'; + `int`や`double`などの単一型の変数を集めたものを配列といいました。 実は、その配列を集めることで『配列の配列』をつくることができます。 実際に、『配列の配列』について学んでいきましょう。 + ## 多次元配列 +--- 配列の考え方を応用することで、配列の要素自体が《配列》である配列をつくることができます。 配列を要素型とする配列を**2次元配列**と呼び、この2次元配列を要素型とする配列を**3次元配列**と呼びます。 2次元以上の配列を総称したものが**多次元配列**(multidimensional array)となります。 + ## 2次元配列 +--- 例として、3つの要素をもつ2つの配列を考えてみましょう。 1次元配列の宣言は、次のようにして実現することができました。 @@ -54,7 +60,9 @@ int a[3][4]; // 上記の配列を4個まとめて配列化 ``` + ## 2次元配列とその走査 +--- 多次元配列の走査では、多重ループを用いて実現します。 @@ -106,33 +114,9 @@ int main(int argc, const char * argv[]) { -プログラムの冒頭に、`#define`から始まる命令文が記述されています。 -これは、**オブジェクト形式マクロ**(object-like macro)を定義するための特殊な宣言であり、**`#define`指令**(#define directive)と呼びます。 - -## オブジェクト形式マクロ - -```c -#define A b // この指令以降の A を b とせよ -``` - -この指令文以降に記述された`A`は、`b`に置換された上でプログラムが翻訳・実行されることになります。 -また、**マクロ名**(macro name)`A`は、一般的な変数と区別するために、大文字で定義する慣習があります。 - -上記の九九表を出力するプログラムでは、行数`ROWS`および列数`COLS`に対して、`9`という数値が割り当てられています。 -この値を変更することで、出力される九九表のサイズを変えることができます。 - -プログラム中に直接記述された数値を**マジックナンバー**と呼びます(何を表す数値なのかが不明なため)。 -オブジェクト形式マクロを活用することで、値の管理を1文に集約させたり、マジックナンバーを減らしたりすることに役立ちます。 - -オブジェクト形式マクロを活用することで、プログラムの品質を向上させましょう。 - - ## 練習問題 +--- @@ -211,3 +195,26 @@ int main(int argc, const char * argv[]) { ``` +{/* +### 練習問題******(問題番号) + +******(問題文) + +**実行結果** +``` +******(実行結果) +``` +ファイル名: `18_issue******(問題番号).c` +
+ヒント + +
+
+模範解答 +```c title="18_issue******(問題番号).c" +******(模範解答) +``` +
+
*/} \ No newline at end of file diff --git a/src/content/docs/textbook/c-lang/beginner/19--function-definition.mdx b/src/content/docs/textbook/c-lang/beginner/19--function-definition.mdx index 0df16a3..87eed64 100644 --- a/src/content/docs/textbook/c-lang/beginner/19--function-definition.mdx +++ b/src/content/docs/textbook/c-lang/beginner/19--function-definition.mdx @@ -11,6 +11,7 @@ import { Tabs, TabItem } from '@astrojs/starlight/components'; 自作関数の設計について学んでいきましょう。 ## 関数定義と関数呼出し +--- **関数**(function)とは、値を引数として受け取り、何らかの処理を行う命令のことを指します。 特に、今まで扱ってきた`printf`関数や`puts`関数、`scanf`関数などは、C言語によって標準で提供されており、これを**ライブラリ関数**(library function)と呼びます。 @@ -74,6 +75,7 @@ max: 7 ## 自作関数内での関数呼出し +--- 関数を組み合わせることで、プログラムを少ない記述量で実現することができます。 @@ -125,6 +127,7 @@ max: 7 プログラムの処理の流れと戻り値に注意して、関数を活用していきましょう。 ## 値を返さない関数・引数を受け取らない関数 +--- 値を返さない関数を定義する場合、関数型を`void`(『空の』を意味します)とします。 @@ -187,6 +190,7 @@ natural number? 5(注:標準入力) 上記のような関数を設計する場合、`void`を記述することを忘れないようにしましょう。 ## 練習問題 +--- @@ -297,3 +301,26 @@ int main(int argc, const char * argv[]) { ``` +{/* +### 練習問題******(問題番号) + +******(問題文) + +**実行結果** +``` +******(実行結果) +``` +ファイル名: `19_issue******(問題番号).c` +
+ヒント + +
+
+模範解答 +```c title="19_issue******(問題番号).c" +******(模範解答) +``` +
+
*/} \ No newline at end of file diff --git a/src/content/docs/textbook/c-lang/beginner/20--recursive-functio-call.mdx b/src/content/docs/textbook/c-lang/beginner/20--recursive-functio-call.mdx index d966dc5..b855cad 100644 --- a/src/content/docs/textbook/c-lang/beginner/20--recursive-functio-call.mdx +++ b/src/content/docs/textbook/c-lang/beginner/20--recursive-functio-call.mdx @@ -11,6 +11,7 @@ import { Tabs, TabItem } from '@astrojs/starlight/components'; 再帰関数呼出しの基本を学んでいきましょう。 ## 再帰 +--- 自分自身を含んでいたり、自分自身によって定義されたりする事象は、**再帰的**(recursive)であるといえます。 @@ -20,6 +21,7 @@ import { Tabs, TabItem } from '@astrojs/starlight/components'; 再帰の考え方を効率的に活用することで、繰返し処理のともなうプログラムを簡潔かつ効率的に記述することができます。 ## 関数の再帰的定義 +--- 再帰を扱う例として、階乗値を求める関数を作成してみましょう。 @@ -90,6 +92,7 @@ int main(int argc, const char * argv[]) { このような関数の再帰的な呼び出しを、**再帰関数呼出し**(recursive functio call)と呼びます。 ## 練習問題 +--- @@ -168,3 +171,26 @@ int main(int argc, const char * argv[]) { ``` +{/* +### 練習問題******(問題番号) + +******(問題文) + +**実行結果** +``` +******(実行結果) +``` +ファイル名: `20_issue******(問題番号).c` +
+ヒント + +
+
+模範解答 +```c title="20_issue******(問題番号).c" +******(模範解答) +``` +
+
*/} \ No newline at end of file diff --git a/src/content/docs/textbook/c-lang/beginner/21--tic-tac-toe.mdx b/src/content/docs/textbook/c-lang/beginner/21--tic-tac-toe.mdx index 8395bb1..3aa031e 100644 --- a/src/content/docs/textbook/c-lang/beginner/21--tic-tac-toe.mdx +++ b/src/content/docs/textbook/c-lang/beginner/21--tic-tac-toe.mdx @@ -10,6 +10,7 @@ import { Steps } from '@astrojs/starlight/components'; これまで学習してきたことを踏まえて、簡単な「まるばつゲーム」を作成していきましょう。 ## アルゴリズム +--- まずは、「まるばつゲーム」の簡単なアルゴリズムを確認しましょう。 @@ -40,6 +41,7 @@ import { Steps } from '@astrojs/starlight/components'; ## プログラムの設計 +--- 上記のアルゴリズムから、必要になりそうな機能(関数)や変数などを考えていきましょう。 @@ -54,6 +56,7 @@ import { Steps } from '@astrojs/starlight/components'; アルゴリズムにのっとり、これらを組み合わせていくことで、「まるばつゲーム」を作成していきましょう。 ## プログラムの作成 +--- 1. `main`関数内に、必要になりそうな変数を宣言します。 @@ -325,6 +328,7 @@ import { Steps } from '@astrojs/starlight/components'; ## 完成品 +--- From de8cad72ce6c0077bf4bcb59ef88fa939c5971ba Mon Sep 17 00:00:00 2001 From: Yukito-Aomi Date: Sat, 17 May 2025 19:07:00 +0900 Subject: [PATCH 05/10] =?UTF-8?q?update:=20=E3=82=B3=E3=83=9E=E3=83=B3?= =?UTF-8?q?=E3=83=89=E3=83=A9=E3=82=A4=E3=83=B3=E5=BC=95=E6=95=B0=E3=81=AE?= =?UTF-8?q?=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - `int main(int argc, const char * argv[])`から`int main(void)`へ変更 --- .../docs/textbook/c-lang/beginner/16--array.mdx | 12 ++++++------ .../c-lang/beginner/17--character-string.mdx | 4 ++-- .../c-lang/beginner/18--multidimensional-array.mdx | 4 ++-- .../c-lang/beginner/19--function-definition.mdx | 10 +++++----- .../c-lang/beginner/20--recursive-functio-call.mdx | 6 +++--- .../textbook/c-lang/beginner/21--tic-tac-toe.mdx | 4 ++-- 6 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/content/docs/textbook/c-lang/beginner/16--array.mdx b/src/content/docs/textbook/c-lang/beginner/16--array.mdx index e09e586..ffc9318 100644 --- a/src/content/docs/textbook/c-lang/beginner/16--array.mdx +++ b/src/content/docs/textbook/c-lang/beginner/16--array.mdx @@ -21,7 +21,7 @@ import { Card } from '@astrojs/starlight/components'; ```c title="list16_01.c" #include -int main(int argc, const char * argv[]) { +int main(void) { int score1 = 70; // 1 人目の点数 int score2 = 100; // 2 人目の点数 int score3 = 85; // 3 人目の点数 @@ -111,7 +111,7 @@ int a[5]; ```c title="list16_02.c" #include -int main(int argc, const char * argv[]) { +int main(void) { // 配列の宣言 int scores[3]; // int型の 3 つの値を格納する配列 @@ -161,7 +161,7 @@ int main(int argc, const char * argv[]) { ```c title="list16_03.c" #include -int main(int argc, const char * argv[]) { +int main(void) { int scores[3] = {70, 100, 85}; // 配列の初期化 printf("1人目の点数: %3d\n", scores[0]); @@ -262,7 +262,7 @@ int d[3] = {0}; // 全要素を 0 で初期化 ```c title="list16_04.c" #include -int main(int argc, const char * argv[]) { +int main(void) { int scores[3] = {70, 100, 85}; // 配列の初期化 int sum = 0; // 合計点(初期値:0) @@ -339,7 +339,7 @@ scanf("%lf", &a[1]); ```c title="16_issue1.c" #include -int main(int argc, const char * argv[]) { +int main(void) { double heights[5]; // 身長の入力 @@ -402,7 +402,7 @@ if (min > arr[i]) { ```c title="16_issue2.c" #include -int main(int argc, const char * argv[]) { +int main(void) { int arr[] = {170, 168, 186, 152, 171}; // 最小値と最大値(初期値として適当な要素を格納しておく) diff --git a/src/content/docs/textbook/c-lang/beginner/17--character-string.mdx b/src/content/docs/textbook/c-lang/beginner/17--character-string.mdx index 2bf2153..7594c83 100644 --- a/src/content/docs/textbook/c-lang/beginner/17--character-string.mdx +++ b/src/content/docs/textbook/c-lang/beginner/17--character-string.mdx @@ -48,7 +48,7 @@ char str[] = "abcde"; // {'a', 'b', 'c', 'd', 'e', '\0'} と同じ ```c #include -int main(int argc, const char * argv[]) { +int main(void) { // "hoge" char str[] = {'h', 'o', 'g', 'e', '\0'}; @@ -155,7 +155,7 @@ for (int i = ******; ******; i--) { ```c #include -int main(int argc, const char * argv[]) { +int main(void) { char str[] = "the quick brown fox jumps over the lazy dog"; // 文字数 diff --git a/src/content/docs/textbook/c-lang/beginner/18--multidimensional-array.mdx b/src/content/docs/textbook/c-lang/beginner/18--multidimensional-array.mdx index 303c225..bbb4168 100644 --- a/src/content/docs/textbook/c-lang/beginner/18--multidimensional-array.mdx +++ b/src/content/docs/textbook/c-lang/beginner/18--multidimensional-array.mdx @@ -77,7 +77,7 @@ int a[3][4]; // 上記の配列を4個まとめて配列化 #define ROWS 9 // 行数 #define COLS 9 // 列数 -int main(int argc, const char * argv[]) { +int main(void) { int table[9][9]; // 二次元配列の初期化 @@ -168,7 +168,7 @@ for (int i = 0; i < 3; i++) { ```c #include -int main(int argc, const char * argv[]) { +int main(void) { int scores[3][5] = { {58, 46, 100, 89, 64}, // 学籍番号 0001 {98, 96, 32, 38, 72}, // 学籍番号 0002 diff --git a/src/content/docs/textbook/c-lang/beginner/19--function-definition.mdx b/src/content/docs/textbook/c-lang/beginner/19--function-definition.mdx index 87eed64..cb6061a 100644 --- a/src/content/docs/textbook/c-lang/beginner/19--function-definition.mdx +++ b/src/content/docs/textbook/c-lang/beginner/19--function-definition.mdx @@ -41,7 +41,7 @@ int max2(int n1, int n2) { return n2; } -int main(int argc, const char * argv[]) { +int main(void) { int a = 3; int b = 7; @@ -97,7 +97,7 @@ int max4(int n1, int n2, int n3, int n4) { return max2(max2(n1, n2), max2(n3, n4)); } -int main(int argc, const char * argv[]) { +int main(void) { int a = 3; int b = 7; int c = 2; @@ -162,7 +162,7 @@ void putStars(int n) { putchar('\n'); // 改行 } -int main(int argc, const char * argv[]) { +int main(void) { putStars(scanN()); return 0; @@ -222,7 +222,7 @@ int scanN(void) { return ***; } -int main(int argc, const char * argv[]) { +int main(void) { int n = scanN(); printf("%d! = %d\n", n, factorial(n)); @@ -291,7 +291,7 @@ int factorial(int n) { return ans; } -int main(int argc, const char * argv[]) { +int main(void) { int n = scanN(); printf("%d! = %d\n", n, factorial(n)); diff --git a/src/content/docs/textbook/c-lang/beginner/20--recursive-functio-call.mdx b/src/content/docs/textbook/c-lang/beginner/20--recursive-functio-call.mdx index b855cad..12c601a 100644 --- a/src/content/docs/textbook/c-lang/beginner/20--recursive-functio-call.mdx +++ b/src/content/docs/textbook/c-lang/beginner/20--recursive-functio-call.mdx @@ -49,7 +49,7 @@ int factorial(int n) { return 1; } -int main(int argc, const char * argv[]) { +int main(void) { int n = 3; printf("%d! = %d\n", n, factorial(n)); @@ -103,7 +103,7 @@ int combination(int n, int r) { // TODO } -int main(int argc, const char * argv[]) { +int main(void) { int n = ***, r = ***; printf("C(%d, %d) = %d\n", n, r, combination(n, r)); @@ -161,7 +161,7 @@ int combination(int n, int r) { return combination(n - 1, r - 1) + combination(n - 1, r); } -int main(int argc, const char * argv[]) { +int main(void) { int n = 6, r = 3; printf("C(%d, %d) = %d\n", n, r, combination(n, r)); diff --git a/src/content/docs/textbook/c-lang/beginner/21--tic-tac-toe.mdx b/src/content/docs/textbook/c-lang/beginner/21--tic-tac-toe.mdx index 3aa031e..c331232 100644 --- a/src/content/docs/textbook/c-lang/beginner/21--tic-tac-toe.mdx +++ b/src/content/docs/textbook/c-lang/beginner/21--tic-tac-toe.mdx @@ -85,7 +85,7 @@ import { Steps } from '@astrojs/starlight/components'; // /* Step.6 にて記述 */ - int main(int argc, const char * argv[]) { + int main(void) { // 盤を表す2次元配列 int board[BOARD_SIZE][BOARD_SIZE] = { {BLANK, BLANK, BLANK}, @@ -426,7 +426,7 @@ int checkWin(int board[BOARD_SIZE][BOARD_SIZE], int mark) { return 0; } -int main(int argc, const char * argv[]) { +int main(void) { // 盤を表す2次元配列 int board[BOARD_SIZE][BOARD_SIZE] = { {BLANK, BLANK, BLANK}, From a0046662beb7f1f51e6f2d72d5feefbca40e011f Mon Sep 17 00:00:00 2001 From: Yukito-Aomi Date: Sun, 18 May 2025 23:10:38 +0900 Subject: [PATCH 06/10] update: Chapter18 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Chapter 18 - 二次元配列の図の追加 - 練習問題の修正 - プログラム番号の追加 --- public/chap18_multi_arr.drawio.svg | 4 + .../textbook/c-lang/beginner/16--array.mdx | 2 +- .../beginner/18--multidimensional-array.mdx | 141 +++++++++++------- 3 files changed, 92 insertions(+), 55 deletions(-) create mode 100644 public/chap18_multi_arr.drawio.svg diff --git a/public/chap18_multi_arr.drawio.svg b/public/chap18_multi_arr.drawio.svg new file mode 100644 index 0000000..84e7776 --- /dev/null +++ b/public/chap18_multi_arr.drawio.svg @@ -0,0 +1,4 @@ + + + +
c [ 1 ]
c [ 1 ]
c [ 0 ]
c [ 0 ]
c [ 1 ] [ 2 ]
c [ 1 ] [ 2 ]
c [ 1 ] [ 1 ]
c [ 1 ] [ 1 ]
c [ 1 ] [ 0 ]
c [ 1 ] [ 0 ]
c [ 0 ] [ 2 ]
c [ 0 ] [ 2 ]
c [ 0 ] [ 1 ]
c [ 0 ] [ 1 ]
c [ 0 ] [ 0 ]
c [ 0 ] [ 0 ]
c の要素は2個
c の要素は2個
c [ 0 ] の要素は3個
c [ 0 ] の要素は3個
c [ 1 ] の要素は3個
c [ 1 ] の要素は3個
int  c [ 2 ] [ 3 ] ;
int  c [ 2 ] [ 3...
構成要素は6個
構成要素は6個
Text is not SVG - cannot display
\ No newline at end of file diff --git a/src/content/docs/textbook/c-lang/beginner/16--array.mdx b/src/content/docs/textbook/c-lang/beginner/16--array.mdx index ffc9318..1daed78 100644 --- a/src/content/docs/textbook/c-lang/beginner/16--array.mdx +++ b/src/content/docs/textbook/c-lang/beginner/16--array.mdx @@ -16,7 +16,7 @@ import { Card } from '@astrojs/starlight/components'; --- -まずは、3人の学生の点数について、その合計値と平均値を算出するプログラム(`list16_01.c`)を作成してみましょう。 +まずは、3人の学生の点数について、その合計値と平均値を算出するプログラム`list16_01.c`を作成してみましょう。 ```c title="list16_01.c" #include diff --git a/src/content/docs/textbook/c-lang/beginner/18--multidimensional-array.mdx b/src/content/docs/textbook/c-lang/beginner/18--multidimensional-array.mdx index bbb4168..a76f78e 100644 --- a/src/content/docs/textbook/c-lang/beginner/18--multidimensional-array.mdx +++ b/src/content/docs/textbook/c-lang/beginner/18--multidimensional-array.mdx @@ -42,6 +42,8 @@ int c[2][3] = { }; ``` +![](/public/chap18_multi_arr.drawio.svg) + -## 文字列の入出力 -文字列の出力(印字)は次のようにして実現します。 +## 文字および文字列の入出力 +--- + +単一の文字の表示のために使う関数が`putchar`関数になります。 +実引数として、`()`の中には、表示すべき文字を与える必要があります。 - - + + +文字列の出力(印字)は次のようにして実現します。 + +```c title="list17_01.c" #include int main(void) { @@ -64,15 +81,13 @@ int main(void) { return 0; } ``` - - + +**実行結果** ``` str: hoge str[0]: h o ``` - - また、文字列の入力は次のようにして実現します。 @@ -87,6 +102,7 @@ scanf("%s", str); scanf("%c", &str[0]); ``` + ## 文字列の代入 --- @@ -110,102 +126,140 @@ str[4] = 'e'; str[5] = '\0'; ``` - - -文字列リテラル`"the quick brown fox jumps over the lazy dog"`について、全体の文字数`len`および空白(`" "`)を除いた文字数`cnt`をカウントして求めてください。 -また、`for`文を用いて、文字列リテラル内の文字を逆順に印字してください。 -```c -char str[] = "the quick brown fox jumps over the lazy dog"; + +### 練習問題1 + +以下の文字列リテラルについて、空白(`' '`)を除いた文字数をカウントして出力してください。 -int len = 0; -int cnt = 0; -``` - - ``` -len: 43 -cnt: 35 -god yzal eht revo spmuj xof nworb kciuq eht +"the quick brown fox jumps over the lazy dog" ``` - - +**実行結果** +``` +文字数: 35 +``` +ファイル名: `17_issue1.c` +
+ヒント(文字列リテラルの走査) + +
+
+模範解答 +```c title="17_issue1.c" #include int main(void) { char str[] = "the quick brown fox jumps over the lazy dog"; // 文字数 - int len = 0; int cnt = 0; // ヌル文字になるまで文字列リテラルを走査 for (int i = 0; str[i] != '\0'; i++) { - len++; if (str[i] != ' ') { // 空白ではない場合 cnt++; } } - printf("len: %d\n", len); - printf("cnt: %d\n", cnt); - - // 逆順に印字 - for (int i = len - 1; i >= 0; i--) { - putchar(str[i]); - } - - putchar('\n'); // 改行 + printf("文字数: %d\n", cnt); return 0; } ``` - - -{/* -### 練習問題******(問題番号) +
+ -******(問題文) + + +### 練習問題2 + +キーボードから入力された文字列の文字数をカウントしてください。 +また、入力された文字列を逆順に出力してください。 **実行結果** ``` -******(実行結果) +str? nyancat +文字数: 7 +tacnayn ``` -ファイル名: `17_issue******(問題番号).c` +ファイル名: `17_issue2.c`
-ヒント +ヒント(文字列の入力) +
+
+ヒント(文字列を逆順に出力) +
模範解答 -```c title="17_issue******(問題番号).c" -******(模範解答) +```c title="17_issue2.c" +#include + +int main(void) { + char str[128]; + int len = 0; // 文字数 + + // 文字列の入力(%s) + printf("str? "); + scanf("%s", str); + + // 文字数のカウント + for (int i = 0; str[i] != '\0'; i++) { + len++; + } + printf("文字数: %d\n", len); + + // 逆順に印字 + for (int i = len - 1; i >= 0; i--) { + putchar(str[i]); + } + putchar('\n'); // 改行 + + return 0; +} ```
-
*/} \ No newline at end of file + From 46cfb2b85856043951efd6ece218e2ff88511ff9 Mon Sep 17 00:00:00 2001 From: Yukito-Aomi Date: Mon, 19 May 2025 03:05:40 +0900 Subject: [PATCH 09/10] update: Chapter19 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Chapter 19 - 練習問題の修正 - プログラム番号の追加 --- .../beginner/19--function-definition.mdx | 357 ++++++++++++------ 1 file changed, 243 insertions(+), 114 deletions(-) diff --git a/src/content/docs/textbook/c-lang/beginner/19--function-definition.mdx b/src/content/docs/textbook/c-lang/beginner/19--function-definition.mdx index cb6061a..6826766 100644 --- a/src/content/docs/textbook/c-lang/beginner/19--function-definition.mdx +++ b/src/content/docs/textbook/c-lang/beginner/19--function-definition.mdx @@ -5,11 +5,14 @@ slug: http://localhost:4321/textbook/c-lang/beginner/function-definition import { Aside } from '@astrojs/starlight/components'; import { Tabs, TabItem } from '@astrojs/starlight/components'; +import { Card } from '@astrojs/starlight/components'; + プログラムは、多くの機能の組合わせによって構成されます。 ひとつひとつの機能を提供する基本単位が関数になります。 自作関数の設計について学んでいきましょう。 + ## 関数定義と関数呼出し --- @@ -24,14 +27,16 @@ import { Tabs, TabItem } from '@astrojs/starlight/components'; ```c 返却値型 関数名(仮引数型並び) { 処理の内容 + return 返却値 } ``` +なお、呼び出される関数は、呼び出す箇所よりも上の行に記述される必要があります。 +そのため、関数定義は`main`関数よりも上の行に記述しましょう。 + 例として、2つの整数値を受け取り、最大値を返す関数を作成してみましょう。 - - -```c +```c title="list19_01.c" #include int max2(int n1, int n2) { @@ -50,19 +55,17 @@ int main(void) { return 0; } ``` - - + +**実行結果** ``` max: 7 ``` - - `main`関数上に、`max2`関数を定義しました。 `main`関数内にて、実引数`a, b`を与えて`max2`関数を呼び出しています。 次に、仮引数`n1, n2`には実引数の値が代入され、自作関数内の処理へと進みます。 -最後に、関数呼出しの式`max2(a, b)`は、2値の比較によって返却された値に置換され、翻訳・実行されます。 +最後に、関数呼出しの式`max2(a, b)`は、2値の比較によって返却された値に置換され、実行されます。 また、`return`文は関数の実行を終了して、プログラムの流れを呼出し元に戻すとともに値を返却する仕組みであることを覚えておきましょう。 @@ -70,20 +73,20 @@ max: 7 ```c 返却値型 関数名(仮引数型並び) { 処理の内容 + return 返却値 } ``` + ## 自作関数内での関数呼出し --- 関数を組み合わせることで、プログラムを少ない記述量で実現することができます。 -例として、次の4つの整数値の最大値を求めるプログラムをみてみましょう。 +例として、次の4つの整数値の最大値を求めるプログラム`list17_02`をみてみましょう。 - - -```c +```c title="list19_02.c" #include int max2(int n1, int n2) { @@ -108,13 +111,11 @@ int main(void) { return 0; } ``` - - + +**実行結果** ``` max: 7 ``` - - プログラムの処理の流れは、次のとおりです。 @@ -126,18 +127,14 @@ max: 7 プログラムの処理の流れと戻り値に注意して、関数を活用していきましょう。 -## 値を返さない関数・引数を受け取らない関数 ---- - -値を返さない関数を定義する場合、関数型を`void`(『空の』を意味します)とします。 -また、受け取る仮引数がない関数を定義する場合も、仮引数型並びを`void`とします。 +## 引数を受け取らない関数 +--- -実際に、次のプログラムを確認して、`void`の使い方を確認しましょう。 +受け取る仮引数がない関数を定義する場合、仮引数型並びを`void`(『空の』を意味します)とします。 +実際に、次のプログラム`list19_03.c`を確認してみましょう。 - - -```c +```c title="list19_03.c" #include // 自然数を標準入力によって受け取り返却する関数 @@ -149,59 +146,57 @@ int scanN(void) { scanf("%d", &n); if (n <= 0) - puts("error!"); + printf("error!\n"); } while (n <= 0); return n; } -// 与えた整数値だけ * を出力する関数 -void putStars(int n) { - for (int i = 0; i < n; i++) - putchar('*'); - putchar('\n'); // 改行 -} - int main(void) { - putStars(scanN()); + // 関数の呼出し + int n = scanN(); + + printf("n: %d\n", n); return 0; } ``` - - + +**実行結果** ``` -natural number? 0(注:標準入力) +natural number? 0 error! -natural number? -3(注:標準入力) +natural number? -3 error! -natural number? 5(注:標準入力) -***** +natural number? 5 +n: 5 ``` - - - -`printStars`関数は、与えた整数値だけ`*`を出力する関数になります。 -そのため、戻り値は存在しません。 `scanN`関数は、主に自然数を標準入力によって受け取り返却する関数になります。 そのため、仮引数を必要としません。 -上記のような関数を設計する場合、`void`を記述することを忘れないようにしましょう。 +このような関数を設計する場合、仮引数並びとして`void`を記述することを忘れないようにしましょう。 -## 練習問題 ---- + - - -自然数`n`を受け取り、その階乗`n!`を算出する関数`factorial`を作成してください。 -また、自然数の受け取りは、前述の`scanN`関数を利用してください。 -なお、階乗`n!`とは、`1`から`n`までの自然数の積のことを指します。 +## 値を返さない関数 +--- -```c +値を返さない関数を定義する場合も、関数型を`void`とします。 +実際に、次のプログラム`list19_04.c`を確認してみましょう。 + +```c title="list19_04.c" #include +// 自然数を標準入力によって受け取り返却する関数 int scanN(void) { int n; @@ -210,117 +205,251 @@ int scanN(void) { scanf("%d", &n); if (n <= 0) - puts("error!"); + printf("error!\n"); } while (n <= 0); return n; } -*** factorial(*** ***) { - // TODO - - return ***; +// 与えた整数値だけ * を出力する関数 +void putStars(int n) { + for (int i = 0; i < n; i++) + putchar('*'); + putchar('\n'); // 改行 } int main(void) { - int n = scanN(); - - printf("%d! = %d\n", n, factorial(n)); + putStars(scanN()); return 0; } ``` - - + +**実行結果** ``` -natural number? 4(注:標準入力) -4! = 24 +natural number? 0 +error! +natural number? -3 +error! +natural number? 5 +***** ``` - - -返却値型を`int`、仮引数を`int n`として、関数`factorial`を設計しましょう。 +`printStars`関数は、与えた整数値だけ`*`を出力する関数になります。 +そのため、戻り値は存在しません。 + +このような関数を設計する場合、返却値型として`void`を記述することを忘れないようにしましょう。 + + + + +## 関数原型宣言 +--- + +コンパイラがプログラムを先頭から末尾へと読み進める都合上、関数の定義は、その関数を呼び出す箇所よりも上の行に記述する必要がありました。 +実は、関数を宣言することによって、関数の定義を呼出し箇所よりも下の行に記述することができます。 + +この宣言は、関数の使用ともいうべき、関数の返却値型/関数名/仮引数を記述する必要があることから、**関数原型宣言**(function prototype declaration)と呼ばれます。 + +実際に、プログラム`list19_01.c`を、関数原型宣言を用いて書き換えましょう。 + +```c title="list19_05.c" {3} +#include + +int max2(int, int); + +int main(void) { + int a = 3; + int b = 7; - // TODO + printf("max: %d\n", max2(a, b)); - return ans; + return 0; +} + +int max2(int n1, int n2) { + if (n1 > n2) + return n1; + else + return n2; } ``` - - -`n!`を、繰返し処理によって実現しましょう。 -返却値`ans`の初期値として、`1`を与えることに注意しましょう。 + +関数の使用に関する宣言を`main`関数の上部に記述したことで、`main`関数の下部に関数の実体の定義を記述することができました。 + + + +### 練習問題1 + +次のプログラムの`TODO`を補うことで、実行結果に合うように関数を呼出してください。 ```c -int ans = 1; // 初期値を 1 とする +#include -// すべての自然数の積を算出する -for (int i = 1; i <= n; i++) - ans *= i; +// 与えた整数値だけ * を出力する関数 +void putStars(int n) { + for (int i = 0; i < n; i++) + putchar('*'); + putchar('\n'); // 改行 +} + +int main(void) { + int n = 5; + + // TODO: ループ文などを用いて putStars 関数を呼出してください + + return 0; +} +``` + +**実行結果** +`n = 1`のとき、 +``` +* +``` + +`n = 3`のとき、 ``` - - +* +** +*** +``` + +`n = 5`のとき、 +``` +* +** +*** +**** +***** +``` +ファイル名: `19_issue1.c` +
+ヒント + +
+
+模範解答 +```c title="19_issue1.c" #include -// 自然数を標準入力によって受け取り返却する関数 -int scanN(void) { - int n; +// 与えた整数値だけ * を出力する関数 +void putStars(int n) { + for (int i = 0; i < n; i++) + putchar('*'); + putchar('\n'); // 改行 +} + +int main(void) { + int n = 5; - do { - printf("natural number? "); - scanf("%d", &n); - - if (n <= 0) - puts("error!"); - } while (n <= 0); + for (int i = 1; i <= n; i++) { + putStars(i); + } - return n; + return 0; } +``` +
+ + + + +### 練習問題2 + +自然数`n`を受け取り、その階乗`n!`を算出する関数`factorial`を作成してください。 + +なお、階乗`n!`とは、`1`から`n`までの自然数の積のことを指します。 + +```c +#include // 与えられた整数値の階乗を求める関数 int factorial(int n) { - int ans = 1; // 初期値を 1 とする - - for (int i = 1; i <= n; i++) - ans *= i; - - return ans; + // TODO: ここにプログラムを記述してください +} + +void issue1902(int n) { + printf("%d! = %d\n", n, factorial(n)); } int main(void) { - int n = scanN(); + int n = 5; - printf("%d! = %d\n", n, factorial(n)); + issue1902(n); return 0; } ``` -
-
-{/* -### 練習問題******(問題番号) - -******(問題文) **実行結果** ``` -******(実行結果) +1! = 1 +``` +``` +5! = 120 ``` -ファイル名: `19_issue******(問題番号).c` +``` +10! = 3628800 +``` +ファイル名: `19_issue2.c`
ヒント
模範解答 -```c title="19_issue******(問題番号).c" -******(模範解答) +```c title="19_issue2.c" +#include + +// 与えられた整数値の階乗を求める関数 +int factorial(int n) { + int ans = 1; // 初期値を 1 とする + + for (int i = 1; i <= n; i++) { + ans *= i; + } + + return ans; +} + +void issue1902(int n) { + printf("%d! = %d\n", n, factorial(n)); +} + +int main(void) { + int n = 5; + + issue1902(n); + + return 0; +} ```
-
*/} \ No newline at end of file + From eae9b7c27c63aaf579b6087d54760f36566a4cbb Mon Sep 17 00:00:00 2001 From: Yukito-Aomi Date: Mon, 19 May 2025 03:10:51 +0900 Subject: [PATCH 10/10] update: Chapter21 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Chapter 21 - next: false の記述 - 記述漏れの補完 --- .../docs/textbook/c-lang/beginner/21--tic-tac-toe.mdx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/content/docs/textbook/c-lang/beginner/21--tic-tac-toe.mdx b/src/content/docs/textbook/c-lang/beginner/21--tic-tac-toe.mdx index c331232..ffd539a 100644 --- a/src/content/docs/textbook/c-lang/beginner/21--tic-tac-toe.mdx +++ b/src/content/docs/textbook/c-lang/beginner/21--tic-tac-toe.mdx @@ -1,14 +1,17 @@ --- title : 「まるばつゲーム」の作成 slug: http://localhost:4321/textbook/c-lang/beginner/tic-tac-toe +next: false --- import { Aside } from '@astrojs/starlight/components'; import { Tabs, TabItem } from '@astrojs/starlight/components'; import { Steps } from '@astrojs/starlight/components'; + これまで学習してきたことを踏まえて、簡単な「まるばつゲーム」を作成していきましょう。 + ## アルゴリズム --- @@ -40,6 +43,7 @@ import { Steps } from '@astrojs/starlight/components'; 6. ゲーム終了 + ## プログラムの設計 --- @@ -55,6 +59,7 @@ import { Steps } from '@astrojs/starlight/components'; アルゴリズムにのっとり、これらを組み合わせていくことで、「まるばつゲーム」を作成していきましょう。 + ## プログラムの作成 --- @@ -82,7 +87,7 @@ import { Steps } from '@astrojs/starlight/components'; // 任意のマークをカウントする関数 /* Step.5 にて記述 */ - // + // 勝利判定用の関数 /* Step.6 にて記述 */ int main(void) { @@ -327,6 +332,7 @@ import { Steps } from '@astrojs/starlight/components'; また、`break`文によって`while`文を脱出して、勝敗結果を表示するフェーズまでプログラムを進行させます。 + ## 完成品 ---