第8章:配列と文字列
この章で学ぶこと
- 配列の基本
- 配列の宣言と初期化
- 配列とループ
- 多次元配列
- 文字列の基礎
- 文字列操作関数
- ヌル終端の重要性
---
8.1 配列とは
同じ型のデータをまとめる
配列(Array)は、同じ型のデータを連続して格納するデータ構造です。
例えば、10人の学生のテスト点数を管理したい場合:
配列を使わない場合:
int score1 = 85;
int score2 = 90;
int score3 = 78;
// ... 10個の変数が必要
配列を使う場合:
int scores[10]; // 10個の整数を格納
配列のイメージ
配列は、番号付きの引き出しの列です:
scores: [85][90][78][92][88][75][95][82][87][91]
index: 0 1 2 3 4 5 6 7 8 9
- 各「引き出し」を要素(Element)と呼びます
- 番号をインデックス(Index)と呼びます
- インデックスは0から始まります(重要!)
---
8.2 配列の宣言と初期化
宣言
型 配列名[要素数];
int numbers[5]; // 5個のint
double prices[10]; // 10個のdouble
char letters[26]; // 26個のchar
初期化
宣言と同時に値を設定できます:
int numbers[5] = {10, 20, 30, 40, 50};
要素数を省略すると、初期値の数から自動で決まります:
int numbers[] = {10, 20, 30, 40, 50}; // 要素数5
一部だけ初期化すると、残りは0になります:
int numbers[5] = {10, 20}; // {10, 20, 0, 0, 0}
すべて0で初期化:
int numbers[5] = {0}; // {0, 0, 0, 0, 0}
要素へのアクセス
配列名[インデックス]
int scores[5] = {85, 90, 78, 92, 88};
printf("%d\n", scores[0]); // 85(最初の要素)
printf("%d\n", scores[2]); // 78(3番目の要素)
printf("%d\n", scores[4]); // 88(最後の要素)
scores[1] = 95; // 2番目の要素を変更
配列の範囲外アクセス
インデックスが範囲外だと、未定義動作になります。
int arr[5] = {1, 2, 3, 4, 5};
printf("%d\n", arr[5]); // 危険!範囲外
printf("%d\n", arr[-1]); // 危険!範囲外
arr[10] = 100; // 危険!メモリ破壊の可能性
コンパイルエラーにならないことが多いので、自分で注意する必要があります。
---
8.3 配列とループ
配列の全要素を処理
配列とループは相性抜群です。
#include <stdio.h>
int main(void)
{
int scores[5] = {85, 90, 78, 92, 88};
// 全要素を表示
for (int i = 0; i < 5; i++) {
printf("scores[%d] = %d\n", i, scores[i]);
}
return 0;
}
出力:
scores[0] = 85
scores[1] = 90
scores[2] = 78
scores[3] = 92
scores[4] = 88
合計と平均
#include <stdio.h>
int main(void)
{
int scores[5] = {85, 90, 78, 92, 88};
int sum = 0;
for (int i = 0; i < 5; i++) {
sum += scores[i];
}
double average = (double)sum / 5;
printf("Sum: %d\n", sum);
printf("Average: %.1f\n", average);
return 0;
}
出力:
Sum: 433
Average: 86.6
最大値・最小値を見つける
#include <stdio.h>
int main(void)
{
int scores[5] = {85, 90, 78, 92, 88};
int max = scores[0];
int min = scores[0];
for (int i = 1; i < 5; i++) {
if (scores[i] > max) {
max = scores[i];
}
if (scores[i] < min) {
min = scores[i];
}
}
printf("Max: %d\n", max);
printf("Min: %d\n", min);
return 0;
}
出力:
Max: 92
Min: 78
配列サイズの計算
配列のサイズを計算で求めることができます:
int arr[] = {1, 2, 3, 4, 5, 6, 7};
int size = sizeof(arr) / sizeof(arr[0]);
printf("Size: %d\n", size); // Size: 7
sizeof(arr): 配列全体のバイト数sizeof(arr[0]): 1要素のバイト数- 割り算すると要素数がわかる
---
8.4 配列を関数に渡す
配列を引数に
void print_array(int arr[], int size)
{
for (int i = 0; i < size; i++) {
printf("%d ", arr[i]);
}
printf("\n");
}
int main(void)
{
int numbers[] = {1, 2, 3, 4, 5};
print_array(numbers, 5);
return 0;
}
配列は参照渡し
通常の変数は値渡しですが、配列は参照渡しのように動作します。関数内で配列を変更すると、元の配列も変わります。
void double_values(int arr[], int size)
{
for (int i = 0; i < size; i++) {
arr[i] *= 2; // 元の配列が変更される
}
}
int main(void)
{
int numbers[] = {1, 2, 3, 4, 5};
printf("Before: ");
for (int i = 0; i < 5; i++) printf("%d ", numbers[i]);
printf("\n");
double_values(numbers, 5);
printf("After: ");
for (int i = 0; i < 5; i++) printf("%d ", numbers[i]);
printf("\n");
return 0;
}
出力:
Before: 1 2 3 4 5
After: 2 4 6 8 10
---
8.5 多次元配列
2次元配列
表形式のデータには2次元配列を使います。
int matrix[3][4]; // 3行4列の配列
イメージ:
列0 列1 列2 列3
行0 [ 0 1 2 3 ]
行1 [ 4 5 6 7 ]
行2 [ 8 9 10 11 ]
初期化
int matrix[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
アクセス
matrix[0][0] // 1(1行目1列目)
matrix[1][2] // 7(2行目3列目)
matrix[2][3] // 12(3行目4列目)
2次元配列のループ
#include <stdio.h>
int main(void)
{
int matrix[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
printf("%3d", matrix[i][j]);
}
printf("\n");
}
return 0;
}
出力:
1 2 3 4
5 6 7 8
9 10 11 12
---
8.6 文字列の基礎
文字列とは
C言語には「文字列型」がありません。代わりに、char型の配列で文字列を表現します。
char str[6] = {'H', 'e', 'l', 'l', 'o', '\0'};
文字列の終わりは \0(ヌル文字) で示します。これをヌル終端と呼びます。
文字列リテラル
ダブルクォートで囲むと、自動的にヌル文字が追加されます:
char str[] = "Hello"; // {'H', 'e', 'l', 'l', 'o', '\0'}
"Hello" は5文字ですが、配列のサイズは6(ヌル文字を含む)です。
文字列の表示
#include <stdio.h>
int main(void)
{
char greeting[] = "Hello, World!";
printf("%s\n", greeting);
return 0;
}
%s は文字列用のフォーマット指定子です。
文字列のサイズに注意
char str[5] = "Hello"; // エラー!ヌル文字の分が足りない
char str[6] = "Hello"; // OK
char str[] = "Hello"; // OK(自動で6になる)
---
8.7 ヌル終端の重要性
なぜヌル終端が必要か
C言語の文字列関数は、\0 を見つけるまで処理を続けます。
char str[] = "Hello";
printf("%s\n", str);
printf は、メモリを順番に読んで \0 が見つかるまで表示します。
ヌル終端がないと...
char str[5] = {'H', 'e', 'l', 'l', 'o'}; // ヌル終端なし
printf("%s\n", str); // 未定義動作!ゴミが表示される可能性
ヌル文字がないと、printf はメモリの他の部分まで読み続けてしまいます。
文字列の長さ
文字列の長さは、ヌル文字を含まない文字数です。
#include <stdio.h>
#include <string.h>
int main(void)
{
char str[] = "Hello";
printf("Length: %zu\n", strlen(str)); // 5
printf("Size: %zu\n", sizeof(str)); // 6(ヌル文字含む)
return 0;
}
---
8.8 文字列操作
strlen - 文字列の長さ
#include <string.h>
char str[] = "Hello";
size_t len = strlen(str); // 5
strcpy - 文字列のコピー
#include <string.h>
char src[] = "Hello";
char dest[10];
strcpy(dest, src); // srcをdestにコピー
printf("%s\n", dest); // "Hello"
注意: dest に十分なサイズが必要です。サイズチェックは行われません。
strncpy - 安全なコピー
char src[] = "Hello, World!";
char dest[6];
strncpy(dest, src, 5);
dest[5] = '\0'; // 手動でヌル終端
printf("%s\n", dest); // "Hello"
strcmp - 文字列の比較
#include <string.h>
char str1[] = "Apple";
char str2[] = "Banana";
int result = strcmp(str1, str2);
if (result < 0)
printf("str1 < str2\n"); // 辞書順で str1 が先
else if (result > 0)
printf("str1 > str2\n");
else
printf("str1 == str2\n");
注意: == では文字列を比較できません。
if (str1 == str2) // これはアドレスの比較!
strcat - 文字列の連結
#include <string.h>
char str1[20] = "Hello, ";
char str2[] = "World!";
strcat(str1, str2);
printf("%s\n", str1); // "Hello, World!"
注意: str1 に十分なサイズが必要です。
---
8.9 文字列の入力
scanf での入力
char name[50];
printf("Enter your name: ");
scanf("%s", name); // スペースまで読む
printf("Hello, %s!\n", name);
注意: scanf("%s", ...) はスペースで区切られます。
Enter your name: John Smith
Hello, John! // "Smith" は読み込まれない
fgets での入力
行全体を読むには fgets を使います。
#include <stdio.h>
int main(void)
{
char line[100];
printf("Enter a line: ");
fgets(line, sizeof(line), stdin);
printf("You entered: %s", line);
return 0;
}
fgets は改行文字も含めて読み込みます。
---
8.10 実践:文字列関数を自作
ft_strlen - 文字列の長さ
int ft_strlen(char *str)
{
int len = 0;
while (str[len] != '\0') {
len++;
}
return len;
}
ft_strcpy - 文字列のコピー
void ft_strcpy(char *dest, char *src)
{
int i = 0;
while (src[i] != '\0') {
dest[i] = src[i];
i++;
}
dest[i] = '\0';
}
ft_strcmp - 文字列の比較
int ft_strcmp(char *s1, char *s2)
{
int i = 0;
while (s1[i] != '\0' && s2[i] != '\0') {
if (s1[i] != s2[i]) {
return s1[i] - s2[i];
}
i++;
}
return s1[i] - s2[i];
}
これらの関数を自作することは、42のPiscineやlibftプロジェクトで求められます。
---
8.11 この章のまとめ
学んだこと
- 配列の基本
- 配列の操作
- 多次元配列
- 文字列
\0 が重要
- "" で自動的にヌル終端される- 文字列関数
strlen: 長さ
- strcpy: コピー
- strcmp: 比較
- strcat: 連結重要な注意点
- インデックスは0から:
arr[n]の最後の要素はarr[n-1] - 範囲外アクセス禁止: コンパイルエラーにならないので自分で注意
- 文字列はヌル終端必須: 忘れると未定義動作
- 文字列比較は
strcmp:==ではアドレス比較になる - バッファサイズに注意:
strcpyなどはサイズチェックしない
次のステップ
この章で学んだ配列と文字列は、Cプログラミングの基礎です。
42のPiscineでは:
- これらの概念を深く理解していることが前提
- 標準関数を使わず自作することが求められる
- ポインタとの関係(次のレベル)も重要
この教材で基礎を固めて、Piscineに臨みましょう!
---
Column: 配列とメモリ
配列のメモリ配置
配列の要素は、メモリ上に連続して配置されます。
int arr[4] = {10, 20, 30, 40};
メモリ上のイメージ(intが4バイトの場合):
アドレス: 0x1000 0x1004 0x1008 0x100C
値: 10 20 30 40
インデックス: [0] [1] [2] [3]
このため、arr[i] は実際には「配列の先頭アドレス + i × 要素サイズ」の位置にアクセスしています。
配列とポインタ
C言語では、配列名は先頭要素へのポインタとして扱われます。これは中級者向けの重要な概念ですが、今は「配列は連続したメモリ領域」と覚えておけば十分です。
なぜインデックスは0から?
多くの言語で配列のインデックスが0から始まるのは、C言語の影響です。
0から始めると、arr[i] の計算が単純になります:
- 0始まり:
先頭アドレス + i × サイズ - 1始まり:
先頭アドレス + (i - 1) × サイズ
1を引く必要がないため、わずかに効率的です。1970年代のコンピュータでは、この違いも重要でした。
---
確認問題
問題1
以下の配列宣言で、要素数はいくつですか?int arr[] = {5, 10, 15, 20, 25};
解答
5
初期値の数から自動で決まる。
問題2
配列int arr[5] の最後の要素にアクセスするには?解答
arr[4]
インデックスは0から始まるので、要素数5の配列の最後は arr[4]。
問題3
以下の文字列の配列サイズは?char str[] = "Hello";
解答
6
"Hello" は5文字だが、ヌル文字 \0 が追加されるので、サイズは6。
問題4
2つの文字列が等しいか比較するには、どの関数を使いますか?解答
strcmp
== ではアドレスの比較になってしまう。
if (strcmp(str1, str2) == 0) {
// 等しい
}
問題5
配列の全要素を0で初期化する最も簡単な方法は?解答
int arr[10] = {0};
最初の要素を0で初期化すると、残りも自動的に0になる。
---
おわりに
おめでとうございます!この教材の全8章を学び終えました。
学んだこと:
- プログラミングの基本概念
- ターミナル操作とvim
- C言語のHello World
- 変数と型
- 条件分岐
- ループ
- 関数
- 配列と文字列
これらは、42のPiscineで必要となる基礎知識です。
Piscineは「C言語を学ぶ場所」ではなく「C言語で問題を解く場所」です。この教材で基礎を固めておけば、Piscineで課題に集中できます。
次のステップ:
- コードをたくさん書く(練習あるのみ!)
- エラーと仲良くなる
- 分からないことを調べる習慣をつける
- 仲間と協力する
42での学習を心から応援しています。頑張ってください!