diff --git a/tutorials/learn-c.org/ja/Arrays and Pointers.md b/tutorials/learn-c.org/ja/Arrays and Pointers.md new file mode 100644 index 00000000..4dc30cd3 --- /dev/null +++ b/tutorials/learn-c.org/ja/Arrays and Pointers.md @@ -0,0 +1,234 @@ +Tutorial +-------- + +以前の[[ポインタ]]に関するチュートリアルでは、特定のデータ型へのポインタは、そのデータ型の任意の変数のアドレスを格納できることを学びました。例えば、次のコードでは、ポインタ変数 `pc` は文字変数 `c` のアドレスを格納しています。 + + char c = 'A'; + char *pc = &c; + +ここで、`c` は単一の値しか格納できないスカラー変数です。しかし、連続して割り当てられたメモリブロックに同じデータ型の複数の値を保持できる配列については既にご存知でしょう。では、配列へのポインタも使用できるのだろうかと疑問に思うかもしれません。はい、可能です。 + +まずはサンプルコードとその出力を見てみましょう。その後、その動作について説明します。 + + char vowels[] = {'A', 'E', 'I', 'O', 'U'}; + char *pvowels = vowels; + int i; + + // 住所を印刷する + for (i = 0; i < 5; i++) { + printf("&vowels[%d]: %p, pvowels + %d: %p, vowels + %d: %p\n", i, &vowels[i], i, pvowels + i, i, vowels + i); + } + + // 値を印刷する + for (i = 0; i < 5; i++) { + printf("vowels[%d]: %c, *(pvowels + %d): %c, *(vowels + %d): %c\n", i, vowels[i], i, *(pvowels + i), i, *(vowels + i)); + } + +上記コードの典型的な出力を以下に示します。 + +>&vowels[0]: 0x7ffee146da17, pvowels + 0: 0x7ffee146da17, vowels + 0: 0x7ffee146da17 +> +>&vowels[1]: 0x7ffee146da18, pvowels + 1: 0x7ffee146da18, vowels + 1: 0x7ffee146da18 +> +>&vowels[2]: 0x7ffee146da19, pvowels + 2: 0x7ffee146da19, vowels + 2: 0x7ffee146da19 +> +>&vowels[3]: 0x7ffee146da1a, pvowels + 3: 0x7ffee146da1a, vowels + 3: 0x7ffee146da1a +> +>&vowels[4]: 0x7ffee146da1b, pvowels + 4: 0x7ffee146da1b, vowels + 4: 0x7ffee146da1b +> +>vowels[0]: A, \*(pvowels + 0): A, *(vowels + 0): A +> +>vowels[1]: E, \*(pvowels + 1): E, *(vowels + 1): E +> +>vowels[2]: I, \*(pvowels + 2): I, *(vowels + 2): I +> +>vowels[3]: O, \*(pvowels + 3): O, *(vowels + 3): O +> +>vowels[4]: U, \*(pvowels + 4): U, *(vowels + 4): U + + + +ご想像のとおり、`&vowels[i]` は配列 `vowels` の *i* 番目の要素のメモリ位置を表します。さらに、これは文字配列であるため、各要素は 1 バイトを占有し、連続するメモリアドレスは 1 バイトで区切られます。また、ポインタ `pvowels` を作成し、配列 `vowels` のアドレスをそれに代入しました。`pvowels + i` は有効な演算ですが、一般的には必ずしも意味を持つとは限りません ([[ポインタ演算]] で詳しく説明します)。特に、上記の出力は `&vowels[i]` と `pvowels + i` が同等であることを示しています。配列変数とポインタ変数のデータ型を自由に変更して、これをテストしてみてください。 + +前のコードをよく見ると、`vowels + i` という、一見意外な記法も使われていることに気づくでしょう。さらに、`pvowels + i` と `vowels + i` は同じもの、つまり配列 `vowels` の *i* 番目の要素のアドレスを返します。一方、`*(pvowels + i)` と `*(vowels + i)` はどちらも配列 `vowels` の *i* 番目の要素を返します。なぜでしょうか? + +これは、配列の名前自体が配列の最初の要素への(定数)ポインタであるためです。つまり、`vowels`、`&vowels[0]`、`vowels + 0` という記法はすべて同じ位置を指しているのです。 + +配列の動的メモリ割り当て +------------------------ + +ここまでで、ポインタを使って配列を走査できることをご理解いただけたと思います。さらに、ブロックポインタを使って(連続した)メモリを動的に確保できることも分かりました。これら2つの要素を組み合わせることで、配列のメモリを動的に確保することができます。これは以下のコードで示されています。 + + // 5文字を格納するためのメモリを割り当てる + int n = 5; + char *pvowels = (char *) malloc(n * sizeof(char)); + int i; + + pvowels[0] = 'A'; + pvowels[1] = 'E'; + *(pvowels + 2) = 'I'; + pvowels[3] = 'O'; + *(pvowels + 4) = 'U'; + + for (i = 0; i < n; i++) { + printf("%c ", pvowels[i]); + } + + printf("\n"); + + free(pvowels); + +上記のコードでは、5文字を格納するために連続する5バイトのメモリを割り当てました。その後、配列表記法を使用して、`pvowels` が配列であるかのようにメモリブロックを走査しました。ただし、`pvowels` は実際にはポインタであることに注意してください。一般的に、ポインタと配列は同じではありません。 + +では、これはどのような場合に便利なのでしょうか? 配列を宣言する際には、その要素数を事前に把握しておく必要があることを覚えておいてください。そのため、場合によっては、配列に割り当てられる領域が目的の領域よりも少なかったり、多すぎたりすることがあります。しかし、動的メモリ割り当てを使用すれば、プログラムに必要なだけのメモリを割り当てることができます。さらに、未使用のメモリは不要になったらすぐに「free()」関数を呼び出して解放できます。ただし、動的メモリ割り当ての欠点は、必要な場合に「free()」を責任を持って呼び出す必要があることです。そうしないと、メモリリークが発生します。 + +このチュートリアルの最後に、2次元配列の動的メモリ割り当てについて見ていきます。これは同様の方法でn次元配列にも一般化できます。1次元配列ではポインタを使用していましたが、この場合は以下に示すように、ポインタへのポインタが必要になります。 + + int nrows = 2; + int ncols = 5; + int i, j; + + // nrowsポインタにメモリを割り当てる + char **pvowels = (char **) malloc(nrows * sizeof(char *)); + + // 各行にncols要素のメモリを割り当てる + pvowels[0] = (char *) malloc(ncols * sizeof(char)); + pvowels[1] = (char *) malloc(ncols * sizeof(char)); + + pvowels[0][0] = 'A'; + pvowels[0][1] = 'E'; + pvowels[0][2] = 'I'; + pvowels[0][3] = 'O'; + pvowels[0][4] = 'U'; + + pvowels[1][0] = 'a'; + pvowels[1][1] = 'e'; + pvowels[1][2] = 'i'; + pvowels[1][3] = 'o'; + pvowels[1][4] = 'u'; + + for (i = 0; i < nrows; i++) { + for(j = 0; j < ncols; j++) { + printf("%c ", pvowels[i][j]); + } + + printf("\n"); + } + + // 個々の行を解放する + free(pvowels[0]); + free(pvowels[1]); + + // トップレベルのポインタを解放する + free(pvowels); + + +Exercise +-------- + +[パスカルの三角形](http://mathworld.wolfram.com/PascalsTriangle.html)の最初の7行を以下に示します。行*i*には*i*個の要素が含まれていることに注意してください。したがって、最初の3行の数値を格納するには、1 + 2 + 3 = 6個のメモリスロットが必要になります。 + +>1 +> +>1 1 +> +>1 2 1 +> +>1 3 3 1 +> +>1 4 6 4 1 +> +>1 5 10 10 5 1 +> +>1 6 15 20 15 6 1 + +パスカルの三角形の最初の3行の数値を、動的メモリ割り当てを用いて、2次元「配列」に格納するためのスケルトンコードを以下に示す。これらの6つの数値を格納するには、正確に6つのメモリスロットを割り当てる必要があることに注意する。余分なメモリは割り当てない。プログラムの最後に、このプログラムで使用したすべてのメモリブロックを解放する。 + + +Tutorial Code +------------- + + #include + #include + + int main() { + int i, j; + /* TODO: ここで2Dポインタ変数を定義する */ + + /* TODO: 3行を保持するためのメモリを割り当てるには、次の行を完成させる */ + pnumbers = (int **) malloc(); + + /* TODO: 行内の個々の要素を格納するためのメモリを割り当てる */ + pnumbers[0] = (int *) malloc(1 * sizeof(int)); + + pnumbers[0][0] = 1; + pnumbers[1][0] = 1; + pnumbers[1][1] = 1; + pnumbers[2][0] = 1; + pnumbers[2][1] = 2; + pnumbers[2][2] = 1; + + for (i = 0; i < 3; i++) { + for (j = 0; j <= i; j++) { + printf("%d", pnumbers[i][j]); + } + printf("\n"); + } + + for (i = 0; i < 3; i++) { + /* TODO: 各行に割り当てられたメモリを解放する */ + } + + /* TODO: トップレベルのポインタを解放する */ + + return 0; + } + +Expected Output +--------------- + + 1 + 11 + 121 + +Solution +---- + + #include + #include + + int main() { + int i, j; + /* TODO: 行内の個々の要素を格納するためのメモリを割り当てる */ + int **pnumbers; + + /* TODO: 3行を保持するためのメモリを割り当てるには、次の行を完成させる */ + pnumbers = (int **) malloc(3 *sizeof(int *)); + + /* TODO: 行内の個々の要素を格納するためのメモリを割り当てる */ + pnumbers[0] = (int *) malloc(1 * sizeof(int)); + pnumbers[1] = (int *) malloc(2 * sizeof(int)); + pnumbers[2] = (int *) malloc(3 * sizeof(int)); + + pnumbers[0][0] = 1; + pnumbers[1][0] = 1; + pnumbers[1][1] = 1; + pnumbers[2][0] = 1; + pnumbers[2][1] = 2; + pnumbers[2][2] = 1; + + for (i = 0; i < 3; i++) { + for (j = 0; j <= i; j++) { + printf("%d", pnumbers[i][j]); + } + printf("\n"); + } + + for (i = 0; i < 3; i++) { + free(pnumbers[i]); + } + + free(pnumbers); + + return 0; + } diff --git a/tutorials/learn-c.org/ja/Arrays.md b/tutorials/learn-c.org/ja/Arrays.md new file mode 100644 index 00000000..9c938708 --- /dev/null +++ b/tutorials/learn-c.org/ja/Arrays.md @@ -0,0 +1,79 @@ +Tutorial +-------- + +配列は、同じ変数名で複数の値をインデックスで整理して保持できる特殊な変数です。配列は非常に簡単な構文で定義されます。 + + /* 10個の整数の配列を定義する */ + int numbers[10]; + +配列から数値にアクセスする場合も同じ構文を使用します。C言語の配列は0から始まります。つまり、サイズが10の配列を定義した場合、配列のセル0から9(両端を含む)が定義されます。`numbers[10]` は有効な値を持ちません。 + + int numbers[10]; + + /* populate the array */ + numbers[0] = 10; + numbers[1] = 20; + numbers[2] = 30; + numbers[3] = 40; + numbers[4] = 50; + numbers[5] = 60; + numbers[6] = 70; + + /* 配列のインデックスが6である7番目の数値を出力します */ + printf("The 7th number in the array is %d", numbers[6]); + +配列はコンピュータのメモリ内で、『値が等間隔に整列したもの』として実装されるため、1種類の変数しか持つことができません。 +そのため、特定の配列セルへのアクセスは非常に効率的です。 + +Exercise +-------- + +* 以下のコードは `grades` 変数が欠落しているためコンパイルできません。 +* 成績の1つが欠落しています。成績平均が85になるように定義できますか? + +Tutorial Code +------------- + + #include + + int main() { + /* TODO: ここでgrades変数を定義します */ + int average; + + grades[0] = 80; + /* TODO: 欠落している成績を + 平均が85になるように定義します。 */ + grades[2] = 90; + + average = (grades[0] + grades[1] + grades[2]) / 3; + printf("The average of the 3 grades is: %d", average); + + return 0; + } + +Expected Output +--------------- + + The average of the 3 grades is: 85 + +Solution +---- + + #include + + int main() { + /* TODO: ここでgrades変数を定義します */ + int grades[3]; + int average; + + grades[0] = 80; + /* TODO: 欠落している成績を + 平均が85になるように定義します。 */ + grades[1] = 85; + grades[2] = 90; + + average = (grades[0] + grades[1] + grades[2]) / 3; + printf("The average of the 3 grades is: %d", average); + + return 0; + } diff --git a/tutorials/learn-c.org/ja/Binary trees.md b/tutorials/learn-c.org/ja/Binary trees.md new file mode 100644 index 00000000..eeb489c5 --- /dev/null +++ b/tutorials/learn-c.org/ja/Binary trees.md @@ -0,0 +1,212 @@ +Tutorial +-------- + +### Introduction + +二分木は、各ノードが最大 2 つの子 (左の子と右の子) を持つデータ構造のタイプです。二分木は、二分探索木と二分ヒープの実装に使用され、効率的な検索とソートに使用されます。二分木は、k が 2 である K 分木の特殊なケースです。二分木の一般的な操作には、挿入、削除、トラバーサルがあります。これらの操作を実行する難易度は、木がバランスが取れているかどうか、またノードがリーフ ノードであるかブランチ ノードであるかによって異なります。**バランスの取れた木** では、各ノードの左と右のサブツリーの深さの差は 1 以下です。これにより、**深さ** (高さ** とも呼ばれる) を予測できるようになります。これは、ルートからリーフまでのノードの測定値で、ルートが 0、後続のノードが (1,2..n) です。これは、log2(n) の整数部分で表すことができます。ここで、n は木内のノードの数です。 + + g s 9 + / \ / \ / \ + b m f u 5 13 + / \ / \ / \ + c d t y 11 15 + +ツリーに対して実行される操作には、深さ優先探索と幅優先探索という 2 つの主な方法のいずれかによる検索が必要です。**深さ優先探索 (DFS)** は、ツリーまたはグラフのデータ構造を走査または検索するためのアルゴリズムです。ルートから開始し、各ブランチに沿ってあり能な限り探索してからバックトラックします。深さ優先探索の走査には、**前順序** 訪問、左、右、**内順序** 訪問、右、**後順序** 左、右、訪問の 3 つの種類があります。**幅優先探索 (BFS)** は、ツリーまたはグラフの構造を走査または検索するためのアルゴリズムです。レベル順序では、下位レベルに移動する前に、そのレベルのすべてのノードを訪問します。
+ + +Exercise +-------- + +以下は、挿入と出力の機能を持つ二分木の実装です。この木は順序付けされていますが、バランスはとれていません。この例では、挿入時に順序付けが維持されます。 + +出力ルーチンを深さ優先探索の **pre-order** に変更してください。 + + +Tutorial Code +------------- + + #include + #include + + typedef struct node + { + int val; + struct node * left; + struct node * right; + } node_t; + + void insert(node_t * tree,int val); + void print_tree(node_t * current); + void printDFS(node_t * current); + + int main() + { + node_t * test_list = (node_t *) malloc(sizeof(node_t)); + /* 値を明示的に設定する代わりに、calloc()を使用する方法もあり */ + test_list->val = 0; + test_list->left = NULL; + test_list->right = NULL; + + insert(test_list,5); + insert(test_list,8); + insert(test_list,4); + insert(test_list,3); + + printDFS(test_list); + printf("\n"); + } + + void insert(node_t * tree, int val) + { + if (tree->val == 0) + { + /* 現在の(空の)位置に挿入する */ + tree->val = val; + } + else + { + if (val < tree->val) + { + /* insert left */ + if (tree->left != NULL) + { + insert(tree->left, val); + } + else + { + tree->left = (node_t *) malloc(sizeof(node_t)); + /* 値を明示的に設定する代わりに、calloc()を使用する方法もあり */ + tree->left->val = val; + tree->left->left = NULL; + tree->left->right = NULL; + } + } + else + { + if (val >= tree->val) + { + /* 右に挿入 */ + /* 右に挿入 */ + if (tree->right != NULL) + { + insert(tree->right,val); + } + else + { + tree->right = (node_t *) malloc(sizeof(node_t)); + /* 値を明示的に設定する代わりに、calloc()を使用する方法もあり */ + tree->right->val = val; + tree->right->left = NULL; + tree->right->right = NULL; + } + } + } + } + } + + /* 深さ優先探索 */ + void printDFS(node_t * current) + { + /* ここでコードを変更してください */ + if (current == NULL) return; /* セキュリティ対策 */ + if (current->left != NULL) printDFS(current->left); + if (current != NULL) printf("%d ", current->val); + if (current->right != NULL) printDFS(current->right); + } + + +Expected Output +--------------- + + 5 4 3 8 + +Solution +---- + + #include + #include + + typedef struct node + { + int val; + struct node * left; + struct node * right; + } node_t; + + void insert(node_t * tree,int val); + void print_tree(node_t * current); + void printDFS(node_t * current); + + int main() + { + node_t * test_list = (node_t *) malloc(sizeof(node_t)); + /* 値を明示的に設定する代わりに、calloc()を使用する方法もあり */ + test_list->val = 0; + test_list->left = NULL; + test_list->right = NULL; + + insert(test_list,5); + insert(test_list,8); + insert(test_list,4); + insert(test_list,3); + + printDFS(test_list); + printf("\n"); + } + + void insert(node_t * tree, int val) + { + if (tree->val == 0) + { + /* 現在の(空の)位置に挿入する */ + tree->val = val; + } + else + { + if (val < tree->val) + { + /* 左に挿入 */ + if (tree->left != NULL) + { + insert(tree->left, val); + } + else + { + tree->left = (node_t *) malloc(sizeof(node_t)); + /* 値を明示的に設定する代わりに、calloc()を使用する方法もあり */ + tree->left->val = val; + tree->left->left = NULL; + tree->left->right = NULL; + } + } + else + { + if (val >= tree->val) + { + /* 右に挿入 */ + if (tree->right != NULL) + { + insert(tree->right,val); + } + else + { + tree->right = (node_t *) malloc(sizeof(node_t)); + /* 値を明示的に設定する代わりに、calloc()を使用する方法もあり */ + tree->right->val = val; + tree->right->left = NULL; + tree->right->right = NULL; + } + } + } + } + } + + /* 深さ優先探索 */ + void printDFS(node_t * current) + { + /* change the code here */ + if (current == NULL) return; /* セキュリティ対策 */ + if (current != NULL) printf("%d ", current->val); + if (current->left != NULL) printDFS(current->left); + if (current->right != NULL) printDFS(current->right); + } diff --git a/tutorials/learn-c.org/ja/Bitmasks.md b/tutorials/learn-c.org/ja/Bitmasks.md new file mode 100644 index 00000000..38103ac9 --- /dev/null +++ b/tutorials/learn-c.org/ja/Bitmasks.md @@ -0,0 +1,187 @@ +Tutorial +-------- + +ビットマスクとは、データをchar/int/float型ではなく、ビットとして保存するプロセスです。特定の種類のデータをコンパクトかつ効率的に保存するのに非常に便利です。 + +ビットマスクの考え方はブール論理に基づいています。ブール論理とは、論理演算(0と1を引数として取る)によって「真」(1)と「偽」(0)を操作することです。ここでは以下の演算について考えます。 + +* NOT a - 最終値は入力値の反対になります (1 -> 0、0 -> 1) +* a AND b - 両方の値が 1 の場合、最終値は 1 になります。それ以外の場合は、最終値は 0 になります。 +* a OR b - どちらかの値が 1 の場合、最終値は 1 になります。それ以外の場合は、最終値は 0 になります。 +* a XOR b - 一方の値が 1 でもう一方の値が 0 の場合、最終値は 1 になります。それ以外の場合は、最終値は 0 になります。 + +コンピューティングにおいて、これらの真偽値の1つは*ビット*です。C言語のプリミティブ(`int`、`float`など)は、8の倍数のビット数で構成されます。例えば、`int`は少なくとも16ビットのサイズであるのに対し、`char`は8ビットです。8ビットは通常*バイト*と呼ばれます。C言語では、特定のプリミティブが[少なくともあるバイト数](http://en.wikipedia.org/wiki/C_data_types#Basic_types)バイトのサイズであることが保証されています。C11で導入された`stdint.h`により、プログラマーは正確にあるバイト数の整数型を指定できるようになりました。これはマスクを使用する際に非常に便利です。 + +ビットマスクはフラグを設定する際によく使用されます。フラグとは、「オン/オフ」や「移動/静止」など、2つの状態を取ることができる値です。 + +### ビット n の設定 + +ビット `n` を設定するのは、ストレージ変数の値と、値 `2^n` の OR 演算を行うのと同じくらい簡単です。 + +``` +storage |= 1 << n; +``` + +例として、`storage` が char (8 ビット) の場合のビット 3 の設定を次に示します。 + +``` +01000010 + OR +00001000 + == +01001010 +``` + +`2^n` ロジックは、マスク自体の適切なビットに '1' の値を配置し、ストレージ変数内の同じビットにアクセスできるようにします。 + +### ビット n をクリアする + +ビット `n` をクリアすることは、ストレージ変数の値と値 `2^n` の逆数 (NOT) との AND 演算の結果です。 + +``` +storage &= ~(1 << n); +``` + +もう一度例を挙げます。 + +``` +01001010 + AND +11110111 + == +01000010 +``` + +### 反転ビット n + +ビット `n` の反転は、ストレージ変数の値と `2^n` の XOR 演算の結果です。 + +``` +storage ^= 1 << n; +``` + +``` +01000010 01001010 + XOR XOR +00001000 00001000 + == == +01001010 01000010 +``` + +### ビット n をチェック + +ビットをチェックすることは、ビットストレージと `2^n` の値の AND 演算です。 + +``` +bit = storage & (1 << n); +``` + +``` +01000010 01001010 + AND AND +00001000 00001000 + == == +00000000 00001000 +``` + +Exercise +-------- + +ビットマスクを使用して一部のフラグを操作します。 + + +Tutorial Code +------------- + + #include + #include + + /* フラグの初期化を完了する */ + + const short FLAG_ON = 1 << 0; // 1 (0x01) + const short FLAG_MOVEMENT = 1 << 1; // 2 (0x02) + const short FLAG_TRANSPARENT = 1 << 2; // 4 (0x04) + const short FLAG_ALIVE = ; + const short FLAG_BROKEN = ; + const short FLAG_EDIBLE = 1 << 5; // 32 (0x20) + + int main() { + short attributes = 0; + + /* 属性をON、TRANSPARENT、BROKENに設定する */ + + assert(attributes == (FLAG_ON | FLAG_TRANSPARENT | FLAG_BROKEN)); + + /* 属性がONとALIVEのみになるように変更(設定/クリア/切り替え)します */ + + assert(attributes == (FLAG_ON | FLAG_ALIVE)); + + /* ALIVEフラグが設定されているかどうかを確認する */ + assert(/* ??? */); + + /* BROKENフラグが設定されていないか確認する */ + assert(/* ??? */); + + /* EDIBLE属性のみが設定されるように変更します */ + + assert(attributes == FLAG_EDIBLE); + + printf("Done!"); + } + + +Expected Output +--------------- + Done! + +Solution +---- + #include + #include + + /* フラグの初期化を完了する */ + + const short FLAG_ON = 1 << 0; // 1 (0x01) + const short FLAG_MOVEMENT = 1 << 1; // 2 (0x02) + const short FLAG_TRANSPARENT = 1 << 2; // 4 (0x04) + const short FLAG_ALIVE = 1 << 3; // 8 (0x08) + const short FLAG_BROKEN = 1 << 4; // 16 (0x10) + const short FLAG_EDIBLE = 1 << 5; // 32 (0x20) + + int main() { + short attributes = 0; + + /* 属性をON、TRANSPARENT、BROKENに設定する */ + + attributes |= FLAG_ON; + attributes |= FLAG_TRANSPARENT; + attributes |= FLAG_BROKEN; + // possible solution(s): + // attributes |= FLAG_ON | FLAG_TRANSPARENT | FLAG_BROKEN; + // attributes = FLAG_ON | FLAG_TRANSPARENT | FLAG_BROKEN; + + assert(attributes == (FLAG_ON | FLAG_TRANSPARENT | FLAG_BROKEN)); + + /* 属性がONとALIVEのみになるように変更(設定/クリア/切り替え)します */ + + attributes &= ~FLAG_TRANSPARENT; + // possible: attributes ^= FLAG_TRANSPARENT; + attributes ^= FLAG_BROKEN; + // possible: attributes &= ~FLAG_BROKEN; + attributes |= FLAG_ALIVE; + + assert(attributes == (FLAG_ON | FLAG_ALIVE)); + + /* ALIVEフラグが設定されているかどうかを確認する */ + assert(attributes & FLAG_ALIVE); + + /* BROKENフラグが設定されていないか確認する */ + assert(!(attributes & FLAG_BROKEN)); + + /* EDIBLE属性のみが設定されるように変更します */ + attributes = FLAG_EDIBLE; + + assert(attributes == FLAG_EDIBLE); + + printf("Done!"); + } diff --git a/tutorials/learn-c.org/ja/Conditions.md b/tutorials/learn-c.org/ja/Conditions.md new file mode 100644 index 00000000..d808c348 --- /dev/null +++ b/tutorials/learn-c.org/ja/Conditions.md @@ -0,0 +1,152 @@ +Tutorial +-------- + +### 決断を下す + +人生において、私たちは皆、決断を下さなければなりません。決断を下すために、私たちは選択肢を検討し、計画も練り上げます。 + +以下は、C 言語に見られる『決断を下す』構造の一般的な形式です。 + + int target = 10; + if (target == 10) { + printf("Target is equal to 10"); + } + + +### `if`文 + +`if` 文を使うと、式が `true` か `false` かを確認し、結果に応じて異なるコードを実行できます。 + +2つの変数が等しいかどうかを評価するには、最初の例のように `==` 演算子を使用します。 + +不等号演算子も式の評価に使用できます。例: + + int foo = 1; + int bar = 2; + + if (foo < bar) { + printf("foo is smaller than bar."); + } + + if (foo > bar) { + printf("foo is greater than bar."); + } + +式が `false` と評価された場合にコードを実行するには、 `else` キーワードを使用できます。 + + int foo = 1; + int bar = 2; + + if (foo < bar) { + printf("foo is smaller than bar."); + } else { + printf("foo is greater than bar."); + } + +場合によっては、2つ以上の結果から選択する必要があります。このような場合は、複数の `if` `else` 文を「連結」することができます。 + + int foo = 1; + int bar = 2; + + if (foo < bar) { + printf("foo is smaller than bar."); + } else if (foo == bar) { + printf("foo is equal to bar."); + } else { + printf("foo is greater than bar."); + } + +必要に応じて、`if` `else` ステートメントをネストすることもできます。 + + int peanuts_eaten = 22; + int peanuts_in_jar = 100; + int max_peanut_limit = 50; + + if (peanuts_in_jar > 80) { + if (peanuts_eaten < max_peanut_limit) { + printf("Take as many peanuts as you want!\n"); + } + } else { + if (peanuts_eaten > peanuts_in_jar) { + printf("You can't have anymore peanuts!\n"); + } + else { + printf("Alright, just one more peanut.\n"); + } + } + + +論理演算子を使用して2つ以上の式をまとめて評価することで、『2つの式が両方とも「true」と評価されるか』『または少なくともどちらか一方が「true」と評価されるか』を確認できます。2つの式が両方とも「true」と評価されるかどうかを確認するには、AND演算子「&&」を使用します。少なくとも一方の式が「true」と評価されるかどうかを確認するには、OR演算子「||」をを使用します。 + + int foo = 1; + int bar = 2; + int moo = 3; + + if (foo < bar && moo > bar) { + printf("foo is smaller than bar AND moo is larger than bar."); + } + + if (foo < bar || moo > bar) { + printf("foo is smaller than bar OR moo is larger than bar."); + } + +The NOT operator `!` can also be used likewise: + +NOT 演算子 `!` も同様に使用できます。 + + int target = 9; + if (target != 10) { + printf("Target is not equal to 10"); + } + + +Exercise +-------- + +この演習では、`guessNumber` 関数ステートメント内に、数値 `guess` が 555 に等しいかどうかを確認する `if` ステートメントを作成する必要があります。等しい場合、関数は `printf` を使用して「正解です。お察しの通りです!」と出力する必要があります。`guess` が 555 未満の場合、関数は `printf` を使用して「あなたの推測は低すぎます。」と出力する必要があります。`guess` が 555 より大きい場合、関数は `printf` を使用して「あなたの推測は高すぎます。」と出力する必要があります。 + +* **重要**: printf 文字列の最後に改行文字 `\n` を追加することを忘れないでください。 + +Tutorial Code +------------- + + #include + + void guessNumber(int guess) { + // TODO: ここにコードを記述してください + } + + int main() { + guessNumber(500); + guessNumber(600); + guessNumber(555); + } + +Expected Output +--------------- + + Your guess is too low. + Your guess is too high. + Correct. You guessed it! + +Solution +---- + + #include + + void guessNumber(int guess) { + // TODO: ここにコードを記述してください + if (guess < 555) { + printf("Your guess is too low.\n"); + } else if (guess > 555) { + printf("Your guess is too high.\n"); + } else { + printf("Correct. You guessed it!\n"); + } + } + + int main() { + guessNumber(500); + guessNumber(600); + guessNumber(555); + } diff --git a/tutorials/learn-c.org/ja/Dynamic allocation.md b/tutorials/learn-c.org/ja/Dynamic allocation.md new file mode 100644 index 00000000..a970054b --- /dev/null +++ b/tutorials/learn-c.org/ja/Dynamic allocation.md @@ -0,0 +1,93 @@ +Tutorial +-------- + +メモリの動的割り当てはC言語において非常に重要なテーマです。これにより、連結リストなどの複雑なデータ構造を構築できます。メモリを動的に割り当てることで、プログラム作成時にデータのサイズを事前に把握することなく、データを保存できるようになります。 + +メモリチャンクを動的に割り当てるには、新しく割り当てられたメモリの位置を格納するためのポインタを用意しておく必要があります。同じポインタを使って割り当てられたメモリにアクセスでき、使い終わったメモリもそのポインタを使って解放できます。 + +person構造体を動的に割り当てるとします。personは次のように定義されています。 + + typedef struct { + char * name; + int age; + } person; + +`myperson` 引数に新しい人物を割り当てるには、次の構文を使用します。 + + person * myperson = (person *) malloc(sizeof(person)); + +これは、person構造体を保持するのに十分なメモリを動的に割り当て、その新しく割り当てられたデータへの`person`型のポインタを返すことをコンパイラに指示します。メモリ割り当て関数`malloc()`は、指定されたメモリ空間を予約します。この場合、これは`person`のバイト単位のサイズです。 + +`malloc()` の呼び出しの前に `(person *)` と書くのは、`malloc()` が「void ポインタ」、つまり型を持たないポインタを返すためです。`(person *)` を前に書くことは *型キャスト* と呼ばれ、`malloc()` から返されるポインタの型を `person` に変更します。ただし、型キャストしない場合、C 言語は返されるポインタの型を、代入先のポインタ(この場合は `myperson`)の型に暗黙的に変換するため、必ずしもこのように書く必要はありません。 + +`sizeof` は実際の関数ではないことに注意してください。コンパイラがこれを解釈し、 person 構造体の実際のメモリ サイズに変換するためです。 + +person 構造体のメンバーにアクセスするには、`->` 表記を使用できます。 + + myperson->name = "John"; + myperson->age = 27; + +動的に割り当てられた構造体の使用が終わったら、`free` を使用して解放できます。 + + free(myperson); + +free は `myperson` 変数自体を削除するのではなく、その変数が指しているデータを解放するだけであることに注意してください。`myperson` 変数はメモリ内のどこかを指したままになりますが、`free(myperson)` を呼び出した後は、その領域にアクセスできなくなります。新しいデータを割り当てるまでは、そのポインタを再び使用してはいけません。 + +Exercise +-------- + +`malloc` を使用してポイント構造体を動的に割り当てます。 + +Tutorial Code +------------- + + #include + #include + + typedef struct { + int x; + int y; + } point; + + int main() { + point * mypoint = NULL; + + /* ここで、mypoint が指す新しいポイント構造体を + 動的に割り当てます */ + + mypoint->x = 10; + mypoint->y =5 ; + printf("mypoint coordinates: %d, %d\n", mypoint->x, mypoint->y); + + free(mypoint); + return 0; + } + +Expected Output +--------------- + + mypoint coordinates: 10, 5 + +Solution +---- + + #include + #include + + typedef struct { + int x; + int y; + } point; + + int main() { + point * mypoint; + + mypoint = (point *)malloc(sizeof(point)); + + mypoint->x = 10; + mypoint->y =5 ; + printf("mypoint coordinates: %d, %d\n", mypoint->x, mypoint->y); + + free(mypoint); + return 0; + } diff --git a/tutorials/learn-c.org/ja/For loops.md b/tutorials/learn-c.org/ja/For loops.md new file mode 100644 index 00000000..ce3f2819 --- /dev/null +++ b/tutorials/learn-c.org/ja/For loops.md @@ -0,0 +1,82 @@ +Tutorial +-------- + +C言語のforループはシンプルです。ループとは、複数回実行されるコードブロックのことです。 +forループには、通常「i」と表記されるイテレータ変数が必要です。 + +forループには以下の機能があります。 + +* 初期値を使用してイテレータ変数を初期化する +* イテレータが最終値に達したかどうかを確認する +* イテレータの値を増やす + +例えば、あるブロックを10回繰り返し処理したい場合は、次のように記述します。 + + int i; + for (i = 0; i < 10; i++) { + printf("%d\n", i); + } + +This block will print the numbers 0 through 9 (10 numbers in total). + +For loops can iterate on array values. For example, if we would want to sum all the values of an array, we would use +the iterator `i` as the array index: + +このブロックは0から9までの数字(合計10個)を出力します。 + +forループは配列の値を、繰り返し処理できます。例えば、配列のすべての値を合計したい場合は、 +イテレータ「i」を配列のインデックスとして使用します。 + + int array[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int sum = 0; + int i; + + for (i = 0; i < 10; i++) { + sum += array[i]; + } + + /* 合計には a[0] + a[1] + ... + a[9] が含まれます */ + printf("Sum of the array is %d\n", sum); + +Exercise +-------- + +変数 `array` の階乗(`array[0]` から `array[9]` までのすべての項目の乗算)を計算します。 + +Tutorial Code +------------- + + #include + + int main() { + int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int factorial = 1; + int i; + + /* ここでforループを使用して階乗を計算します*/ + + printf("10! is %d.\n", factorial); + } + +Expected Output +--------------- + + 10! is 3628800. + +Solution +---- + + #include + + int main() { + int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int factorial = 1; + + int i; + + for(i=0;i<10;i++){ + factorial *= array[i]; + } + + printf("10! is %d.\n", factorial); + } diff --git a/tutorials/learn-c.org/ja/Function Pointers.md b/tutorials/learn-c.org/ja/Function Pointers.md new file mode 100644 index 00000000..0ab025a2 --- /dev/null +++ b/tutorials/learn-c.org/ja/Function Pointers.md @@ -0,0 +1,161 @@ +Tutorial +-------- + +ポインタを覚えていますか?文字の配列を指し示し、それを文字列に変換するためにポインタを使いました。 +その後、ポインタの制御方法を学んだことで、さらに面白くなりました。 +次は、ポインタを使って関数を指し示したり呼び出したりしてみましょう。 + +### なぜ関数を指すのか? + +おそらく最初に頭に浮かぶ疑問は、関数名で関数を呼び出すのになぜポインタを使うのか、ということでしょう。`function();` と書けばいいのですから。これは良い質問です! では、配列をソートする必要がある `sort` 関数を想像してみてください。配列の要素を昇順または降順に並べ替えたい場合があります。どちらを選びますか? 関数ポインタです! + +### 関数ポインタ構文 + + void (*pf)(int); + +ま、そうでしょうね。確かに非常に複雑ですね。少なくとも、そう思われるかもしれません。では、コードをもう一度読んで、一つ一つ理解してみましょう。隅々まで読んでみてください。`*pf` は関数へのポインタです。`void` はその関数の戻り値の型、そして `int` はその関数の引数の型です。分かりましたか?いいですね。 + +関数ポインタにポインタを挿入して、もう一度読み取ってみます: + + char* (*pf)(int*) + +繰り返します +1. `*pf` は関数ポインタです。 +2. `char*` はその関数の戻り値の型です。 +3. `int*` は引数の型です。 + +理論はもう十分です。実際のコードで実際に試してみましょう。 +次の例をご覧ください。 + + #include + void someFunction(int arg) + { + printf("This is someFunction being called and arg is: %d\n", arg); + printf("Whoops leaving the function now!\n"); + } + + main() + { + void (*pf)(int); + pf = &someFunction; + printf("We're about to call someFunction() using a pointer!\n"); + (pf)(5); + printf("Wow that was cool. Back to main now!\n\n"); + } + +先ほど説明した `sort()` を覚えていますか?これを使って同じことができます。 +集合を昇順で並べ替える代わりに、次のように独自の比較関数を使って逆の順序付けを行うことができます。 + + #include + #include //for qsort() + + int compare(const void* left, const void* right) + { + return (*(int*)right - *(int*)left); + // 複雑に思える場合は、ref に戻ってください: http://www.cplusplus.com/reference/cstdlib/qsort/ + } + main() + { + int (*cmp) (const void* , const void*); + cmp = &compare; + + int iarray[] = {1,2,3,4,5,6,7,8,9}; + qsort(iarray, sizeof(iarray)/sizeof(*iarray), sizeof(*iarray), cmp); + + int c = 0; + while (c < sizeof(iarray)/sizeof(*iarray)) + { + printf("%d \t", iarray[c]); + c++; + } + } + +もう一度思い出してみましょう。なぜ関数ポインタを使うのでしょうか? +1. プログラマーが様々な用途でライブラリを利用できるようにするため -> 「柔軟性」 + + +Exercise +-------- + +関数へのポインタの配列を完成させ、配列内のポインタを使って各関数を呼び出します。関数へのポインタの配列? はい、できますよ! + +Tutorial Code +------------- + + #include + + void f1(int var) + { + printf("this is f1 and var is: %d\n", var); + } + + void f2(int var) + { + printf("this is f2 and var is: %d\n", var); + } + + void f3(int var) + { + printf("this is f3 and var is: %d\n", var); + } + + int main() + { + /* define an array full of function pointers + to the above functions, that take an `int` as + their only argument */ + + + int c = 0; + while(c < 3) + { + /* call the functions using the function pointers + of the array at index `c` with `c` as an argument */ + + ++c; + } + + return 0; + } + + +Expected Output +--------------- + + this is f1 and var is: 0 + this is f2 and var is: 1 + this is f3 and var is: 2 + +Solution +---- + + #include + + void f1(int var) + { + printf("this is f1 and var is: %d\n", var); + } + + void f2(int var) + { + printf("this is f2 and var is: %d\n", var); + } + + void f3(int var) + { + printf("this is f3 and var is: %d\n", var); + } + + int main() + { + void (*pf[])(int) = {f1, f2, f3}; + + int c = 0; + while(c < 3) + { + pf[c](c); + ++c; + } + + return 0; + } diff --git a/tutorials/learn-c.org/ja/Function arguments by reference.md b/tutorials/learn-c.org/ja/Function arguments by reference.md new file mode 100644 index 00000000..0a5d1572 --- /dev/null +++ b/tutorials/learn-c.org/ja/Function arguments by reference.md @@ -0,0 +1,118 @@ +Tutorial +-------- + +ポインタと関数について理解した上で、関数の引数は値渡し、つまり関数間でコピーされて渡されることをご存知でしょう。 +しかし、値そのものではなく、値へのポインタを渡したらどうなるでしょうか? こうすることで、関数は親関数の変数や構造体をコピーではなく制御できるようになり、元のオブジェクトを直接読み書きできるようになります。 + +例えば、`addone` という、数値を1ずつ増やす関数を記述するとします。これはうまくいきません。 + + void addone(int n) { + // nは関数スコープ内にのみ存在するローカル変数です + n++; // そのため、nを増やしても、関数の外からは影響を与えません + } + + int n; + printf("Before: %d\n", n); + addone(n); + printf("After: %d\n", n); + +しかし、これはうまくいきます。 + + void addone(int *n) { + // nは関数スコープ外のメモリアドレスを指すポインタです + (*n)++; // これにより、nの値が実質的に増加します。 + } + + int n; + printf("Before: %d\n", n); + addone(&n); + printf("After: %d\n", n); + +違いは、`addone` の 2 番目のバージョンは変数 `n` へのポインタを引数として受け取り、それがメモリ内のどこにあるかを知っているため、それを操作できるという点です。 + +`addone` 関数を呼び出す際は、変数 `n` 自体ではなく、変数への参照を渡す必要があることに注意してください。これは、関数が変数のアドレスを認識し、変数のコピーを受け取らないようにするためです。 + +### 構造体へのポインタ + +点を `x` 方向と `y` 方向の両方に移動する、`move` という関数を作成するとします。2つのポインタを引数として渡す代わりに、点構造体の関数へのポインタを1つだけ渡すことができます。 + + void move(point * p) { + (*p).x++; + (*p).y++; + } + +構造体を逆参照して内部メンバーにアクセスしたいときのために、簡略化された構文があります。それほど、この操作は構造体データで広く使用されているのです。この関数は、次の構文を使って書き換えることができます。 + + void move(point * p) { + p->x++; + p->y++; + } + +Exercise +-------- + +`person`の`age`に1を加算する`birthday`という関数を書いてください。 + +Tutorial Code +------------- + + #include + + typedef struct { + char * name; + int age; + } person; + + /* 関数宣言 */ + + void birthday(person * p); + + /* ここに関数を記述してください */ + + int main() { + person john; + john.name = "John"; + john.age = 27; + + printf("%s is %d years old.\n", john.name, john.age); + birthday(&john); + printf("Happy birthday! %s is now %d years old.\n", john.name, john.age); + + return 0; + } + +Expected Output +--------------- + + John is 27 years old. + Happy birthday! John is now 28 years old. + +Solution +---- + + #include + + typedef struct { + char * name; + int age; + } person; + + /* 関数宣言 */ + void birthday(person * p); + + void birthday(person * p){ + p->age++; // This is the same.. + //(*p).age++; // ... as this would be + } + + int main() { + person john; + john.name = "John"; + john.age = 27; + + printf("%s is %d years old.\n", john.name, john.age); + birthday(&john); + printf("Happy birthday! %s is now %d years old.\n", john.name, john.age); + + return 0; + } diff --git a/tutorials/learn-c.org/ja/Functions.md b/tutorials/learn-c.org/ja/Functions.md new file mode 100644 index 00000000..294f9f38 --- /dev/null +++ b/tutorials/learn-c.org/ja/Functions.md @@ -0,0 +1,110 @@ +Tutorial +-------- + +C関数はシンプルですが、C言語の仕組み上、その威力には多少の限界があります。 + +* 関数は、固定数または可変数の引数を受け取ります。 +* 関数は1つの値のみを返すか、値を返さないかのいずれかです。 + +C言語では、引数は関数に値としてコピーされます。つまり、関数外で引数の値を変更して変更することはできません。 +そのためには、後ほど説明するポインタを使用する必要があります。 + +関数は以下の構文で定義されます。 + + int foo(int bar) { + /* 何かをする */ + return bar * 2; + } + + int main() { + foo(1); + } + +定義した関数 `foo` は、`bar` という1つの引数を受け取ります。受け取った整数をを2倍して結果を返します。 + +引数 `bar` に1を指定して関数 `foo` を実行するには、次の構文を使用します。 + + foo(1); + +C言語では、関数はコード内で使用される前に定義される必要があります。関数は最初に宣言し、後でヘッダーファイルやC言語ファイルの先頭で実装します。また、使用される順序で実装することもできます(こちらはあまり推奨されません)。 + +関数の正しい使用方法は次のとおりです。 + + /* 関数宣言 */ + int foo(int bar); + + int main() { + /* main から foo を呼ぶ */ + printf("The value of foo is %d", foo(1)); + } + + int foo(int bar) { + return bar + 1; + } + +キーワード `void` を使用して、値を返さない関数を作成することもできます。 + + void moo() { + /* 何かを実行するが、値を返さない */ + } + + int main() { + moo(); + } + +Exercise +-------- + +`print_big` という関数を作成してください。この関数は、引数(整数)を1つ受け取り、引数が10より大きい数値の場合に `x is big`(x は引数)という行を出力します。 + +* **重要**: printf 文字列の末尾に改行文字 `\n` を追加することを忘れないでください。 + +Tutorial Code +------------- + + #include + + /* 関数宣言 */ + void print_big(int number); + + int main() { + int array[] = { 1, 11, 2, 22, 3, 33 }; + int i; + for (i = 0; i < 6; i++) { + print_big(array[i]); + } + return 0; + } + + /* あなたの関数をここに書く */ + +Expected Output +--------------- + + 11 is big + 22 is big + 33 is big + +Solution +---- + + #include + + /* 関数宣言 */ + void print_big(int number); + + int main() { + int array[] = { 1, 11, 2, 22, 3, 33 }; + int i; + for (i = 0; i < 6; i++) { + print_big(array[i]); + } + return 0; + } + + void print_big(int number){ + if(number > 10){ + printf("%d is big\n",number); + } + } + diff --git a/tutorials/learn-c.org/ja/Hello, World!.md b/tutorials/learn-c.org/ja/Hello, World!.md new file mode 100644 index 00000000..ca7fcf94 --- /dev/null +++ b/tutorials/learn-c.org/ja/Hello, World!.md @@ -0,0 +1,66 @@ +Tutorial +-------- + +### イントロダクション + +C言語は汎用プログラミング言語であり、マシンの動作と密接に関連しています。 +コンピュータメモリの仕組みを理解することは、C言語の重要な側面です。C言語は「習得が難しい」と考えられがちですが、実際には非常にシンプルな言語であり、同時に非常に強力な機能を備えています。 + +C言語はきわめて一般的な言語であり、Windows、Pythonインタープリター、Gitなど、多くのアプリケーションがC言語で開発されました。 + +C言語はコンパイル言語です。つまり、それを動かすには、コンパイラ(GCCやVisual Studioなど)が記述したコードを受け取り、処理して、実行ファイルを作成する必要があります。 +そうなって、ようやく、われわれはプログラムに「そう動いてほしい」と期待した動きを行わせることができます。 + +### 最初のプログラム + +すべてのCプログラムは、必要な関数を実行するためのライブラリを使用します。例えば、画面に出力する最も基本的な関数である「printf」は、「stdio.h」ヘッダーファイルで定義されています。 + +プログラムに「printf」コマンドを実行する機能を追加するには、コードの1行目に次のincludeディレクティブを追加する必要があります。 + + #include + +コードの2番目の部分は、実際に記述するコードです。最初に実行されるコードは、常に`main` 関数内にあります。 + + int main() { + ... ここにコードが書かれる + } + +`int` キーワードは、関数 `main` が整数、つまり単純な数値を返すことを示します。関数によって返される数値は、記述したプログラムが正しく動作したかどうかを示します。コードが正常に実行されたことを示す場合は、数値 0 を返します。0 より大きい数値は、記述したプログラムが失敗したことを意味します。 + +このチュートリアルでは、プログラムが成功したことを示すために 0 を返します。 + + return 0; + +C言語のすべての文はセミコロンで終わる必要があることに注意してください。これにより、コンパイラは次の文が始まったことを認識します。 + +最後になりましたが、重要なことです。文を出力するために関数「printf」を呼び出さなくてはなりません。 + +Exercise +-------- + +下部のプログラムを変更して、出力に「Hello, World!」と表示されるようにします。 + +Tutorial Code +------------- + + #include + + int main() { + printf("Goodbye, World!"); + return 0; + } + +Expected Output +--------------- + + Hello, World! + +Solution +-------- + + #include + + int main() { + printf("Hello, World!"); + return 0; + } diff --git a/tutorials/learn-c.org/ja/Linked lists.md b/tutorials/learn-c.org/ja/Linked lists.md new file mode 100644 index 00000000..3b9e7aec --- /dev/null +++ b/tutorials/learn-c.org/ja/Linked lists.md @@ -0,0 +1,383 @@ +Tutorial +-------- + +### イントロダクション + +連結リストは、ポインタを用いて実装する動的データ構造の最も優れた、そして最もシンプルな例です。 +しかし、連結リストの仕組みを理解するには、ポインタを理解することが重要です。そのため、ポインタのチュートリアルをスキップした場合は、戻ってもう一度学習してください。また、動的メモリ割り当てと構造についても理解しておく必要があります。 + +基本的に、連結リストは、配列内の任意の位置から必要に応じて拡大または縮小できる配列として機能します。 + +連結リストには、配列に比べていくつかの利点があります。 + +1. リストの途中から項目を追加または削除できます。 +2. 初期サイズを定義する必要はありません。 + +しかし、連結リストにはいくつかの欠点もあります。 + +1. 「ランダム」アクセスが不可能です。配列のn番目の項目に到達するには、まずその項目までのすべての項目を反復処理する必要があります。つまり、リストの先頭から始めて、目的の項目に到達するまでリスト内を何回進んだかを数える必要があります。 +2. 動的なメモリ割り当てとポインタが必要となるため、コードが複雑になり、メモリリークやセグメントフォールトのリスクが高まります。 +3. 連結リストは配列よりもオーバーヘッドがはるかに大きくなります。これは、連結リストの項目が動的に割り当てられるため(メモリ使用効率が低い)、リスト内の各項目に追加のポインタを格納する必要があるためです。 + +### リンクリストとは何か? + +リンクリストは、動的に割り当てられたノードの集合であり、各ノードが1つの値と1つのポインタを持つように配置されています。ポインタは常にリストの次のメンバーを指します。ポインタがNULLの場合、リストの最後のノードを指します。 + +リンクリストは、リストの最初の項目を指すローカルポインタ変数を使用して保持されます。そのポインタもNULLの場合、リストは空であるとみなされます。 + + ------------------------------ ------------------------------ + | | | \ | | | + | DATA | NEXT |--------------| DATA | NEXT | + | | | / | | | + ------------------------------ ------------------------------ + +リンク リスト ノードを定義する: + + typedef struct node { + int val; + struct node * next; + } node_t; + +構造体を再帰的に定義していることに注意してください。これはC言語でも可能です。ノード型の名前を「node_t」としましょう。 + +これでノードを使用できるようになりました。リストの最初の項目を指すローカル変数(「head」)を作成しましょう。 + + node_t * head = NULL; + head = (node_t *) malloc(sizeof(node_t)); + if (head == NULL) { + return 1; + } + + head->val = 1; + head->next = NULL; + +リストの最初の変数を作成しました。リストへのデータの追加を完了するには、値を設定し、次の項目を空にする必要があります。 +malloc が NULL 値を返したかどうかを常に確認する必要があることに注意してください。 + +リストの末尾に変数を追加するには、次のポインタまで進み続けます。 + + node_t * head = NULL; + head = (node_t *) malloc(sizeof(node_t)); + head->val = 1; + head->next = (node_t *) malloc(sizeof(node_t)); + head->next->val = 2; + head->next->next = NULL; + +これを延々と続けることもできますが、実際に行うべきことは、`next` 変数が `NULL` になるまで、リストの最後の項目まで進むことです。 + +### リストの反復処理 + +リストのすべての項目を出力する関数を作成しましょう。そのためには、現在出力中のノードを追跡する「current」ポインタを使用する必要があります。ノードの値を出力した後、「current」ポインタを次のノードに設定し、リストの末尾に達するまで(次のノードはNULLです)、再度出力します。 + + void print_list(node_t * head) { + node_t * current = head; + + while (current != NULL) { + printf("%d\n", current->val); + current = current->next; + } + } + +### リストの最後に項目を追加する + +連結リストのすべてのメンバーを反復処理するには、「current」というポインタを使用します。このポインタを先頭から開始するように設定し、各ステップでポインタをリストの次の項目に進め、最後の項目に到達するまで続けます。 + + void push(node_t * head, int val) { + node_t * current = head; + while (current->next != NULL) { + current = current->next; + } + + /* 新しい変数を追加できるようになった */ + current->next = (node_t *) malloc(sizeof(node_t)); + current->next->val = val; + current->next->next = NULL; + } + +リンク リストの最適な使用例はスタックとキューです。これらをここで実装します。 + +### リストの先頭にアイテムを追加する(リストにプッシュする) + +リストの先頭に追加するには、以下の手順が必要です。 + +1. 新しい項目を作成し、その値を設定します。 +2. 新しい項目をリストの先頭にリンクします。 +3. リストの先頭を新しい項目に設定します。 + +これにより、リストに新しい値を持つ新しいヘッドが作成され、リストの残りの部分はそれにリンクされたままになります。 + +この操作には関数を使用するため、ヘッド変数を変更できるようにする必要があります。そのためには、ポインタ変数へのポインタ(ダブルポインタ)を渡す必要があります。これにより、ポインタ自体を変更できるようになります。 + + void push(node_t ** head, int val) { + node_t * new_node; + new_node = (node_t *) malloc(sizeof(node_t)); + + new_node->val = val; + new_node->next = *head; + *head = new_node; + } + + +### 最初の項目を削除する(リストからポップする) + +変数をポップするには、このアクションを逆に実行する必要があります。 + +1. ヘッドが指している次の項目を取得して保存します。 +2. ヘッド項目を解放します。 +3. サイドに格納した次の項目をヘッドに設定します。 + +コードは次のとおりです。 + + int pop(node_t ** head) { + int retval = -1; + node_t * next_node = NULL; + + if (*head == NULL) { + return -1; + } + + next_node = (*head)->next; + retval = (*head)->val; + free(*head); + *head = next_node; + + return retval; + } + + +### リストの最後の項目を削除する + +リストから最後の項目を削除することは、リストの末尾に追加することと非常に似ていますが、大きな違いが 1 つあります。最後の項目の 1 つ前の項目を変更する必要があるため、実際には 2 つ先の項目を調べて、次の項目がリストの最後の項目であるかどうかを確認する必要があります。: + + + int remove_last(node_t * head) { + int retval = 0; + /* リストに項目が1つしかない場合はそれを削除します */ + if (head->next == NULL) { + retval = head->val; + free(head); + return retval; + } + + /* リストの最後から2番目のノードに移動する */ + node_t * current = head; + while (current->next->next != NULL) { + current = current->next; + } + + /* 現在、currentはリストの最後から2番目の項目を指しているので、current->nextを削除します */ + retval = current->next->val; + free(current->next); + current->next = NULL; + return retval; + + } + + +### 特定のアイテムを削除する + +リストから特定の項目を削除するには、リストの先頭からのインデックス、または値のいずれかで、すべての項目を調べ、削除したい項目の前のノードに到達しているかどうかを確認する必要があります。 +これは、前のノードが指している場所にも位置を変更する必要があるためです。 + +アルゴリズムは次のとおり: + +1. 削除したいノードの前のノードまで反復処理する +2. 削除したいノードを一時ポインタに保存する +3. 前のノードの次のポインタを、削除したいノードの次のノードを指すように設定する +4. 一時ポインタを使ってノードを削除する + +いくつかのエッジケースに対処する必要があるので、コードの内容を理解しておいてください。 + + int remove_by_index(node_t ** head, int n) { + int i = 0; + int retval = -1; + node_t * current = *head; + node_t * temp_node = NULL; + + + if (n == 0) { + return pop(head); + } + + for (i = 0; i < n-1; i++) { + if (current->next == NULL) { + return -1; + } + current = current->next; + } + + if (current->next == NULL) { + return -1; + } + + temp_node = current->next; + retval = temp_node->val; + current->next = temp_node->next; + free(temp_node); + + return retval; + + } + + +Exercise +-------- + +先頭へのダブルポインタを受け取り、リスト内の値 `val` を持つ最初の項目を削除する関数 `remove_by_value` を実装する必要があります。 + +Tutorial Code +------------- + + #include + #include + + typedef struct node { + int val; + struct node * next; + } node_t; + + void print_list(node_t * head) { + node_t * current = head; + + while (current != NULL) { + printf("%d\n", current->val); + current = current->next; + } + } + + int pop(node_t ** head) { + int retval = -1; + node_t * next_node = NULL; + + if (*head == NULL) { + return -1; + } + + next_node = (*head)->next; + retval = (*head)->val; + free(*head); + *head = next_node; + + return retval; + } + + int remove_by_value(node_t ** head, int val) { + /* TODO: ここにコードを入力してください */ + } + + int main() { + + node_t * test_list = (node_t *) malloc(sizeof(node_t)); + test_list->val = 1; + test_list->next = (node_t *) malloc(sizeof(node_t)); + test_list->next->val = 2; + test_list->next->next = (node_t *) malloc(sizeof(node_t)); + test_list->next->next->val = 3; + test_list->next->next->next = (node_t *) malloc(sizeof(node_t)); + test_list->next->next->next->val = 4; + test_list->next->next->next->next = NULL; + + remove_by_value(&test_list, 3); + + print_list(test_list); + } + +Expected Output +--------------- + + 1 + 2 + 4 + +Solution +---- + + #include + #include + + typedef struct node { + int val; + struct node * next; + } node_t; + + void print_list(node_t * head) { + node_t * current = head; + + while (current != NULL) { + printf("%d\n", current->val); + current = current->next; + } + } + + int pop(node_t ** head) { + int retval = -1; + node_t * next_node = NULL; + + if (*head == NULL) { + return -1; + } + + next_node = (*head)->next; + retval = (*head)->val; + free(*head); + *head = next_node; + + return retval; + } + + int remove_by_value(node_t ** head, int val) { + node_t *previous, *current; + + if (*head == NULL) { + return -1; + } + + if ((*head)->val == val) { + return pop(head); + } + + previous = *head; + current = (*head)->next; + while (current) { + if (current->val == val) { + previous->next = current->next; + free(current); + return val; + } + + previous = current; + current = current->next; + } + return -1; + } + + void delete_list(node_t *head) { + node_t *current = head, + *next = head; + + while (current) { + next = current->next; + free(current); + current = next; + } + } + + int main(void) { + node_t * test_list = (node_t *) malloc(sizeof(node_t)); + + test_list->val = 1; + test_list->next = (node_t *) malloc(sizeof(node_t)); + test_list->next->val = 2; + test_list->next->next = (node_t *) malloc(sizeof(node_t)); + test_list->next->next->val = 3; + test_list->next->next->next = (node_t *) malloc(sizeof(node_t)); + test_list->next->next->next->val = 4; + test_list->next->next->next->next = NULL; + + remove_by_value(&test_list, 3); + + print_list(test_list); + delete_list(test_list); + + return EXIT_SUCCESS; + } diff --git a/tutorials/learn-c.org/ja/Multidimensional Arrays.md b/tutorials/learn-c.org/ja/Multidimensional Arrays.md new file mode 100644 index 00000000..8894054b --- /dev/null +++ b/tutorials/learn-c.org/ja/Multidimensional Arrays.md @@ -0,0 +1,153 @@ +Tutorial +-------- + +前回の[配列](https://www.learn-c.org/en/Arrays)のチュートリアルでは、配列とその仕組みについて説明しました。これまで見てきた配列はすべて1次元でしたが、C言語では多次元配列を作成・使用できます。多次元配列の宣言の一般的な形式は次のとおりです。 + + type name[size1][size2]...[sizeN]; + +For example, here's a basic one for you to look at - + +基本的な例をここに挙げると… + + int foo[1][2][3]; + +or maybe this one - + +ほかには、こんなのも… + + char vowels[1][5] = { + {'a', 'e', 'i', 'o', 'u'} + }; + +### 二次元配列 + +多次元配列の最も単純な形式は二次元配列です。二次元配列は、基本的に一次元配列のリストです。サイズが[ x ][ y ]の二次元整数配列を宣言するには、次のように記述します。 + + type arrayName [x][y]; + +ここで、__type__ は任意の C データ型(int、char、long、long long、double など)で、__arrayName__ は有効な C 識別子または変数です。2 次元配列は、[ x ] 行と [ y ] 列を持つ表と考えることができます。3 行 4 列の 2 次元配列 a は、次のように表すことができます。 + +![Table 1A](https://www.tutorialspoint.com/cprogramming/images/two_dimensional_arrays.jpg) + +配列 a 内のすべての要素は、__a[i][j]__ という形式の要素名によって識別されます。ここで、'a' は配列の名前であり、'i' と 'j' は 'a' 内の各要素を一意に識別または表示するインデックスです。 + +実際には、[x]の値を入力する必要はありません。なぜなら、次のようにすれば、 + + char vowels[][5] = { + {'A', 'E', 'I', 'O', 'U'}, + {'a', 'e', 'i', 'o', 'u'} + }; + +それだけで、コンパイラは2つの「次元」があることを認識しています。が、それでも[ y ] 値は必須です。コンパイラは賢いかもしれませんが、その次元に整数、文字、浮動小数点数などが何個あるかは*認識*できません。この点にご注意ください。 + +### 二次元配列の初期化 + +多次元配列は、各行に括弧[]で囲まれた値を指定することで使用できます。下の配列は3行で、各行に4列あります。楽がしたいなら、3を省略して空白のままにしても問題ありません。 + + int a[3][4] = { + {0, 1, 2, 3} , /* initializers for row indexed by 0 */ + {4, 5, 6, 7} , /* initializers for row indexed by 1 */ + {8, 9, 10, 11} /* initializers for row indexed by 2 */ + }; + +必要な行を示す内側の括弧は、オプションです。なくてもかまいませんん。したがって、以下の初期化は、上の例と同じです。 + + int a[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11}; + +### 二次元配列の要素にアクセスする + +二次元配列の要素には、添字、つまり配列の行インデックスと列インデックスを使ってアクセスします。例えば、 + + int val = a[2][3]; + +というステートメントは、配列の 3 行目で 4 列目の要素を取得します。 + +Exercise +-------- + +数学と物理の2つの科目について、5人の生徒グループの平均点を調べてみましょう。そのためには、「grades」という2次元配列を使用します。数学の点数は1行目(「grades[0]」)に格納され、物理の点数は2行目(「grades[1]」)に格納されます。このプログラムを実行するには、以下の手順を実行してください。 + +- 成績を整数の2次元配列として宣言する +- 終了条件を指定してforループを完了する +- 各科目の平均点を計算する + +Tutorial Code +------------- + + #include + + int main() { + /* TODO: ここで2次元配列の grades を宣言します */ + float average; + int i; + int j; + + grades[0][0] = 80; + grades[0][1] = 70; + grades[0][2] = 65; + grades[0][3] = 89; + grades[0][4] = 90; + + grades[1][0] = 85; + grades[1][1] = 80; + grades[1][2] = 80; + grades[1][3] = 82; + grades[1][4] = 87; + + /* TODO: 適切な終了条件で for ループを完了する */ + for (i = 0; i < ; i++) { + average = 0; + for (j = 0; j < ; j++) { + average += grades[i][j]; + } + + /* TODO: 科目 i の平均点を計算する */ + printf("The average marks obtained in subject %d is: %.2f\n", i, average); + } + + return 0; + } + + +Expected Output +--------------- + + The average marks obtained in subject 0 is: 78.80 + The average marks obtained in subject 1 is: 82.80 + +Solution +---- + + #include + + int main() { + int grades[2][5]; + float average; + int i; + int j; + + grades[0][0] = 80; + grades[0][1] = 70; + grades[0][2] = 65; + grades[0][3] = 89; + grades[0][4] = 90; + + grades[1][0] = 85; + grades[1][1] = 80; + grades[1][2] = 80; + grades[1][3] = 82; + grades[1][4] = 87; + + for (i = 0; i < 2; i++) { + average = 0; + + for (j = 0; j < 5; j++) { + average += grades[i][j]; + } + + average /= 5.0; + printf("The average marks obtained in subject %d is: %.2f\n", i, average); + } + + return 0; + } diff --git a/tutorials/learn-c.org/ja/Pointer Arithmetics.md b/tutorials/learn-c.org/ja/Pointer Arithmetics.md new file mode 100644 index 00000000..bad6979a --- /dev/null +++ b/tutorials/learn-c.org/ja/Pointer Arithmetics.md @@ -0,0 +1,186 @@ +Tutorial +-------- + +以前、ポインタとは何か、そしてポインタの操作方法を学びました。このチュートリアルでは、ポインタに対する算術演算について学習します。 +C言語のポインタには、++、--、-、+ といった複数の算術演算を適用できます。 + +### (++) によるポインタのインクリメント + +他の変数と同様に、++ 演算はその変数の値を増加させます。今回の場合、変数はポインタなので、その値を増加させると、ポインタが指すメモリ内のアドレスが増加します。 +この演算を例に、配列と組み合わせてみましょう: + + #include + + int main() + { + int intarray[5] = {10,20,30,40,50}; + + int i; + for(i = 0; i < 5; i++) + printf("intarray[%d] has value %d - and address @ %x\n", i, intarray[i], &intarray[i]); + + + int *intpointer = &intarray[3]; // 配列の4番目の要素を指す + printf("address: %x - has value %d\n", intpointer, *intpointer); // 4番目の要素のアドレスを出力する + + intpointer++; // ポインタのアドレスを増やして配列の5番目の要素を指すようにします。 + printf("address: %x - has value %d\n", intpointer, *intpointer); // 5番目の要素のアドレスを出力する + + return 0; + } + + +### (--) によるポインタの減少 + +前の例で ++ 演算子を使用してポインターの指すアドレスを 1 つ増やしたのと同様に、デクリメント演算子 (--) を使用して、指すアドレスを 1 つ減らすことができます。 + + #include + + int main() + { + int intarray[5] = {10,20,30,40,50}; + + int i; + for(i = 0; i < 5; i++) + printf("intarray[%d] has value %d - and address @ %x\n", i, intarray[i], &intarray[i]); + + + int *intpointer = &intarray[4]; // 配列の5番目の要素を指す + printf("address: %x - has value %d\n", intpointer, *intpointer); // 5番目の要素のアドレスを出力する + + intpointer--; // ポイントのアドレスを減らして、配列の4番目の要素を指すようにする + printf("address: %x - has value %d\n", intpointer, *intpointer); // 4番目の要素のアドレスを出力する + + return 0; + } + +### (+) でポインタを追加する + +先ほど、ポインタの指すアドレスを1ずつ増やしました。次のように整数値で増やすこともできます。 + + #include + + int main() + { + int intarray[5] = {10,20,30,40,50}; + + int i; + for(i = 0; i < 5; i++) + printf("intarray[%d] has value: %d - and address @ %x\n", i, intarray[i], &intarray[i]); + + + int *intpointer = &intarray[1]; // 配列の2番目の要素を指す + printf("address: %x - has value %d\n", intpointer, *intpointer); // 2番目の要素のアドレスを出力する + + intpointer += 2; // ポイントのアドレスを2つシフトして、配列の4番目の要素を指すようにします。 + printf("address: %x - has value %d\n", intpointer, *intpointer); // 4番目の要素のアドレスを出力する + + return 0; + } + +出力では、メモリ内でアドレスが8ステップシフトしていることに注目してください。なぜだろうと疑問に思うかもしれません。 +答えは簡単です。ポインタがint型ポインタであり、int型変数のサイズが4バイトであるため、メモリは4ブロックシフト可能です。 +コードでは、最初のアドレスに2(+2)シフトしたので、2 x 4バイト = 8になります。 + +### (-) によるポインタの減算 + +同様に引き算もできます: + + #include + + int main() + { + int intarray[5] = {10,20,30,40,50}; + + int i; + for(i = 0; i < 5; i++) + printf("intarray[%d] has value: %d - and address @ %x\n", i, intarray[i], &intarray[i]); + + + int *intpointer = &intarray[4]; // 配列の5番目の要素を指す + printf("address: %x - has value %d\n", intpointer, *intpointer); // 5番目の要素のアドレスを出力する + + intpointer -= 2; // ポイントのアドレスを2つシフトして、配列の3番目の要素を指すようにする + printf("address: %x - has value %d\n", intpointer, *intpointer); // 3番目の要素のアドレスを出力する + + return 0; + } + +ここでも、アドレスは 4 バイトのブロック単位でシフトされます (int の場合)。 + +### その他の操作 + +他にも比較演算(>、<、==など)があります。考え方は変数の比較と非常に似ていますが、この場合はメモリアドレスを比較します。 + +Exercise +-------- + +intarray の最後の 3 つのアドレスを、int へのポインターの配列である parray にコピーします。 + +Tutorial Code +------------- + + #include + + int main() { + int intarray[5] = {10,20,30,40,50}; + //-----------------------^ + int *pointer = &intarray[2]; + + // 3つのポインタの配列 + int *parray[3]; + + // intarray の最後の3つのアドレスを parray にコピーします + // parray とポインタを使用します + int i; + for (i = 0; i < 3; i++) { + // Insert code here + } + + // テスト コード + for (i = 0; i < 3; i++) { + if (parray[i] == &pointer[i]) { + printf("Matched!\n"); + } else { + printf("Fail\n"); + } + } + + return 0; + } + + +Expected Output +--------------- + Matched! + Matched! + Matched! + +Solution +---- + #include + + int main() { + int intarray[5] = {10,20,30,40,50}; + //-----------------------^ + int *pointer = &intarray[2]; + + int *parray[3]; + + int i; + for (i = 0; i < 3; i++) { + parray[i] = pointer + i; + } + + for (i = 0; i < 3; i++) { + if (parray[i] == &pointer[i]) { + printf("Matched!\n"); + } else { + printf("Fail\n"); + } + } + + return 0; + } + + \ No newline at end of file diff --git a/tutorials/learn-c.org/ja/Pointers.md b/tutorials/learn-c.org/ja/Pointers.md new file mode 100644 index 00000000..9ef2d27a --- /dev/null +++ b/tutorials/learn-c.org/ja/Pointers.md @@ -0,0 +1,116 @@ +Tutorial +-------- + +ポインタも変数の一種であり、Cプログラミング言語において非常に重要な役割を果たします。ポインタは、以下のような様々な目的で使用されます。 + +* 文字列 +* 動的メモリ割り当て +* 関数の引数を参照渡しする +* 複雑なデータ構造を構築する +* 関数へのポインタ +* 特殊なデータ構造(ツリー、トライなど)を構築する + +その他多数。 + +### ポインタとは何ですか? + +ポインタは本質的には、実際の値自体を保持するのではなく、値を指す **メモリ アドレス** を保持する単純な整数変数です。 + +コンピュータのメモリはデータを格納するための連続的な場所であり、ポインタはそのメモリの特定の部分を指し示します。プログラムでは、ポインタを広範囲のメモリ領域を指すように使用することができます。その範囲は、メモリ上の特定の一点からどれだけの量のデータを読み込むかによって決まります。 + +### ポインタとしての文字列 + +文字列については既に説明しましたが、ここではもう少し深く掘り下げて、C における文字列が実際に何であるかを理解します (C++ と混在する場合、他の文字列と区別するために C 文字列と呼ばれます)。 + +以下を見てください: + + char * name = "John"; + +この行は3つの処理を行います。 + +1. `name` というローカル(スタック)変数をメモリに割り当てます。メモリアドレスを格納する箱となるもので、1文字へのポインタとなります。 +2. 文字列 "John" をプログラムメモリのどこかに出現させます(もちろん、コンパイルおよび実行後)。 +3. `name` 引数を初期化し、`J` 文字のアドレスを格納します。メモリ内の文字列の残りの部分がそれに続きます。 + +`name` 変数に配列としてアクセスしようとすると、それはうまくいって、文字 `J` のASCIIコード値を返します。これは、`name` 変数が実際には文字列の先頭を正確に指しているためです。 + +メモリは連続的であることがわかっているため、メモリ内で次の文字に進むと、文字列の末尾 (ASCIIコード値が 0 の文字、`\0` と表記) に達するまで、文字列内の次の文字を受け取ると想定できます。 + +### デリファレンス(逆参照) + +デリファレンスとは、メモリアドレスを参照する代わりに、ポインタが指している場所に格納されている値を参照する動作です。実はもう、配列でデリファレンスを使用していたのですが、あのときはまだ、そのことを知らなかったのです。例えば、添字演算子 `[0]` は配列の最初の要素にアクセスします。配列は実際にはポインタなので、配列の最初の要素にアクセスすることは、すなわちポインタをデリファレンスすることと、同じ動作になります。ポインタをデリファレンスするには、アスタリスク演算子 `*` を使用します。 + +スタック内の別の変数を指す配列を作成する場合は、次のコードを記述します。 + + /* ローカル変数 a を定義する */ + int a = 1; + + /* ポインタ変数を定義し、&演算子を使用して、aを指す */ + int * pointer_to_a = &a; + + printf("The value a is %d\n", a); + printf("The value of a is also %d\n", *pointer_to_a); + +先ほど作成した変数 `a` のアドレスを得るために `&` 演算子を使用していることに注意してください。 + +次に、アスタリスク演算子を使って、アドレスから逆に、そこに格納されている値を参照しました。値を読むだけでなく、逆参照(デリファレンス)した変数の内容を変更することもできます。 + + int a = 1; + int * pointer_to_a = &a; + + /* 変数 a を変更してみましょう */ + a += 1; + + /* 変数をもう一度変更しました! */ + *pointer_to_a += 1; + + /* 3 と表示されます */ + printf("The value of a is now %d\n", a); + +Exercise +-------- + +`pointer_to_n` というローカル変数 `n` へのポインタを作成し、それを使用して `n` の値を 1 増やします。 + +Tutorial Code +------------- + + #include + + int main() { + int n = 10; + + /* your code goes here */ + + /* testing code */ + if (pointer_to_n != &n) return 1; + if (*pointer_to_n != 11) return 1; + + printf("Done!\n"); + return 0; + } + +Expected Output +--------------- + + Done! + +Solution +---- + + #include + + int main() { + int n = 10; + + int * pointer_to_n = &n; + + *pointer_to_n += 1; + + /* testing code */ + if (pointer_to_n != &n) return 1; + if (*pointer_to_n != 11) return 1; + + printf("Done!\n"); + return 0; + } diff --git a/tutorials/learn-c.org/ja/Recursion.md b/tutorials/learn-c.org/ja/Recursion.md new file mode 100644 index 00000000..d2c41370 --- /dev/null +++ b/tutorials/learn-c.org/ja/Recursion.md @@ -0,0 +1,89 @@ +Tutorial +-------- + +再帰は、関数内に自分自身の呼び出しが含まれている場合に発生します。再帰により、非常に簡潔で洗練された、直感的に理解しやすいコードを作成できます。ただし、再帰が深くなりすぎると、大量のメモリが消費される可能性があります。 + +再帰が使用される一般的な例: + +* リンクリスト、二分木などの再帰データ構造の探索 +* チェスなどのゲームにおけるシナリオの探索 + +再帰は常に2つの主要な部分から構成されます。再帰がいつ終了するかを示す終了ケースと、終了ケースに向かって進む必要がある自分自身の呼び出しです。 + +例えば、この関数は再帰的に加算することで乗算を実行します。 + + #include + + unsigned int multiply(unsigned int x, unsigned int y) + { + if (x == 1) + { + /* 終了させる */ + return y; + } + else if (x > 1) + { + /* 再帰ステップ */ + return y + multiply(x-1, y); + } + + /* xが0のとき */ + return 0; + } + + int main() { + printf("3 times 5 is %d", multiply(3, 5)); + return 0; + } + +Exercise +-------- + +再帰乗算によって階乗を計算する `factorial()` という新しい関数を定義します (5! = 5 x 4 x 3 x 2 x 1)。慣例により、0の階乗は1に等しい (0! = 1) ことに注意してください。 + +Tutorial Code +------------- + + #include + + int main() { + /* テスト用コード */ + printf("0! = %i\n", factorial(0)); + printf("1! = %i\n", factorial(1)); + printf("3! = %i\n", factorial(3)); + printf("5! = %i\n", factorial(5)); + } + + /* ここで関数を定義します(宣言することを忘れないでください) */ + +Expected Output +--------------- + + 0! = 1 + 1! = 1 + 3! = 6 + 5! = 120 + +Solution +---- + + #include + + int factorial(int number); + + int main() { + /* テスト用コード */ + printf("0! = %i\n", factorial(0)); + printf("1! = %i\n", factorial(1)); + printf("3! = %i\n", factorial(3)); + printf("5! = %i\n", factorial(5)); + } + + int factorial(int number) { + if (number > 1) { + return number * factorial(number-1); + } + else { + return 1; + } + } diff --git a/tutorials/learn-c.org/ja/Static.md b/tutorials/learn-c.org/ja/Static.md new file mode 100644 index 00000000..c36552dc --- /dev/null +++ b/tutorials/learn-c.org/ja/Static.md @@ -0,0 +1,102 @@ +Tutorial +-------- + +`static` はCプログラミング言語のキーワードです。変数や関数で使用できます。 + +しかし、同じ`static`でも、変数と関数で、まったく効果が異なる点は、注意が必要です。 + +### static変数とは? + +変数はデフォルトではローカル変数で、それが定義されたスコープ内でのみ有効です。しかし、変数をstatic変数として宣言することで、その変数を含むファイル全体にスコープを拡張できます。その結果、これらの変数はファイル内のどこからでもアクセスできるようになります。 + +レースに参加するランナーの数をカウントしたいという次のシナリオを考えてみましょう。 + + #include + int runner() { + int count = 0; + count++; + return count; + } + + int main() + { + printf("%d ", runner()); + printf("%d ", runner()); + return 0; + } + +`count` は関数の完了と同時にメモリから削除されるので、継続してカウントアップされることはありません。しかし、`static` を使用すると、期待通りの結果が得られます。 + + #include + int runner() + { + static int count = 0; + count++; + return count; + } + + int main() + { + printf("%d ", runner()); + printf("%d ", runner()); + return 0; + } + +### static関数とは? + +C言語では、関数はデフォルトでグローバルです。しかし、`static` を使って関数を宣言すると、その関数のスコープはその関数を含むファイル内に縮小されます。 + +構文は次のようになります。 + + static void fun(void) { + printf("I am a static function."); + } + +### static変数とグローバル変数の違い + +static変数は、その変数を含むファイル内でのみアクセス可能なため、特定のファイル内でのみアクセス可能です。一方、グローバル変数はファイル外からもアクセスできます。 + +Exercise +-------- + +この演習では、staticキーワードを使っていくつかの数値の合計を計算してみましょう。`sum()`関数には、累計を表す変数を渡さないでください。 + +Tutorial Code +------------- + + #include + int sum (int num) { + /** + * * n 個の数値の合計を求める + */ + } + + int main() { + printf("%d ",sum(55)); + printf("%d ",sum(45)); + printf("%d ",sum(50)); + return 0; + } + +Expected Output +--------------- + + 55 100 150 + +Solution +---- + + #include + int sum (int num) { + static int total = 0; + total += num; + return total; + } + + int main() { + printf("%d ",sum(55)); + printf("%d ",sum(45)); + printf("%d ",sum(50)); + return 0; + } + diff --git a/tutorials/learn-c.org/ja/Strings.md b/tutorials/learn-c.org/ja/Strings.md new file mode 100644 index 00000000..b59436d7 --- /dev/null +++ b/tutorials/learn-c.org/ja/Strings.md @@ -0,0 +1,123 @@ +Tutorial +-------- + +### 文字列の定義 + +C言語における文字列は、実際には文字の配列です。C言語におけるポインタの使用は高度なテーマであり、後ほど詳しく説明しますが、ここでは文字配列へのポインタを使用して、単純な文字列を定義します。その方法は以下のとおりです。 + + char * name = "John Smith"; + +このメソッドは、読み取り専用の文字列を作成します。 +操作可能な文字列を定義したい場合は、ローカル文字配列として定義する必要があります。 + + char name[] = "John Smith"; + +この表記法は、配列変数を割り当てて操作できるようにするという点で異なります。空の括弧表記 `[]` は、コンパイラに配列のサイズを自動的に計算するように指示します。これは実際には、明示的に割り当てて文字列の長さに1を加算するのと同じです。 + + char name[] = "John Smith"; + /* is the same as */ + char name[11] = "John Smith"; + +文字列「John Smith」の長さはちょうど10文字ですが、1文字追加する必要があるのは、文字列の終端を示すためです。 +文字列の終端を示す特殊文字(0に等しい)です。文字列の終端を示すのは、プログラムが文字列の長さを把握していないためです。 +コードから判断して、コンパイラのみが文字列の長さを把握します。 + +### printfによる文字列のフォーマット + +次のように、`printf` コマンドを使用して文字列を他の文字列と一緒にフォーマットすることができます。 + + char * name = "John Smith"; + int age = 27; + + /* 'John Smith は 27 歳です。' と出力します。 */ + printf("%s is %d years old.\n", name, age); + +文字列を印刷するとき、次の `printf` ステートメントが新しい行に印刷されるように、改行文字 (`\n`) を追加する必要があることに注意してください。 + +### 文字列の長さ + +関数 'strlen' は、引数として渡す必要のある文字列の長さを返します。 + + char * name = "Nikhil"; + printf("%d\n",strlen(name)); + +### 文字列の比較 + +関数 `strncmp` は2つの文字列を比較し、等しい場合は数値 0 を、異なる場合は異なる数値を返します。 +引数は、比較する2つの文字列と比較する文字列の最大長です。この関数には安全でないバージョンである `strcmp` も存在しますが、使用は推奨されません。例: + + char * name = "John"; + + if (strncmp(name, "John", 4) == 0) { + printf("Hello, John!\n"); + } else { + printf("You are not John. Go away.\n"); + } + +### 文字列の連結 + +関数 'strncat' は、src 文字列の最初の n 文字を destination 文字列に追加します。ここで n は min(n,length(src)) です。 +渡される引数は、 destination 文字列、source 文字列、および n です。n は追加する最大文字数です。例: + + char dest[20]="Hello"; + char src[20]="World"; + strncat(dest,src,3); + printf("%s\n",dest); + strncat(dest,src,20); + printf("%s\n",dest); + +Exercise +-------- + +ポインタ表記法を使用して、文字列 `first_name` を値 `John` で定義し、ローカル配列表記法を使用して、文字列 `last_name` を値 `Doe` で定義します。 + +Tutorial Code +------------- + + #include + #include + int main() { + /* first_name を定義 */ + /* last_name を定義 */ + char name[100]; + + last_name[0] = 'B'; + sprintf(name, "%s %s", first_name, last_name); + if (strncmp(name, "John Boe", 100) == 0) { + printf("Done!\n"); + } + name[0]='\0'; + strncat(name,first_name,4); + strncat(name,last_name,20); + printf("%s\n",name); + return 0; + } + + +Expected Output +--------------- + + Done! + JohnBoe + +Solution +---- + + #include + #include + int main() { + char * first_name = "John"; + char last_name[] = "Doe"; + char name[100]; + + last_name[0] = 'B'; + sprintf(name, "%s %s", first_name, last_name); + if (strncmp(name, "John Boe", 100) == 0) { + printf("Done!\n"); + } + name[0]='\0'; + strncat(name,first_name,4); + strncat(name,last_name,20); + printf("%s\n",name); + return 0; + } diff --git a/tutorials/learn-c.org/ja/Structures.md b/tutorials/learn-c.org/ja/Structures.md new file mode 100644 index 00000000..38a7b9ee --- /dev/null +++ b/tutorials/learn-c.org/ja/Structures.md @@ -0,0 +1,103 @@ +Tutorial +-------- + +C構造体は、内部に複数の名前付き変数を含む特別な大きな変数です。構造体は、構造体とクラスの基本的な基礎です。構造体は以下の目的で使用されます。 + +* データのシリアライズ +* 1つの引数を通じて関数の引数と出力 +* 関連リスト、二分木などのデータ構造 + +構造体の最も基本的な例は、**点**です。点は、xとyという2つの変数を含む単一のエンティティです。点を定義しましょう。 + + struct point { + int x; + int y; + }; + +次に、新しい点を定義し、使用します。関数`draw`が点を受け取り、画面に点を描くと仮定します。構造体を使用せずに、使用するには、各座標用の2つの引数が必要です。 + + /* 10, 5 の位置に点を描く */ + int x = 10; + int y = 5; + draw(x, y); + +構造体を使用して、点を引数として渡すことができます。 + + /* 10, 5 の位置に点を描く */ + struct point p; + p.x = 10; + p.y = 5; + draw(p); + +点の変数にアクセスするには、ドット`.`演算子を使用します。 + +### Typedefs + +typedefを使用することで、型の別名を定義できます。これは構造体とポインタを扱う際に便利です。ここで私たちは、点構造体の長い定義をなんとかしたいと考えています。新しい点を定義するたびに、いちいち`struct`キーワードをつけなくても済むように、以下の構文を使用できます。 + + typedef struct { + int x; + int y; + } point; + +これにより、新しい点を定義する際に、`struct`キーワードが不要になります。 + + point p; + +構造体は、ポインタを含むことができます。これにより、構造体は文字列を保持したり、他の構造体へのポインタを保持したりすることができます。つまり、これが構造体の本当の力です。例えば、以下の方法で乗り物構造体を定義できます。 + + typedef struct { + char * brand; + int model; + } vehicle; + +ブランドは文字列を保持したり、他の構造体へのポインタを保持したりすることができます。つまり、これが構造体の本当の力です。例えば、以下の方法で乗り物構造体を定義できます。 + + vehicle mycar; + mycar.brand = "Ford"; + mycar.model = 2007; + +Exercise +-------- + +新しい構造体を定義してください。構造体の名前は"person"で、`name`という名前の文字列(charへのポインタ)と、`age`という整数を含みます。 + +Tutorial Code +------------- + + #include + + /* typedef構文を使用してここでperson構造体を定義します */ + + int main() { + person john; + + /* testing code */ + john.name = "John"; + john.age = 27; + printf("%s is %d years old.", john.name, john.age); + } + +Expected Output +--------------- + + John is 27 years old. + +Solution +---- + + #include + + typedef struct { + char * name; + int age; + } person; + + int main() { + person john; + + /* testing code */ + john.name = "John"; + john.age = 27; + printf("%s is %d years old.", john.name, john.age); + } diff --git a/tutorials/learn-c.org/ja/Unions.md b/tutorials/learn-c.org/ja/Unions.md new file mode 100644 index 00000000..fa42a109 --- /dev/null +++ b/tutorials/learn-c.org/ja/Unions.md @@ -0,0 +1,156 @@ +Tutorial +-------- + +Cの共用体はCの構造体と基本的に同じですが、複数の変数(それぞれが別々のメモリ領域に割り当てられる)を格納する代わりに、共用体では同じ一つの変数に、複数の名前を付けることができます。それにより、その変数に割り当てられたメモリ領域を、名前ごとに異なる型として扱うことができます(共用体のサイズは、最大の型のサイズに、コンパイラが決定するパディングを加えたものになります)。 + +したがって、変数に割り当てられたメモリをさまざまな方法で読み取りたい場合、たとえば整数を入れた 4 バイトのメモリ領域を 1 バイトずつに分けて読み取りたい場合、次のようにすれば可能です。 + + union intParts { + int theInt; + char bytes[sizeof(int)]; + }; + +ポインタをキャストしたりポインタ演算を使用したりせずに、各バイトを個別に確認できるようになります。 + + union intParts parts; + parts.theInt = 5968145; // 任意の数 > 255 (1 byte) + + printf("The int is %i\nThe bytes are [%i, %i, %i, %i]\n", + parts.theInt, parts.bytes[0], parts.bytes[1], parts.bytes[2], parts.bytes[3]); + + // vs + + int theInt = parts.theInt; + printf("The int is %i\nThe bytes are [%i, %i, %i, %i]\n", + theInt, *((char*)&theInt+0), *((char*)&theInt+1), *((char*)&theInt+2), *((char*)&theInt+3)); + + // あるいは配列構文を使ってもよい……その方が、少しだけわかりやすくなるかも + + printf("The int is %i\nThe bytes are [%i, %i, %i, %i]\n", + theInt, ((char*)&theInt)[0], ((char*)&theInt)[1], ((char*)&theInt)[2], ((char*)&theInt)[3]); + +これを構造体と組み合わせることで、「タグ付き」共用体を作成し、複数の異なる型を1つずつ格納できるようになります。 + +例えば、「数」の構造体が必要だけれど、次のようにはしたくないとします。 + + struct operator { + int intNum; + float floatNum; + int type; + double doubleNum; + }; + +なぜなら、プログラムには変数が多数含まれており、そのすべての変数に必要なメモリは、ちょっとばかり多すぎるから。で、そういうことなら、次のような構造体が使えるかもしれません。 + + struct operator { + int type; + union { + int intNum; + float floatNum; + double doubleNum; + } types; + }; + +この場合、構造体のサイズは int `type` のサイズ + 共用体内の最大の型(double)のサイズになります。ここでは8バイトか16バイトしか節約できませんが、このコンセプトを同様の構造体に適用することはできます。 + +使ってみる: + + operator op; + op.type = 0; // この0は「数」の型がintであること示します。本来は、列挙型またはマクロ定数を使って、0がintであることを、わかりやすくするべきです。 + op.types.intNum = 352; + +また、ユニオンに名前を付けない場合、そのメンバーは構造体から直接アクセスされます。 + + struct operator { + int type; + union { + int intNum; + float floatNum; + double doubleNum; + }; // no name! + }; + + operator op; + op.type = 0; // int + // intNum は共用体の一部ですが、名前が付いていないため、構造体自体から直接アクセスします。 + op.intNum = 352; + +もう 1 つの、おそらくより便利な機能は、常に同じ型の複数の変数があり、名前 (読みやすさのため) とインデックス (反復処理を容易にするため) の両方を使用できるようにしたい場合です。その場合は、次のようにすることができます。 + + union Coins { + struct { + int quarter; + int dime; + int nickel; + int penny; + }; // 匿名の構造体は匿名の共用体と同じように動作し、メンバーには外側のコンテナから直接アクセスします。 + int coins[4]; + }; + +この例では、アメリカ合衆国で一般的に使用される4種類のコインを含む構造体があることがわかります。 + +union によって変数が同じメモリを共有するため、coins 配列は構造体内の各 int と(順番に)一致します。 + + union Coins change; + for(int i = 0; i < sizeof(change) / sizeof(int); ++i) + { + scanf("%i", change.coins + i); // これはよくないコードです! 入力値が常に正当とは限りません! + } + printf("There are %i quarters, %i dimes, %i nickels, and %i pennies\n", + change.quarter, change.dime, change.nickel, change.penny); + + +Exercise +-------- + +21 個の文字と 6 個の整数 (21 / 4 == 5 なので 6 個ですが、5 * 4 == 20 なので、この演習ではあと 1 個必要です) の配列を格納する共用体を作成し、整数を 6 つの指定された値に設定してから、文字配列を一連の文字と文字列の両方として出力します。 + +Tutorial Code +------------- + + #include + + /* ここで共用体を定義します */ + + int main() { + + // このような初期化リストは、共用体/構造体の最初のメンバーに割り当てられます。 + // ここには 6 つの int があるので... + intCharacters = {{1853169737, 1936876900, 1684955508, 1768838432, 561213039, 0}}; + + /* テストコード */ + printf("["); + // 1バイトは終了の0用であり、ループの最後は出力しないため、18までしか進みません。 + for(int i = 0; i < 19; ++i) + printf("%c, ", intCharacters.chars[i]); + printf("%c]\n", intCharacters.chars[19]); + + printf("%s\n", intCharacters.chars); + } + +Expected Output +--------------- + + [I, , u, n, d, e, r, s, t, a, n, d, , U, n, i, o, n, s, !] + I understand Unions! + +Solution +---- + + #include + + union hiddenMessage { + int ints[6]; + char chars[21]; + }; + + int main() { + union hiddenMessage intCharacters = {{1853169737, 1936876900, 1684955508, 1768838432, 561213039, 0}}; + + printf("["); + // 1バイトは終了の0用であり、ループの最後は出力しないため、18までしか進みません。 + for(int i = 0; i < 19; ++i) + printf("%c, ", intCharacters.chars[i]); + printf("%c]\n", intCharacters.chars[19]); + printf("%s\n", intCharacters.chars); + } diff --git a/tutorials/learn-c.org/ja/Variables and Types.md b/tutorials/learn-c.org/ja/Variables and Types.md new file mode 100644 index 00000000..4c587c66 --- /dev/null +++ b/tutorials/learn-c.org/ja/Variables and Types.md @@ -0,0 +1,81 @@ +Tutorial +-------- + +### データ型 + +C言語にはいろいろな変数型がありますが、基本的な型はほんの少しです。 + +* 整数 - 正または負の値をとる整数。`char`、`int`、`short`、`long`、`long long` のいずれかで定義されます。 +* 符号なし整数 - 正の値しかとれない整数。`unsigned char`、`unsigned int`、`unsigned short`、`unsigned long`、`unsigned long long` のいずれかで定義されます。 +* 浮動小数点数 - 実数(小数部を含む数)。`float` と `double` のいずれかで定義されます。 +* 構造体 - 後述の「構造体」セクションで説明します。 + +変数の型によって、その境界が定義されます。 `char` 型の範囲は -128 から 127 までですが、`long` 型の範囲は -2,147,483,648 から 2,147,483,647 までです(`long` 型やその他の数値データ型は、コンピュータによって範囲が異なる場合があります。例えば、64 ビットコンピュータでは -9,223,372,036,854,775,808 から 9,223,372,036,854,775,807 までです)。 + +C にはブール型が存在しないことに注意してください。通常、ブール型は以下の表記法で定義されます。 + + #define BOOL char + #define FALSE 0 + #define TRUE 1 + +C では文字の配列を使用して文字列を定義します。これについては文字列のセクションで説明します。 + +### 変数を定義する + +数値の場合、通常は `int` 型を使用します。今日のほとんどのコンピュータでは、これは 32 ビット数値であり、数値の範囲は -2,147,483,648 から 2,147,483,647 です。 + +変数 `foo` と `bar` を定義するには、次の構文を使用する必要があります。 + + int foo; + int bar = 1; + +変数「foo」は使用できますが、初期化していないため、その内容を知ることはできません。変数「bar」には数値「1」が格納されています。 + +さて、計算してみましょう。「a」、「b」、「c」、「d」、「e」を変数と仮定すると、以下の記法で加算、減算、乗算演算子を使用し、 +「a」に新しい値を代入します。 + + int a = 0, b = 1, c = 2, d = 3, e = 4; + a = b - c + d * e; + printf("%d", a); /* 1-2+3*4 = 11 が出力されます */ + +Exercise +-------- + +次の演習では、数値 `a`、`b`、`c` の合計を出力するプログラムを作成する必要があります。 + +Tutorial Code +------------- + + #include + + int main() { + int a = 3; + float b = 4.5; + double c = 5.25; + float sum; + + /* Your code goes here */ + + printf("The sum of a, b, and c is %f.", sum); + return 0; + } + +Expected Output +--------------- + The sum of a, b, and c is 12.750000. + +Solution +---- + #include + + int main() { + int a = 3; + float b = 4.5; + double c = 5.25; + float sum; + + sum = a + b + c; + + printf("The sum of a, b, and c is %f.", sum); + return 0; + } diff --git a/tutorials/learn-c.org/ja/Welcome.md b/tutorials/learn-c.org/ja/Welcome.md new file mode 100644 index 00000000..b9484849 --- /dev/null +++ b/tutorials/learn-c.org/ja/Welcome.md @@ -0,0 +1,42 @@ +# ようこそ + +learn-c.org の無料インタラクティブ C チュートリアルへようこそ。このウェブサイトは、[Boot.dev の C 言語におけるメモリ管理を学ぶコース](https://www.boot.dev/courses/learn-memory-management-c?promo=LEARNXORG) によってサポートされています。C 言語でのメモリ管理を最初から最後まで学びたい方は、[メンバー登録をしてコード LEARNXORG を入力する](https://www.boot.dev/pricing?promo=LEARNXORG) で初年度の受講料が 25% オフになります! + +経験豊富なプログラマーの方にもそうでない方にも、このウェブサイトは C プログラミング言語を学びたいすべての方を対象としています。 + +ダウンロードは不要です。開始したい章をクリックして、指示に従ってください。頑張ってください! + +learn-c.org は現在構築中です。チュートリアルを投稿していただける場合は、下部の「投稿チュートリアル」をクリックしてください。 + +### 基本を学ぶ + +- [[Hello, World!]] +- [[Variables and Types]] +- [[Arrays]] +- [[Multidimensional Arrays]] +- [[Conditions]] +- [[Strings]] +- [[For loops]] +- [[While loops]] +- [[Functions]] +- [[Static]] + +### 一歩進んで + +- [[Pointers]] +- [[Structures]] +- [[Function arguments by reference]] +- [[Dynamic allocation]] +- [[Arrays and Pointers]] +- [[Recursion]] +- [[Linked lists]] +- [[Binary trees]] +- [[Unions]] +- [[Pointer Arithmetics]] +- [[Function Pointers]] +- [[Bitmasks]] + +### チュートリアルへの貢献 + +詳細はこちら: [[Contributing Tutorials]] + diff --git a/tutorials/learn-c.org/ja/While loops.md b/tutorials/learn-c.org/ja/While loops.md new file mode 100644 index 00000000..59577a74 --- /dev/null +++ b/tutorials/learn-c.org/ja/While loops.md @@ -0,0 +1,109 @@ +Tutorial +-------- + +whileループはforループに似ていますが、機能は少なくなっています。whileループは、while内の条件が真である限り、whileブロックの実行を継続します。例えば、次のコードはちょうど10回実行されます。 + + int n = 0; + while (n < 10) { + n++; + } + +常に true (ゼロ以外) と評価される条件が指定されている場合、while ループは無限に実行することもできます。 + + while (1) { + /* do something */ + } + +### ループ命令文 + +C言語には、あらゆるループタイプで併用される重要なループ命令文が2つあります。それは`break` 命令文と`continue` 命令文です。 + +下の「break」命令文は、whileループが終了しない場合でも、ループを10回実行した後に停止します。 + + int n = 0; + while (1) { + n++; + if (n == 10) { + break; + } + } + +次のコードでは、`continue` 命令文によって `printf` コマンドがスキップされ、偶数のみが出力されます。 + + int n = 0; + while (n < 10) { + n++; + + /* nが奇数であることを確認する */ + if (n % 2 == 1) { + /* whileブロックの先頭に戻る */ + continue; + } + + /* nが偶数の場合にのみこのコードに到達する */ + printf("The number %d is even.\n", n); + } + +Exercise +-------- + +`array` 変数は 10 個の数値のシーケンスで構成されています。while ループ内に 2 つの `if` 条件を記述する必要があります。 +これらの条件は、ループの流れを以下のように変更します(`printf` コマンドは変更しません)。 + +* 現在出力しようとしている数値が 5 未満の場合、出力しません。 +* 現在出力しようとしている数値が 10 より大きい場合、出力せずにループを停止します。 + +反復変数 `i` を進めずに `continue` 導関数を使用しないと、無限ループに陥ることに注意してください。 + +Tutorial Code +------------- + + #include + + int main() { + int array[] = {1, 7, 4, 5, 9, 3, 5, 11, 6, 3, 4}; + int i = 0; + + while (i < 10) { + /* ここにコードを記述します */ + + printf("%d\n", array[i]); + i++; + } + + return 0; + } + +Expected Output +--------------- + + 7 + 5 + 9 + 5 + +Solution +---- + + #include + + int main() { + int array[] = {1, 7, 4, 5, 9, 3, 5, 11, 6, 3, 4}; + int i = 0; + + while (i < 10) { + if(array[i] < 5){ + i++; + continue; + } + + if(array[i] > 10){ + break; + } + + printf("%d\n", array[i]); + i++; + } + + return 0; + }