
C言語入門|10章の練習問題
10章では、ポインタと配列の境界をさらに深く学習し、
memcpy / memcmp、配列を関数に渡す際の振る舞い、
そして スタック vs ヒープのメモリ確保 など、
“実務で頻繁に使われる超重要テーマ” を扱いました。
特に、
- 配列は関数に渡すとポインタになる。
- memcpy はアドレスを起点にバイト単位でコピーする。
- memcmp はバイト列の比較を行う。
- 配列名と &配列名 と &配列[0] の違い。
- malloc によるヒープ領域の確保
こういった「これまでのCの知識を結びつける概念」が一気に登場した章です。
この記事では、10章の理解を定着させるための練習問題を紹介しつつ、
図や表を使って配列・ポインタ・メモリの関係をやさしく説明します。

配列とポインタの関係
配列とアドレスの関係
| インデックス | メモリ内の値 | アドレス |
|---|---|---|
| ages[0] | 10 | 0x1000 |
| ages[1] | 11 | 0x1001 |
| ages[2] | 12 | 0x1002 |
ここで重要なポイント
- ages は「ages[0] のアドレス」として扱われる。
- &ages と &ages[0] は厳密には型が違うが、アドレス値は一致する。
- ages[i] は *(ages + i) と同じ意味
- i[ages] のように逆順でも実は動く(絶対に真似しないほうが良い)
標準関数 memcpy / memcmp の書式と役割
| 関数名 | 書式 | 何をする? |
|---|---|---|
| memcpy | memcpy(コピー先, コピー元, バイト数) | 指定したバイト数を丸ごとコピーする。 |
| memcmp | memcmp(比較A, 比較B, バイト数) | A と B をバイト単位で比較し、同じなら 0 を返す。 |
これらは「配列を丸ごとコピーしたい」「配列同士を比較したい」というときの定番関数です。
スタックとヒープの違い
| 領域 | 用途 | 代表的な確保方法 |
|---|---|---|
| スタック | 関数内の一時変数 | int a; / char name[10]; |
| ヒープ | 長期間保持したいデータ | malloc / free |
10章の練習問題
【練習10-1】(memcpy・memcmp・配列渡しの性質)
次のプログラムについて、問いに答えてください。
プロジェクト名:10-12-1 ソースファイル名: sample10-12-1.c
#include <stdio.h>
#include <string.h>
void showValues(int nums[4])
{
for (int i = 0; i < 4; i++) {
printf("%d番目:%d\n", i + 1, nums[i]);
}
}
int main(void)
{
int x[] = {5, 10, 15, 20};
int y[4];
showValues(x);
memcpy(y, x, sizeof(x));
showValues(y);
if (memcmp(x, y, sizeof(x)) == 0) {
printf("コピーは正常です\n");
}
return 0;
}(1)このプログラムで「特殊構文①と②」が使われている箇所をすべて挙げてください。
(※ 配列名を暗黙にポインタへ変換して扱う部分を指す)
(2)特殊構文を使わず、より明確な記述に書き換えてください。
showValues の引数で添え字演算子を使わないこと。
【解答例】
(1)特殊構文が使われている箇所
- 関数 showValues(int nums[4])
→ 実際には int* nums と同じになる(配列→ポインタ化) - showValues(x)
→ x が「配列名」→「先頭アドレス」へ自動変換 - memcpy(y, x, sizeof(x))
→ x が「&x[0]」と同等として扱われる - memcmp(x, y, sizeof(x))
→ 同上
(2)明確に表現した書き換え例
void showValues(int* nums)
{
for (int i = 0; i < 4; i++) {
printf("%d番目:%d\n", i + 1, *(nums + i));
}
}【練習10-2】(配列名とアドレスの比較)
配列 x[] を定義したとき、次のうち x と同じ意味になるもの をすべて選んでください。
(ア)*x
(イ)&x
(ウ)(x + 0)
(エ)&x[0]
【解答例】
| 選択肢 | 同じ意味? | 解説 |
|---|---|---|
| (ア)*x | × | x はアドレス、*x は中身(値) |
| (イ)&x | × | 型も意味も違う |
| (ウ)*(x + 0) | × | 値になるため × |
| (エ)&x[0] | ○ | x と同じアドレスを指す |
【練習10-3】(ヒープ領域へのコピー)
以下の動作をするように main 関数を書き換えてください。
- x はスタック配列(そのままでOK)
- y はヒープ領域に 4 バイト確保する
- memcpy で x → y へコピー
- コピー後、値を確認して表示
【解答例】
プロジェクト名:10-12-2 ソースファイル名: sample10-12-2.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void showValues(int* nums)
{
for (int i = 0; i < 4; i++) {
printf("%d番目:%d\n", i + 1, nums[i]);
}
}
int main(void)
{
int x[] = {5, 10, 15, 20};
int* y = malloc(sizeof(x)); // ヒープに4要素分
memcpy(y, x, sizeof(x));
showValues(y);
free(y);
return 0;
}【練習10-4】(N[p] を理解する)
次の配列があるとします。
int nums[3] = {8, 9, 10};このとき、次のコードはコンパイルも実行も成功します。
printf("%d\n", 1[nums]);これはなぜ可能なのか、配列アクセスの仕組みを説明してください。
【解答例と解説】
nums[i] は本来、
*(nums + i)として処理されます。
この式は加算なので順序を入れ替えても同じ
*(i + nums)それを角カッコで表せば i[nums] となる。
つまり i[nums] も nums[i] と全く同じ意味 になるため、
1[nums] で nums[1](=9)が表示できる。
ただし、可読性が極端に悪いため 実務では絶対に使わないこと。
