変数・代入のトレース
T-01 変数
int a = 5 , b = 10 ;
a = b;
b = a + 3 ;
a = b - a;
実行後の a, b の値は?
解答を見る
1行目: a=5, b=10 2行目: a=10 , b=10(aにbの値を代入) 3行目: a=10, b=13 (10+3) 4行目: a=3 , b=13(13-10)答え: a=3, b=13
T-02 変数
int x = 7 ;
x = x * 2 ;
x += 3 ;
x %= 5 ;
x++;
最終的な x の値は?
解答を見る
x=7 → x=14 (7*2) → x=17 (14+3) → x=2 (17%5=余り2) → x=3 (2+1)答え: x=3
T-03 変数
int a = 3 , b = 5 , tmp;
tmp = a;
a = b;
b = tmp;
実行後の a, b, tmp の値は?(これは何をしている?)
解答を見る
tmp=3 (aの元の値を退避) → a=5 (bの値をaに) → b=3 (tmpからaの元の値をbに)答え: a=5, b=3, tmp=3 これは値の交換(swap) の典型パターンです。
T-04 変数
int a = 10 ;
int b = a / 3 ;
int c = a % 3 ;
double d = (double )a / 3 ;
b, c, d の値は?
解答を見る
b = 10/3 = 3 (整数除算で切り捨て) c = 10%3 = 1 (余り) d = 10.0/3 = 3.333... (キャストでdouble除算に)答え: b=3, c=1, d=3.333333
T-05 変数
int a = 1 ;
a += a++; // 未定義動作に注意
printf ("%d\n" , a);
出力は何? ヒント:これは「罠」問題です。
解答を見る
未定義動作 です。同じ変数を1つの式の中で変更と参照を同時に行うと、C言語の仕様上結果は保証されません。 コンパイラによって 2 や 3 など異なる結果が出ます。教訓: ++ と代入を同じ式で混ぜない。
T-06 変数
int i = 0 , sum = 0 ;
while (i < 5 ) {
sum += i;
i++;
}
各ループ後の i, sum を表で追跡せよ。
解答を見る
回 i(加算前) sum += i i++後
1 0 0+0=0 1
2 1 0+1=1 2
3 2 1+2=3 3
4 3 3+3=6 4
5 4 6+4=10 5
答え: i=5, sum=10 (0+1+2+3+4)
T-07 変数
int n = 12345 , rev = 0 ;
while (n > 0 ) {
rev = rev * 10 + n % 10 ;
n /= 10 ;
}
各ループ後の n, rev を追跡せよ。このプログラムは何をしている?
解答を見る
回 n%10 rev n
1 5 0*10+5=5 1234
2 4 5*10+4=54 123
3 3 54*10+3=543 12
4 2 543*10+2=5432 1
5 1 5432*10+1=54321 0
答え: n=0, rev=54321 これは
整数の桁を逆順にする プログラムです。
配列のトレース
T-08 配列
int a[5 ] = {3 , 1 , 4 , 1 , 5 };
a[0 ] = a[4 ];
a[2 ] = a[0 ] + a[1 ];
a[4 ] = a[2 ] - a[3 ];
配列 a の最終状態は?
解答を見る
初期: {3,1,4,1,5} a[0]=a[4] → {5 ,1,4,1,5} a[2]=a[0]+a[1] → {5,1,6 ,1,5} a[4]=a[2]-a[3] → {5,1,6,1,5 }答え: {5, 1, 6, 1, 5}
T-09 配列
int a[4 ] = {10 , 20 , 30 , 40 };
for (int i = 0 ; i < 3 ; i++) {
a[i] = a[i + 1 ];
}
配列 a の最終状態は? これは何をしている?
解答を見る
i=0: a[0]=a[1] → {20 ,20,30,40} i=1: a[1]=a[2] → {20,30 ,30,40} i=2: a[2]=a[3] → {20,30,40 ,40}答え: {20, 30, 40, 40} 先頭要素を削除して左にシフトする処理(末尾が残る)。
T-10 配列
int a[5 ] = {2 , 8 , 3 , 9 , 1 };
int max = a[0 ], idx = 0 ;
for (int i = 1 ; i < 5 ; i++) {
if (a[i] > max) {
max = a[i];
idx = i;
}
}
各ステップで max, idx がどう変わるか追跡せよ。
解答を見る
i a[i] a[i]>max? max idx
初期 - - 2 0
1 8 8>2 Yes 8 1
2 3 3>8 No 8 1
3 9 9>8 Yes 9 3
4 1 1>9 No 9 3
答え: max=9, idx=3
T-11 配列
int a[5 ] = {5 , 3 , 8 , 1 , 4 };
// バブルソート1回目(最小値を先頭に)
for (int j = 4 ; j > 0 ; j--) {
if (a[j-1 ] > a[j]) {
int tmp = a[j-1 ]; a[j-1 ] = a[j]; a[j] = tmp;
}
}
1パス後の配列の状態を追跡せよ。
解答を見る
初期: {5,3,8,1,4} j=4: a[3]>a[4]? 1>4 No → {5,3,8,1,4} j=3: a[2]>a[3]? 8>1 Yes → {5,3,1,8 ,4} j=2: a[1]>a[2]? 3>1 Yes → {5,1,3 ,8,4} j=1: a[0]>a[1]? 5>1 Yes → {1,5 ,3,8,4}答え: {1, 5, 3, 8, 4} (最小値1が先頭に来る)
T-12 配列
int a[3 ][3 ] = {{1 ,2 ,3 },{4 ,5 ,6 },{7 ,8 ,9 }};
int sum = 0 ;
for (int i = 0 ; i < 3 ; i++) {
sum += a[i][i];
}
sum の値は? この処理は何を計算している?
解答を見る
a[0][0]=1, a[1][1]=5, a[2][2]=9 sum = 1+5+9 = 15 これは行列の対角線上の要素の合計(トレース) です。
ポインタのトレース
T-13 ポインタ
int a = 10 , b = 20 ;
int *p = &a;
*p = 30 ;
p = &b;
*p = *p + 5 ;
実行後の a, b, *p の値は?
解答を見る
p=&a → *p=30 → a=30 p=&b → *p=20+5=25 → b=25 答え: a=30, b=25, *p=25
T-14 ポインタ
int a = 5 , b = 10 ;
int *p = &a, *q = &b;
*p = *q;
*q = 0 ;
printf ("%d %d\n" , a, b);
出力は?
解答を見る
*p=*q → a=10 (bの値をaに) *q=0 → b=0 答え: 10 0
T-15 ポインタ
int arr[4 ] = {10 , 20 , 30 , 40 };
int *p = arr;
printf ("%d " , *p);
p += 2 ;
printf ("%d " , *p);
printf ("%d " , *(p - 1 ));
printf ("%d\n" , p[1 ]);
出力は?
解答を見る
p=arr → *p=10 p+=2 → pはarr[2]を指す → *p=30 *(p-1)=arr[1]=20 p[1]=*(p+1)=arr[3]=40 答え: 10 30 20 40
T-16 ポインタ
int arr[5 ] = {1 , 2 , 3 , 4 , 5 };
int *p = arr + 1 ;
int *q = arr + 3 ;
printf ("%d %d %ld\n" , *p, *q, q - p);
出力は?
解答を見る
p=&arr[1] → *p=2 q=&arr[3] → *q=4 q-p = 3-1 = 2 (要素数の差)答え: 2 4 2
T-17 ポインタ
int x = 100 ;
int *p = &x;
int **pp = &p;
**pp = 200 ;
printf ("%d %d %d\n" , x, *p, **pp);
出力は?(ダブルポインタ)
解答を見る
pp→p→x の連鎖。**pp=200 は x を200に変更。 x=200 , *p=200 , **pp=200 (全部同じ場所を指す)答え: 200 200 200
T-18 ポインタ
int a[] = {10 , 20 , 30 , 40 , 50 };
int *p = a;
for (int i = 0 ; i < 5 ; i++) {
*(p + i) *= 2 ;
}
配列 a の最終状態は?
解答を見る
各要素を2倍: {20, 40, 60, 80, 100} *(p+i) は a[i] と同じ。ポインタ演算で配列を操作。
関数呼び出しのトレース
T-19 関数
void add_ten (int x) { x += 10 ; }
int main (void ) {
int a = 5 ;
add_ten (a);
printf ("%d\n" , a);
}
出力は?
解答を見る
答え: 5 Cは値渡し。add_tenのxはaのコピー なので、xを変えてもaには影響しない。
T-20 関数
void add_ten (int *px) { *px += 10 ; }
int main (void ) {
int a = 5 ;
add_ten (&a);
printf ("%d\n" , a);
}
出力は?(T-19との違いに注目)
解答を見る
答え: 15 ポインタ経由で渡しているので、*px += 10 は a 自体を変更する。
T-21 関数
int mystery (int a, int b) {
a = a + b;
b = a - b;
a = a - b;
return a * 10 + b;
}
printf ("%d\n" , mystery (3 , 7 ));
出力は?(関数内の a, b を1行ずつ追跡)
解答を見る
a=3, b=7 a=3+7=10 b=10-7=3 a=10-3=7 return 7*10+3 = 73 これは一時変数なしのswap 。a,bが交換されて 7*10+3=73。
T-22 関数
void modify (int arr[], int n) {
for (int i = 0 ; i < n; i++) arr[i] *= 3 ;
}
int main (void ) {
int data[] = {1 , 2 , 3 };
modify (data, 3 );
printf ("%d %d %d\n" , data[0 ], data[1 ], data[2 ]);
}
出力は?(配列の値渡しとポインタ渡しの違い)
解答を見る
答え: 3 6 9 配列を関数に渡すとポインタとして渡される ので、関数内の変更は呼び出し元に反映される。intの値渡し(T-19)とは違う!
T-23 関数
int factorial (int n) {
if (n <= 1 ) return 1 ;
return n * factorial (n - 1 );
}
printf ("%d\n" , factorial (5 ));
コールスタックの展開と戻りを追跡せよ。
解答を見る
factorial(5) → 5 * factorial(4) → 5 * 4 * factorial(3) → 5 * 4 * 3 * factorial(2) → 5 * 4 * 3 * 2 * factorial(1) → 5 * 4 * 3 * 2 * 1 (ベースケース) = 5*4*3*2*1 = 120
総合問題
T-27 総合
struct Point { int x, y; };
struct Point a = {3 , 7 };
struct Point b = a;
b.x = 10 ;
printf ("a=(%d,%d) b=(%d,%d)\n" , a.x, a.y, b.x, b.y);
出力は?(構造体の代入は値コピー?参照?)
解答を見る
答え: a=(3,7) b=(10,7) 構造体の代入は値コピー 。bはaの独立したコピーなので、b.xを変えてもa.xは変わらない。
T-28 総合
struct Point { int x, y; };
struct Point p = {1 , 2 };
struct Point *ptr = &p;
ptr->x = 10 ;
ptr->y = ptr->x + 5 ;
printf ("(%d,%d)\n" , p.x, p.y);
出力は?(アロー演算子経由の変更)
解答を見る
ptr->x=10 → p.x=10 ptr->y=10+5=15 → p.y=15 答え: (10,15)
T-29 総合
int a[3 ] = {1 , 2 , 3 };
int *p = a;
for (int i = 0 ; i < 3 ; i++) {
a[i] = a[(2 -i)];
}
配列aの最終状態は?(これは「逆順」にならない。なぜ?)
解答を見る
i=0: a[0]=a[2] → {3 ,2,3} i=1: a[1]=a[1] → {3,2 ,3} i=2: a[2]=a[0] → {3,2,3 }(a[0]は既に3に変わっている!)答え: {3, 2, 3} ({3,2,1}にはならない)理由: 前半の要素を上書きした後に、その値を使ってしまうため。正しい逆順には一時配列が必要。
T-30 総合
int f (int *a, int *b) {
*a += *b;
*b = *a - *b;
return *a + *b;
}
int main (void ) {
int x = 4 , y = 6 ;
int z = f (&x, &y);
printf ("%d %d %d\n" , x, y, z);
}
出力は?(ポインタ経由での変更を追跡)
解答を見る
*a=&x, *b=&y *a += *b → x=4+6=10 *b = *a-*b → y=10-6=4 return 10+4=14 答え: 10 4 14
T-31 総合
int a[5 ] = {5 , 2 , 8 , 1 , 9 };
for (int i = 0 ; i < 4 ; i++) {
if (a[i] > a[i+1 ]) {
int t = a[i]; a[i] = a[i+1 ]; a[i+1 ] = t;
}
}
配列aの最終状態は? 何が保証される?
解答を見る
i=0: 5>2 → swap → {2,5 ,8,1,9} i=1: 5>8? No → {2,5,8,1,9} i=2: 8>1 → swap → {2,5,1,8 ,9} i=3: 8>9? No → {2,5,1,8,9}答え: {2, 5, 1, 8, 9} 最大値(9)が末尾に来ること が保証される(バブルソート1パス・前方走査版)。
T-32 総合
int count = 0 ;
for (int i = 1 ; i <= 100 ; i++) {
if (i % 3 == 0 && i % 5 == 0 )
count++;
}
countの最終値は?(全部追跡しなくてOK。規則を見つけよ。)
解答を見る
3と5の両方で割り切れる → 15の倍数 をカウント。 1〜100の15の倍数: 15,30,45,60,75,90 の6個 。答え: count=6
T-33 総合
int fib (int n) {
if (n <= 1 ) return n;
return fib (n-1 ) + fib (n-2 );
}
printf ("%d\n" , fib (6 ));
fib(6)の値と、fib関数が合計何回呼ばれるか答えよ。
解答を見る
fib(6)=fib(5)+fib(4)=5+3=8 呼び出し回数: fib(6)→fib(5),fib(4)→...と展開していくと25回 fib(0)=0, fib(1)=1, fib(2)=1, fib(3)=2, fib(4)=3, fib(5)=5, fib(6)=8
T-34 総合
char *p = "ABCDE" ;
printf ("%c %c %s\n" , *p, *(p+2 ), p+3 );
出力は?(文字列リテラルのポインタ操作)
解答を見る
*p = 'A' *(p+2) = 'C' p+3 = "DE"(p+3以降の文字列)答え: A C DE
T-35 総合
int m[2 ][3 ] = {{1 ,2 ,3 },{4 ,5 ,6 }};
int result = 0 ;
for (int i = 0 ; i < 2 ; i++)
for (int j = 0 ; j < 3 ; j++)
if (m[i][j] % 2 == 0 ) result += m[i][j];
resultの値は?
解答を見る
偶数の要素: 2, 4, 6 result = 2+4+6 = 12
T-36 総合
int a = 0xAB ; // 16進数
int hi = (a >> 4 ) & 0x0F ;
int lo = a & 0x0F ;
printf ("hi=%d lo=%d\n" , hi, lo);
出力は?(ビット演算のトレース)
解答を見る
0xAB = 1010 1011 (2進) a>>4 = 0000 1010 → & 0x0F → 10 (0x0A) a & 0x0F = 0000 1011 → 11 (0x0B)答え: hi=10 lo=11 (上位4bit=A=10, 下位4bit=B=11)
T-37 総合
int gcd (int a, int b) {
while (b != 0 ) {
int tmp = b;
b = a % b;
a = tmp;
}
return a;
}
printf ("%d\n" , gcd (48 , 18 ));
各ループの a, b, tmp を追跡せよ。
解答を見る
回 a b tmp a%b
1 48 18 18 48%18=12
2 18 12 12 18%12=6
3 12 6 6 12%6=0
終了 6 0 - -
答え: 6 (48と18の最大公約数)
T-38 総合
int *p = (int *)malloc (sizeof (int ) * 3 );
p[0 ] = 10 ; p[1 ] = 20 ; p[2 ] = 30 ;
int *q = p;
free (p);
// printf("%d\n", *q); ← これは安全?
free後に *q にアクセスするとどうなる?
解答を見る
未定義動作(ダングリングポインタ) qはpと同じ場所を指していたが、free(p)で解放済み。qはまだ同じアドレスを指しているが、その領域はもう使えない。教訓: free後は関連するポインタも全てNULLにする。
T-39 総合
int a[4 ] = {10 , 20 , 30 , 40 };
int *p = a, *q = a + 3 ;
while (p < q) {
int t = *p; *p = *q; *q = t;
p++; q--;
}
配列aの最終状態は? 何をしている?
解答を見る
1回目: p=a[0],q=a[3] → swap(10,40) → {40 ,20,30,10 }, p++,q-- 2回目: p=a[1],q=a[2] → swap(20,30) → {40,30 ,20 ,10}, p++,q-- p>=q → 終了答え: {40, 30, 20, 10} これは配列の逆順(in-place reverse) 。両端から中心に向かってswapする。
T-40 総合
int binary_search (int a[], int n, int key) {
int lo = 0 , hi = n - 1 ;
while (lo <= hi) {
int mid = (lo + hi) / 2 ;
if (a[mid] == key) return mid;
else if (a[mid] < key) lo = mid + 1 ;
else hi = mid - 1 ;
}
return -1 ;
}
// a = {2, 5, 8, 12, 16, 23, 38, 56}, key = 23
lo, hi, mid の変化を追跡し、何回のループで見つかるか答えよ。
解答を見る
回 lo hi mid a[mid] 判定
1 0 7 3 12 12<23 → lo=4
2 4 7 5 23 23==23 → 発見!
答え: 2回目で index=5 を返す