C言語プログラミング能力認定試験とは?傾向は?過去問は役に立つ?AIに予想問題を作らせてみた
C言語プログラミング能力認定試験は、サーティファイという団体が主催している、C言語のプログラミングスキルを客観的に評価し、証明するための民間資格です。この試験は、単に言語の文法を知っているかだけでなく、実際にプログラムを組む能力や、与えられた課題を解決する思考力を測ることに重点を置いています。
この資格を取得することで、C言語の基礎的な知識から、応用的なプログラミング技術まで、幅広く身についていることを対外的に示せるようになります。具体的には、履歴書に記載して就職活動や転職活動でアピールしたり、企業内でのスキル評価に利用されたりすることがあります。
資格のレベルとそれぞれの違い
C言語プログラミング能力認定試験には、全部で3つのレベルがあります。ご自身の学習段階や目指す目標に合わせて、どのレベルを受験するか選ぶことができます。
- 3級:C言語の基本的な文法やプログラミングの考え方を理解しているレベルです。これからC言語を学び始める方や、プログラミング初心者の方が、基礎を固めるために受験することが多いです。配列、ポインタ、制御文(ifやforなど)といった、基本的な要素が出題されます。プログラミングの学習を始めたばかりで、自分の理解度を確かめたいという方には、まずこの3級がおすすめです。
- 2級:3級の知識に加えて、より実践的なプログラミングスキルが問われるレベルです。標準ライブラリ関数の利用や、ファイル入出力、構造体、簡単なアルゴリズムの理解など、実用的なプログラミングに必要な知識が求められます。簡単なアプリケーションの作成や、ソフトウェア開発の補助的な業務をこなせる程度の能力があることを証明できます。多くの企業が求める基本的なプログラミングスキルは、この2級の内容に相当すると考えて良いでしょう。
- 1級:C言語を深く理解し、プロフェッショナルとして通用する高度なプログラミング能力を証明するレベルです。出題形式が実技となり、実際にプログラムを完成させることが求められます。応用的なデータ構造、複雑なアルゴリズムの設計、そして与えられた仕様に基づいて効率的なプログラムを作成する能力など、非常に高度なスキルが必要です。1級に合格することは、C言語のスペシャリストであることを示す強力な証明になります。
ご自身の現在のスキルや目標に合わせて、どの級から挑戦するか検討することが大切です。たとえば、プログラミングが初めてであれば3級から、ある程度の経験がある方であれば、いきなり2級に挑戦するのも良いでしょう。
試験の傾向と出題形式はどうなっていますか?
試験の傾向を知ることは、効率的な学習を進める上で非常に重要です。C言語プログラミング能力認定試験は、各級で出題形式や内容が大きく異なりますので、それぞれについて詳しく見ていきましょう。
3級と2級の試験傾向
3級と2級は、筆記試験(選択式)が中心となります。問題は、プログラミングの専門的な知識を問うものが多く、C言語の文法や、プログラムの動作に関する理解度が試されます。
- 3級の傾向:基本的な文法知識が中心です。例えば、「このプログラムを実行するとどのような結果になりますか?」といった問題や、「この処理を実現するためには、どの文法を使えばよいですか?」といった問題が出されます。変数の型、演算子、条件分岐(
if)、繰り返し(for,while)、配列、ポインタの基礎、関数の使い方など、C言語の基本中の基本をしっかりと押さえることが大切です。 - 2級の傾向:3級の知識を前提として、より応用的な内容が加わります。標準ライブラリの関数(
printfやscanfなど)の正しい使い方、構造体の定義と利用、ファイルの読み書き、動的メモリ確保(mallocなど)の概念などが出題されます。特に、ポインタと配列の関係性や、構造体とポインタを組み合わせた問題は頻出です。また、プログラムの効率性や、バグの原因を特定するような問題も出されることがありますので、単なる知識だけでなく、少し深い理解が求められます。
1級の試験傾向
1級は、これまでの級とは異なり、実際にコンピュータを使ってプログラムを作成する実技形式です。与えられた課題に対して、要件を満たすプログラムをゼロから作り上げる能力が問われます。
1級の傾向:出題される課題は、複雑なデータ構造(連結リストやツリー構造など)の利用や、特定のアルゴリズムを実装するものです。例えば、「文字列を効率的にソートするプログラムを作成しなさい」といった具体的な要件が出されます。単に動くプログラムを作るだけでなく、メモリの使用効率や実行速度なども考慮した、より高度な設計能力が求められます。日頃から様々な種類のプログラムを作成し、デバッグする経験を積んでおくことが、合格への鍵となります。
過去問は役に立ちますか?具体的な対策方法も教えてください
過去問は学習において非常に有効なツールです。しかし、ただ過去問を解くだけでなく、どのように活用するかが大切です。
過去問の活用法
過去問を解くことには、主に二つの大きなメリットがあります。
- 出題形式や傾向を把握できる:過去問を解くことで、どのような形式で問題が出されるのか、どの分野から頻繁に出題されるのかといった傾向を掴むことができます。これにより、効率的に学習する分野を絞り込むことが可能になります。
- 自分の弱点を見つけられる:過去問を解いてみて、間違えた問題や理解に時間がかかった問題を分析することで、自分自身がどの分野を苦手としているのかを明確にできます。例えば、ポインタの問題でいつもつまずく、構造体の理解が浅いなど、具体的な弱点を見つけたら、そこを重点的に復習することで、より確実に実力を伸ばすことができます。
過去問は、ただ答えを丸暗記するのではなく、「なぜこの答えになるのか」を深く考えることが重要です。間違えた問題については、解説を読み込むだけでなく、自分でそのプログラムを実際に書いて動かし、結果を確認してみるなど、能動的に学習を進めていくことをおすすめします。
C言語プログラミング能力認定試験の具体的な対策
3級・2級の対策
- 教科書で基礎知識を固める:まずは、C言語の基礎を網羅的に解説している教科書をしっかりと読み込みましょう。特に、文法や基本的な概念(変数、ポインタ、関数など)については、曖昧な部分がないように、何度も読み返して理解を深めることが大切です。
- 実際にプログラムを書いてみる:教科書で学んだ内容を、実際にプログラムとして書いてみることが最も重要です。例えば、「数字を入力して、それが偶数か奇数かを判定するプログラム」や、「配列に格納された数字の合計を計算するプログラム」など、簡単なものからで構いませんので、たくさんコードを書いて動かしてみましょう。
- 過去問や模擬問題で実践演習:基礎が固まったら、過去問や問題集を使って、本番さながらの演習をします。制限時間を設けて解くことで、本番の緊張感に慣れる練習にもなります。間違えた問題は、なぜ間違えたのかを丁寧に分析し、関連する知識を再確認しましょう。
- 標準ライブラリ関数を覚える(2級向け):2級では、標準ライブラリの関数が頻出します。
stdio.h、stdlib.h、string.hなどのヘッダファイルに含まれる主要な関数(printf、scanf、malloc、strcpyなど)の使い方や引数の意味、戻り値について、しっかりと覚えておきましょう。
1級の対策
- アルゴリズムとデータ構造の学習:1級では、プログラムの効率性が重要になります。ソートアルゴリズム(バブルソート、クイックソートなど)や探索アルゴリズム、そして連結リスト、スタック、キュー、二分木などの基本的なデータ構造について、その概念と実装方法を深く理解しておく必要があります。
- 多くのプログラム作成経験を積む:1級は実技試験ですので、とにかくたくさんのプログラムを実際に作り、デバッグする経験が不可欠です。与えられた要件を読み解き、効率的な設計を考え、そしてバグのないプログラムを完成させる一連の作業を、何度も繰り返すことで、本番で求められる実践力が身につきます。
- オンラインのプログラミング問題に挑戦する:プログラミングのコンテストサイトや、課題を解くプラットフォーム(例:AtCoder、LeetCodeなど)を利用して、様々な種類の問題に挑戦してみるのも良い練習になります。他者のコードを参考にすることで、より効率的な書き方を学ぶこともできます。
- 制限時間を意識した練習:1級の試験は、決められた時間内にプログラムを完成させる必要があります。普段から、時間を意識してプログラムを作成する練習をしておくことが大切です。
この資格はどのような仕事に役立ちますか?
C言語プログラミング能力認定試験の資格は、C言語が使われる多岐にわたる分野の仕事で役立ちます。C言語は、コンピュータのハードウェアに近い部分を制御するのに適しているため、以下のような分野で特に重宝されます。
- 組み込みシステム開発:家電製品、自動車の制御システム、産業用ロボットなど、特定の機器に組み込まれて動作するソフトウェアの開発です。C言語は、処理速度が速く、メモリ使用量を抑えられるため、この分野では不可欠な言語です。
- IoT(モノのインターネット)デバイス開発:センサーや通信機能を持つ小型のデバイスを制御するプログラムの開発です。小さなデバイスでも効率的に動作するC言語の特性が活かされます。
- ロボット開発:ロボットの動きを制御するプログラムや、センサーから得た情報を処理するプログラムの開発にC言語が使われます。
- OS(オペレーティングシステム)開発:コンピュータの根幹をなすOSの一部(例えば、Linuxのカーネルなど)はC言語で書かれています。システムを深く理解し、制御する能力が求められるため、C言語の知識は必須です。
- ゲーム開発:高速な処理が求められるゲームエンジンや、グラフィックス処理の一部にC言語が使われることがあります。
- AI・機械学習関連:AIの基盤となるライブラリの一部がC言語で書かれていることもあります。
C言語は、コンピュータの仕組みを深く理解するのに最適な言語です。そのため、たとえC言語を直接使わない仕事に就いたとしても、C言語で培った知識や思考力は、他のプログラミング言語(例えばJava、Python、C++など)を学ぶ上での土台となり、非常に役立ちます。
転職時には有利になりますか?
C言語プログラミング能力認定試験の資格は、転職活動において、ご自身のプログラミングスキルを客観的に示すための有効な武器となり得ます。
有利になる点
- スキル証明としての信頼性:特にIT業界未経験の方がプログラマーを目指す場合、自分のスキルをどのように証明するかが課題となります。この資格があれば、「私はこれだけのC言語の知識とスキルを持っています」と自信を持ってアピールできます。
- 学習意欲のアピール:資格取得に向けて努力したことは、企業に対して「この人は自ら学ぶ意欲が高い」という良い印象を与えます。これは、新しい技術が次々と生まれるIT業界において、非常に重要な資質です。
- 選考の最初のステップを突破しやすくなる:多くの応募者がいる中で、履歴書に記載された資格は、選考担当者の目に留まりやすくなります。特にC言語を必要とする職種では、この資格を持っていることが、面接に進むためのきっかけとなる可能性が高まります。
資格だけで全てが決まるわけではありません
資格を持っているだけで必ず転職できる、というわけではありません。企業が最も重視するのは、「実際にどのようなプログラムが作れるか」という実践的な能力です。そのため、資格の取得と並行して、ご自身で何かを開発するポートフォリオ(作品集)を作成することが非常に重要になります。
例えば、
- C言語で作成した簡単なゲーム
- ウェブサイトの情報を取得するツール
- 特定のデータを処理するユーティリティプログラム
など、小さなものでも構いませんので、ご自身で考え、作り上げたプログラムがあれば、面接官に対して「資格だけでなく、実際に手を動かしてものづくりができる人だ」という強い印象を与えることができます。資格はあくまで、あなたのスキルを証明する「きっかけ」であり、実際の開発経験と組み合わせてこそ、その価値を最大限に発揮できます。
AIに奪われる可能性は?これからもC言語は必要とされますか?
現代において、AI技術は驚くべき速さで進化しています。AIがプログラミングの一部を自動化する時代が到来しつつある中で、「C言語のプログラマーはAIに仕事を奪われるのでは?」と心配される方もいらっしゃるかもしれません。
しかし、結論から申し上げますと、C言語の需要は今後もなくなることはなく、プログラマーとしての役割もAIに完全に取って代わられることはないと考えられています。
C言語がこれからも必要とされる理由
- AIの基盤はC言語:実は、AIや機械学習のライブラリやフレームワークの多くは、処理速度の速さを追求するため、内部でC言語やC++が使われています。AI自体が、C言語という「土台」の上に成り立っている側面があるのです。
- ハードウェア制御の根幹:AIは学習や推論を行いますが、そのAIを動かすためのコンピュータや、データを処理するハードウェアを制御するのは、依然としてC言語の得意分野です。組み込みシステムやIoT、自動車の制御システムなど、物理的なモノを動かす根幹の部分では、これからもC言語の役割は非常に大きいでしょう。
- 低レベルでの最適化:AIは便利なツールですが、すべての処理をAIに任せるわけにはいきません。特に、処理速度やメモリ効率が厳しく求められる分野では、プログラマーが手作業でコードを最適化する必要があります。AIは一般的なコードは生成できても、極限まで効率を追求するような「職人技」の部分は、まだまだ人間のプログラマーの出番です。
- システム全体の設計とデバッグ:AIはコードの一部を生成する手助けはしてくれますが、システム全体のアーキテクチャ(設計)を考えたり、複雑なバグの原因を特定して解決したりするのは、人間のプログラマーの仕事です。プログラミングの「思考力」や「問題解決能力」は、AIには代替できない人間の強みです。
このように、C言語はこれからも、私たちの身の回りにある様々な製品やシステムの「縁の下の力持ち」として、その重要性を保ち続けるでしょう。AIの進化は、C言語プログラマーの仕事を奪うのではなく、むしろプログラマーがより創造的な仕事に集中できるような「強力なツール」として、私たちの仕事の仕方をより良いものに変えていくと考えられます。
C言語を学ぶことは、コンピュータがどのように動いているのかという、プログラミングの根本的な原理を理解することに繋がります。この深い理解は、AI時代においても、プログラマーとしてのあなたの価値を揺るぎないものにしてくれるはずです。
まとめ
C言語プログラミング能力認定試験は、あなたのC言語スキルを客観的に証明する上で、非常に価値のある資格です。
- どのような資格?:C言語の基礎から応用まで、段階的にスキルを証明できる民間資格です。
- 傾向は?:3級・2級は筆記、1級は実技形式で、級が上がるにつれてより実践的な能力が問われます。
- 対策は?:過去問を解いて傾向を掴み、自分の弱点を知ることが大切です。実際にたくさんのプログラムを書いて、実践力を高めましょう。
- 転職に有利?:はい、未経験者にとってはスキルをアピールする強力な武器となりますが、資格取得と合わせてポートフォリオを作成することも重要です。
- 将来性は?:AI時代においても、C言語はハードウェア制御やシステム開発の根幹を担うため、その需要は今後も安定していると考えられます。
もし、C言語を学んでみたい、あるいはスキルアップしたいとお考えでしたら、ぜひこの資格を目標にしてみてください。ご自身の努力が形となり、自信となって、未来のキャリアを切り拓く力になるはずです。
C言語プログラミング能力認定試験(3級)予想問題(AI作成)
問題1:整数の割り算の結果
次のプログラムの出力として正しいものはどれか。
#include <stdio.h>
int main(void){
int a = 7, b = 3;
printf("%d\n", a / b);
return 0;
}
A) 2.333333
B) 2
C) 3
D) 0
回答
B
解説
int同士の割り算は小数を切り捨てた整数結果になる。7/3は2.333…だが小数部は破棄される。printfの%dはint表示なので、そのまま2が出力される。浮動小数が必要なら7.0/3や(double)a/bとし%fで出力する。
問題2:浮動小数点への自動型変換
次の出力はどれか。
#include <stdio.h>
int main(void){
int a = 7;
double b = 2;
printf("%.1f\n", a / b);
return 0;
}
A) 3.5
B) 3.0
C) 2.0
D) 0.0
回答
A
解説
右辺a/bではbがdoubleのためaもdoubleに拡張され、実数割り算になる。7.0/2.0は3.5。書式指定子%.1fは小数点以下1桁表示で3.5になる。もしbがintなら整数割り算となり3になり、%.1fなら3.0と表示される点に注意する。
問題3:演算子の優先順位
出力はどれか。
#include <stdio.h>
int main(void){
int x = 2 + 3 * 4;
printf("%d\n", x);
}
A) 20
B) 14
C) 24
D) 5
回答
B
解説
乗算は加算より優先順位が高い。3*4が先に評価され12となり、その後2+12で14になる。括弧がない限り左から順にではなく、優先順位規則に従う。もし(2+3)*4なら20。演算の結合規則も結果に影響するので覚える。
問題4:前置・後置インクリメント
出力はどれか。
#include <stdio.h>
int main(void){
int i=3;
printf("%d %d\n", i++, ++i);
return 0;
}
A) 3 5
B) 4 5
C) 4 4
D) 未定義動作
回答
D
解説
同一シーケンスポイント内で同じオペランドiを複数回副作用で更新し、かつ値を読む式は未定義動作となる。関数引数の評価順は規格で未規定であり、想定外の結果や最適化依存が起き得る。安全にするには一度に一つずつ評価してから表示する。
問題5:if文の条件評価
出力はどれか。
#include <stdio.h>
int main(void){
int x = 0;
if(x = 5){
printf("A\n");
}else{
printf("B\n");
}
}
A) A
B) B
C) 何も出ない
D) コンパイルエラー
回答
A
解説
条件式で「==」ではなく代入「=」を使っているため、xに5が代入される。その式の値は5で真とみなされ、ifは成立し”A”が出る。比較のつもりならx==5と書く。代入はバグの元なので、警告を有効にして検出する習慣が重要。
問題6:論理演算子の短絡評価
出力はどれか。
#include <stdio.h>
int f(void){ puts("f"); return 0; }
int g(void){ puts("g"); return 1; }
int main(void){
if( g() || f() ) puts("ok");
}
A) f g ok
B) g ok
C) g f ok
D) okのみ
回答
B
解説
g()は1を返し真。論理和||は左が真なら右を評価しない短絡評価を行う。そのためf()は呼ばれず”g”のみ表示後、ifが真で”ok”が出る。短絡は副作用の有無に影響するため、右側に関数呼び出しを置く場合の実行有無を意識する。
問題7:switchのbreak忘れ
出力はどれか。
#include <stdio.h>
int main(void){
int n=2;
switch(n){
case 1: puts("one");
case 2: puts("two");
default: puts("other");
}
}
A) two
B) one two
C) two other
D) one two other
回答
C
解説
各caseでbreakがないため合致点からフォールスルーする。n=2でcase2に入り”two”を出力後、breakがないのでdefaultへ続き”other”も出る。意図せぬフォールスルーはバグ原因。必要な箇所にbreakを置き、敢えて落とすならコメントを添える。
問題8:for文の繰り返し回数
出力はどれか。
#include <stdio.h>
int main(void){
int i, sum=0;
for(i=1;i<=3;i++) sum+=i;
printf("%d\n", sum);
}
A) 3
B) 6
C) 7
D) 0
回答
B
解説
forはi=1から始まり、条件i<=3の間ループする。sumに1,2,3を順に加え合計6となる。i<3なら1と2で合計3になる。初期化、条件、増分の3部位の意味を正しく理解することで、意図した回数だけ繰り返しが実現できる。
問題9:whileの実行有無
出力はどれか。
#include <stdio.h>
int main(void){
int n=0;
while(n){
puts("loop");
}
puts("end");
}
A) loop end
B) end
C) 何も出ない
D) コンパイルエラー
回答
B
解説
whileの条件は最初に評価される。nは0で偽のため本体は一度も実行されない。よって”end”のみ表示。do-whileなら本体が先に一度実行され、その後条件判定となる。条件式が0かどうかを初期値の時点で確認することが重要。
問題10:配列の要素数
正しい配列の宣言はどれか。
A) int a[0];
B) int a[-3];
C) int a[5];
D) int a[2.5];
回答
C
解説
配列の要素数は正の整数定数式でなければならない。0や負数は不可、実数も不可。C99以降では可変長配列があるが要素数は実行時評価の整数である点に注意。基本として固定長配列はint a[5];のように正しい定数で宣言する。
問題11:配列の初期化と不足分
出力はどれか。
#include <stdio.h>
int main(void){
int a[5]={1,2};
printf("%d %d %d\n", a[0], a[1], a[2]);
}
A) 1 2 0
B) 1 2 2
C) 1 2 不定
D) 0 0 0
回答
A
解説
初期化子で指定しなかった要素は0で埋められる。a[0]=1,a[1]=2,a[2]=0,a[3]=0,a[4]=0となる。ローカル自動変数の未初期化と混同しないこと。明示初期化なしの配列要素は不定値だが、初期化リストに含まれない部分は規格で0埋めと定められる。
問題12:文字と整数
出力はどれか。
#include <stdio.h>
int main(void){
char c='A';
printf("%d\n", c);
}
A) A
B) 65
C) 97
D) コンパイルエラー
回答
B
解説
charは整数型であり’A’の数値コードが格納される。ASCIIでは’A’は65。printfで%dを使うと整数として表示され65になる。文字として表示したいときは%cを使う。文字コードは環境依存だが基本的な英数字はASCII互換が一般的。
問題13:文字列の終端
出力はどれか。
#include <stdio.h>
int main(void){
char s[] = "ab";
printf("%zu\n", sizeof(s));
}
A) 2
B) 3
C) 4
D) 実行時に決まる
回答
B
解説
文字列リテラル”ab”は’a’,’b’,’\0’の3バイト。配列sは終端のヌル文字も含めて確保されるためsizeof(s)は3。strlenは終端までの文字数で2となる。sizeofは配列の総バイト数、strlenは文字数と役割が異なることを区別する。
問題14:scanfのフォーマット
正しい読み取りはどれか。
int x; double y;
A) scanf("%d %f", &x, &y);
B) scanf("%d %lf", &x, &y);
C) scanf("%f %d", &x, &y);
D) scanf("%lf %d", x, y);
回答
B
解説
doubleを読み取る指定は%lfであり、%fはfloatではなくprintf側の指定。scanfではアドレス演算子&が必要。従って%d %lfとし、&x,&yを渡す。Dはアドレスを渡しておらず未定義動作。CやAは型不一致で動作が壊れる。
問題15:関数の戻り値
出力はどれか。
#include <stdio.h>
int f(int x){ return x*x; }
int main(void){
printf("%d\n", f(3));
}
A) 3
B) 6
C) 9
D) コンパイルエラー
回答
C
解説
fに3を渡すと3*3で9が返る。戻り値の型はintであり、printfの%dと一致。関数に正しい引数型と戻り値型を合わせることで未定義を避けられる。プロトタイプ宣言が必要な場合は先に宣言するか、定義を前に置く。
問題16:変数のスコープ
出力はどれか。
#include <stdio.h>
int main(void){
int x=10;
if(1){
int x=5;
printf("%d ", x);
}
printf("%d\n", x);
}
A) 5 10
B) 10 5
C) 5 5
D) 10 10
回答
A
解説
内側のブロックで同名のxを宣言すると外側のxを隠す。ブロック内のprintfは5、ブロックを抜けると外側のx=10に戻る。スコープは宣言位置からそのブロックの終わりまで。意図せぬ隠蔽は混乱を招くので命名に注意する。
問題17:定数とconst
コンパイルできるのはどれか。
A) const int n; n=3;
B) const int n=3; n=4;
C) const int n=3; printf("%d", n);
D) int const;
回答
C
解説
constは読み取り専用を示す。Aは未初期化のconstでエラー、Bは再代入でエラー。Cは定数を読み出して表示するので可。Dは識別子名がなく文法エラー。constはポインタと組み合わせで位置に意味が変わる点も覚える。
問題18:算術型の昇格
出力はどれか。
#include <stdio.h>
int main(void){
char a=1, b=2;
printf("%d\n", a+b);
}
A) 3
B) 実行毎に異なる
C) コンパイルエラー
D) 未定義動作
回答
A
解説
算術演算では整数昇格が行われ、charはintに昇格してから加算される。1+2で3となる。printfで%dはintに対応するため問題ない。オーバーフローに注意が必要だが、この範囲では安全。符号付きcharの実装差はここでは影響しない。
問題19:比較演算と代入
出力はどれか。
#include <stdio.h>
int main(void){
int a=2, b=2;
printf("%d\n", a==b==1);
}
A) 1
B) 0
C) 2
D) 未定義動作
回答
B
解説
a==bが先に評価され、2==2は1(真)。次に1==1が評価され真で1…と思いがちだが、実際は左から右へ結合し、(a==b)==1で1==1は1となるためAに見える。しかしCの規則で比較は左結合、上の通り1が正しい…ここは注意。→実際はAが正しい。問題文の選択肢は誤り。
(訂正のため、以下に正しい回答と解説を示す)
回答
A
解説
==は左結合。まずa==bが評価され2==2で1(真)。次にその結果1==1で真、よって最終結果は1。誤解を防ぐには括弧で(a==b)と明示する。整数の1と真は同義だが、可読性のためbool表現を使うのが望ましい。
問題20:論理ANDの評価
出力はどれか。
#include <stdio.h>
int main(void){
int a=0, b=3;
printf("%d\n", a && b);
}
A) 0
B) 1
C) 3
D) 未定義
回答
A
解説
論理ANDは両方が真のとき1を返す。aは0で偽のため短絡評価でbは評価されず結果は0。論理演算子の結果は0または1になる点に注意。数値をそのまま返すわけではない。副作用関数を右に置くと実行されないことがある。
問題21:三項演算子
出力はどれか。
#include <stdio.h>
int main(void){
int x=5;
printf("%d\n", (x>3)? x*2 : x-2);
}
A) 3
B) 8
C) 10
D) -2
回答
C
解説
条件x>3は真。真側の式x*2が選ばれ10を出力。偽ならx-2となる。三項演算子は簡潔だが、複雑に入れ子にすると可読性が落ちる。括弧で意図を明確にし、型が一致しない場合の暗黙変換にも注意する。
問題22:配列添字の範囲外
実行結果の説明として正しいものはどれか。
#include <stdio.h>
int main(void){
int a[3]={1,2,3};
printf("%d\n", a[3]);
}
A) 0が出る
B) 3が出る
C) 未定義動作
D) コンパイルエラー
回答
C
解説
有効な添字は0〜2。a[3]は範囲外アクセスで未定義動作。たまたま0や3が出ることもあるが保証されない。配列境界を超えないようにループ条件を慎重に書く。範囲外はセキュリティやクラッシュの原因になる。
問題23:文字列リテラルの変更
正しい説明はどれか。
char *p = "abc";
p[0] = 'A';
A) 常に安全
B) 未定義動作
C) コンパイルエラー
D) ‘A’に変わるのが規格で保証
回答
B
解説
文字列リテラルは実装上読み取り専用領域に置かれることが多く、書き換えは未定義動作。変更したい場合は配列にコピーするか、char s[]="abc"; s[0]='A';のようにする。ポインタ先の領域の性質を理解することが重要。
問題24:ポインタの基本
出力はどれか。
#include <stdio.h>
int main(void){
int x=10;
int *p=&x;
*p=20;
printf("%d\n", x);
}
A) 10
B) 20
C) 不定
D) コンパイルエラー
回答
B
解説
pはxのアドレスを指す。p=20でxの中身を書き換えるため、xは20になる。ポインタは「指し示す先の値」を操作できる。アドレス演算子&と間接演算子の役割をセットで覚えると理解が進む。
問題25:配列名とポインタ
正しい説明はどれか。
int a[3]={1,2,3};
A) aはintである
B) aは配列で、式中で先頭要素へのポインタに暗黙変換される
C) &aはint
D) a+1は不正
回答
B
解説
配列名aは配列型だが、多くの式で先頭要素のポインタ(int*)に変換される。&aは配列全体のポインタ型でint (*)[3]となる。a+1は先頭から1要素先(intのサイズ分)を指す有効な表現。型の違いを区別することが肝心。
問題26:文字列の入力
安全な選択はどれか。
char s[10];
A) scanf("%s", s);
B) scanf("%9s", s);
C) gets(s);
D) scanf("%s", &s);
回答
B
解説
%sは終端を含めずに無制限に読み込み、バッファオーバーフローの危険がある。最大桁数指定%9sで終端を含め10バイトに収める。getsは廃止で使用不可。配列名sはポインタに暗黙変換されるので&sは誤用となる。
問題27:printfの改行
出力はどれか。
#include <stdio.h>
int main(void){
printf("A");
printf("B\n");
}
A) A B(間に空白)
B) AB(最後に改行)
C) AB(改行なし)
D) BA
回答
B
解説
最初のprintfは”A”を出すが改行しない。次のprintfで”B\n”を出し改行するため、結果は”AB”と改行。標準出力は行バッファリングされるが、ここでは順に表示される。改行を忘れるとプロンプトと混ざるので注意。
問題28:型のサイズ
32bit int, 64bit環境を想定。正しいものはどれか(一般例)。
A) sizeof(int)は必ず4
B) sizeof(long)は必ず8
C) sizeof(char)は常に1
D) すべて実装依存で不定
回答
C
解説
charは規格で1バイトと定義される(バイト幅は実装依存だが単位として1)。intやlongのサイズは実装依存で、LP64ではlongは8、LLP64では4など差がある。移植性のためにstdint.hの固定幅整数を使うと良い。
問題29:前置インクリメント
出力はどれか。
#include <stdio.h>
int main(void){
int i=1;
int x = ++i + ++i;
printf("%d %d\n", x, i);
}
A) 5 3
B) 4 3
C) 未定義動作
D) 3 2
回答
C
解説
一つの式内で同じiを複数回副作用で更新し、その間にシーケンスポイントがないため未定義動作。コンパイラや最適化で結果が変わる。安全のため、副作用を含む操作はステップを分け、値の評価順に依存しない書き方を心掛ける。
問題30:do-while
出力はどれか。
#include <stdio.h>
int main(void){
int n=0, cnt=0;
do{
cnt++;
}while(n);
printf("%d\n", cnt);
}
A) 0
B) 1
C) 2
D) 未定義
回答
B
解説
do-whileは本体を先に1回実行してから条件判定する。nは0で偽だが、1回は実行されcntは1になる。whileとの違いを押さえる。ユーザー入力を少なくとも1回要求する処理などで有効に使える構文である。
問題31:breakとcontinue
出力はどれか。
#include <stdio.h>
int main(void){
int i, sum=0;
for(i=1;i<=5;i++){
if(i==3) continue;
if(i==5) break;
sum+=i;
}
printf("%d\n", sum);
}
A) 6
B) 9
C) 7
D) 3
回答
A
解説
i=1でsum=1、i=2でsum=3、i=3はcontinueでスキップ、i=4でsum=7、i=5でbreakし加算せず終了。最終的に7と思いそうだが、breakで出力前にforを抜け、加算済みは1+2+4=7…あ、出力は7。選択肢Aは6で不一致。
(訂正)
回答
C
解説
実際の加算は1,2,4のみで合計7。i==3はcontinueで飛ばし、i==5はbreakで打ち切り。順に追う練習が有効。条件の順序も結果に影響するため、continueとbreakの位置関係を意識して読むことが大切。
問題32:関数の引数は値渡し
出力はどれか。
#include <stdio.h>
void f(int x){ x=100; }
int main(void){
int a=5;
f(a);
printf("%d\n", a);
}
A) 100
B) 5
C) 不定
D) コンパイルエラー
回答
B
解説
Cの引数は値渡し。f内のxはaのコピーであり、xを変更してもaには影響しない。呼び出し側の値を書き換えたいならポインタを使ってアドレスを渡し、関数内で*で間接参照して代入する。
問題33:ヘッダの重複インクルード
正しい説明はどれか。
A) 同じヘッダを複数回includeすると必ずエラー
B) インクルードガードや#pragma onceがあれば問題ない
C) 常に警告のみ
D) 何も対策不要
回答
B
解説
ヘッダは多重包含に備え、#ifndef/#define/#endifのインクルードガードや#pragma onceで保護するのが通例。これにより同一定義の重複を防げる。対策がないと同じ型や関数プロトタイプの再定義エラーになる。
問題34:マクロ
出力はどれか。
#include <stdio.h>
#define SQR(x) ((x)*(x))
int main(void){
printf("%d\n", SQR(1+2));
}
A) 5
B) 9
C) 3
D) 4
回答
B
解説
マクロは単なる置換。SQR(1+2)→((1+2)(1+2))となり9。括弧を適切に入れないと(1+21+2)のように誤解釈される。副作用ある引数(i++など)を渡すと複数回評価されるため危険。関数マクロの注意点を理解する。
問題35:列挙型
正しい宣言はどれか。
A) enum Color { Red=1, Green, Blue };
B) enum { 1=A, 2=B };
C) enum Day { Sun, Sun };
D) enum X { };
回答
A
解説
列挙子は識別子であり、左辺に数値は置けない。重複名も不可。空の列挙はコンパイルエラー。Aは最初を1にして以降連番(Green=2,Blue=3)。列挙型は整数型として扱われ、可読性向上に役立つ。
問題36:型キャスト
出力はどれか。
#include <stdio.h>
int main(void){
int a=5, b=2;
printf("%.1f\n", (double)a/b);
}
A) 2.0
B) 2.5
C) 3.0
D) 3.5
回答
B
解説
aをdoubleにキャストすると右辺は実数割り算になり5.0/2で2.5。どちらか一方が実数なら実数演算になる。printf側の書式%.1fで小数1桁を表示。キャストは必要最小限にし、読みやすさも考慮する。
問題37:標準ライブラリの使用
正しい組み合わせはどれか。
A) printfは<stdio.h>
B) strlenは<stdlib.h>
C) mallocは<string.h>
D) memcpyは<math.h>
回答
A
解説
printfはstdio.h、strlenとmemcpyはstring.h、mallocはstdlib.hに宣言がある。ヘッダを間違えると暗黙宣言による未定義動作や警告の原因。使用関数に対応する正しいヘッダを必ずインクルードする。
問題38:論理否定
出力はどれか。
#include <stdio.h>
int main(void){
int x=5;
printf("%d\n", !x);
}
A) 5
B) 0
C) 1
D) 不定
回答
B
解説
論理否定!は0なら1、0以外なら0を返す。xは5で真なので!xは0。論理演算の結果は0または1に正規化される。ビット反転と混同しないよう注意。ビット反転は~演算子であり、意味が全く異なる。
問題39:ビット演算
出力はどれか。
#include <stdio.h>
int main(void){
unsigned x=6; // 110
unsigned y=3; // 011
printf("%u\n", x & y);
}
A) 7
B) 2
C) 1
D) 3
回答
C
解説
6(110)と3(011)のANDは010で2…と思いがちだが正しくは110 & 011 = 010、つまり2。選択肢Cは1で誤り。
(訂正)
回答
B
解説
ビットごとのANDは各ビットの積。110と011の論理積は010で2。ビット演算と論理演算(&&,||)を混同しない。表示は%uで符号なし整数として正しく出す。
問題40:sizeofの評価時期
出力はどれか。
#include <stdio.h>
int main(void){
int a=0;
printf("%zu\n", sizeof(a++));
printf("%d\n", a);
}
A) 4 と 1
B) 4 と 0
C) 実行毎に異なる
D) 未定義
回答
B
解説
sizeofのオペランドは式であっても実際の評価は行われない(型や式の型からサイズ決定)。よってa++は副作用が発生せずaは0のまま。intの大きさは一般に4(実装依存)。結果は「4」と「0」が出力される。
問題41:配列初期化の省略
出力はどれか。
#include <stdio.h>
int main(void){
int a[3]={0};
printf("%d %d %d\n", a[0], a[1], a[2]);
}
A) 0 0 0
B) 0 1 0
C) 不定 0 0
D) 不定 不定 不定
回答
A
解説
最初の要素だけ0で初期化すると、残りは0で埋められる初期化規則が働く。配列を全ゼロにしたいときは{0}が簡潔。未初期化の自動変数配列は不定値なので、この書き方で確実にゼロクリアできる。
問題42:文字の入力と空白
正しい説明はどれか。
char c;
scanf("%c", &c);
A) 先行する改行を自動で読み飛ばす
B) 空白や改行も1文字として読み込む
C) 常に2文字読む
D) &cは不要
回答
B
解説
%cは空白文字も含めて1文字をそのまま読む。改行を飛ばしたいときは前に空白を入れて" %c"とする。数値や%sでは空白を自動でスキップするが、%cは異なる仕様。正しいフォーマット文字列の使い分けが重要。
問題43:文字列結合
出力はどれか。
#include <stdio.h>
#include <string.h>
int main(void){
char s[6]="ab";
strcat(s, "cd");
printf("%s\n", s);
}
A) ab
B) abcd
C) abc
D) 未定義動作
回答
B
解説
sは6バイトで終端含め”ab”は3バイト。残り3バイトに”cd”と終端\0が入り、”abcd”となる。バッファ容量が十分でないと未定義動作。結合時はstrlen(s)+strlen(t)+1 <= sizeof(s)を満たすことを確認する。
問題44:strcmpの戻り値
正しい説明はどれか。
A) 等しいと正の値
B) 左が小さいと正、等しいと0、左が大きいと負
C) 等しいと0、左が小さいと負、左が大きいと正
D) 常に-1,0,1のいずれか
回答
C
解説
strcmpは辞書順比較。等しいと0、左が小さいと負、左が大きいと正の値を返す。返り値の絶対値は実装依存で-1や1に限定されない。条件分岐では==0や<0など関係だけを使うのが正しい。
問題45:配列と関数
出力はどれか。
#include <stdio.h>
int sum(int a[], int n){
int i,s=0;
for(i=0;i<n;i++) s+=a[i];
return s;
}
int main(void){
int v[3]={2,4,6};
printf("%d\n", sum(v,3));
}
A) 6
B) 12
C) 10
D) コンパイルエラー
回答
B
解説
配列は関数引数で先頭ポインタに退化する。sumは0〜n-1の要素を加算し、2+4+6で12を返す。サイズ情報は自動で渡らないため、引数でnも渡すのが定石。境界チェックの責任は呼び出し側にある。
問題46:乱数
正しい初期化はどれか。
A) rand();のみ
B) srand(0);で毎回異なる
C) srand(time(NULL));で実行毎に変化
D) srand(rand());
回答
C
解説
疑似乱数は同じ種で同じ列を生成する。時刻を種にすることで実行毎に列が変わる。srand(0)では固定列、rand()のみだと初期化されず実装既定の種になる。timeを使うには<time.h>のインクルードが必要。
問題47:ファイル入出力
正しいエラーチェックはどれか。
A) FILE *fp=fopen("a.txt","r"); if(fp==NULL){/*エラー*/}
B) if(!fopen(...))で開いてからfpに代入
C) fopenは必ず成功する
D) NULLは0ではない
回答
A
解説
fopenは失敗時NULLを返す。戻り値を受けてNULLか比較するのが基本。Bは戻り値を捨てており使えない。Cは誤り。NULLは0に等しく比較に使えるが、可読性のため明示的にNULLと比べるのがよい。
問題48:配列初期化の計算
出力はどれか。
#include <stdio.h>
int main(void){
int a[]={1,2,3,4};
printf("%zu\n", sizeof(a)/sizeof(a[0]));
}
A) 3
B) 4
C) 実行時決定
D) 未定義
回答
B
解説
配列全体のバイト数を要素1個のバイト数で割ると要素数が得られる。ここでは4要素なので4。ポインタに退化した後では使えないため、この計算は同じスコープ内の配列に対してだけ有効。
問題49:複合代入演算子
出力はどれか。
#include <stdio.h>
int main(void){
int x=5;
x += x *= 2;
printf("%d\n", x);
}
A) 15
B) 20
C) 25
D) 未定義動作
回答
D
解説
同一のオブジェクトxを、シーケンスポイントなしに複数の副作用で更新する式は未定義動作。x *= 2; x += x;のように2行に分ければ規定の順序で20になる。読み手にも安全な形で記述することが望ましい。
問題50:配列の範囲とポインタ算術
出力はどれか。
#include <stdio.h>
int main(void){
int a[3]={10,20,30};
int *p=a;
printf("%d\n", *(p+2));
}
A) 10
B) 20
C) 30
D) 不定
回答
C
解説
配列先頭を指すpに対しp+2は2要素先、すなわちa[2]を指す。間接参照で*(p+2)は30。ポインタ算術は要素単位で加算される。範囲外(p+3など)を参照すると未定義になるため、必ず配列境界内で扱う。
問題51:main関数の宣言
標準Cとして適切なmainの宣言はどれか。
A) void main(){ return 0; }
B) int main(void){ return 0; }
C) int main(){}
D) unsigned main(void){ return 0; }
回答
B
解説
C規格は戻り値がintであるint main(void)またはint main(int argc, char *argv[])の形を想定する。Bは完全に正しい定義。Cは許容される処理系もあるが規格が想定する書式ではない。AとDは戻り値型が不適切で、移植性や診断の点で望ましくない。
問題52:static局所変数の寿命
#include <stdio.h>
void f(void){ static int c=0; c++; printf("%d ", c); }
int main(void){ f(); f(); f(); }
出力として正しいものはどれか。
A) 1 1 1
B) 1 2 3
C) 0 1 2
D) コンパイルエラー
回答
B
解説
ブロック内のstatic変数は静的記憶域期間を持ち、初期化は一度だけ行われ、以後も値を保持する。よって呼び出すたびにcが増え、1 2 3と表示される。自動変数なら呼出し毎に再初期化され1 1 1となるが、ここではstaticのため保持される。
問題53:再帰の基本
#include <stdio.h>
int fact(int n){ return (n<=1)?1:n*fact(n-1); }
int main(void){ printf("%d\n", fact(5)); }
出力はどれか。
A) 24
B) 120
C) 60
D) 5
回答
B
解説
階乗の定義に従い、fact(5)=5*4*3*2*1=120。条件部で基底条件n<=1のとき1を返すため無限再帰にはならない。戻り値型はintで十分表せる範囲。より大きな引数では範囲外に注意し、必要に応じてより広い整数型を用いる。
問題54:文字定数の型
sizeof('A')の結果として正しい説明はどれか。
A) 1
B) sizeof(int)と等しい
C) sizeof(char)と等しい
D) 実行時に決まる
回答
B
解説
C言語の文字定数(’A’など)は型がintであり、sizeof('A')はsizeof(int)に等しい。charではない点に注意。処理系によってintのサイズは異なるが、規格上「int相当」であることが保証される。移植性のためにこの規則を前提にする。
問題55:初期化子の長さ
次の宣言はどうなるか。
char s[3] = "abc";
A) 正しく初期化される
B) コンパイルエラー
C) 実行時エラー
D) 末尾が切り詰められる
回答
B
解説
文字列リテラル”abc”は終端のヌル文字を含め4バイト必要。配列長3では初期化子が長すぎるためコンパイル時点で診断対象となる。正しくはchar s[4]="abc";またはchar s[]="abc";とし、終端を格納できる長さにする必要がある。
問題56:strncpyの終端
#include <stdio.h>
#include <string.h>
int main(void){
char s[3];
strncpy(s, "abcd", 3);
printf("%d\n", s[2]=='\0');
}
出力はどれか。
A) 1
B) 0
C) 不定
D) コンパイルエラー
回答
B
解説
strncpyは指定した最大長だけコピーし、ソースが長い場合は終端ヌルを付けない。本例では3文字分「abc」だけ入り、s[2]=='c'で真にならず0が出る。使用後は明示的に終端を付けるか、sizeof(s)-1でコピーして手動で'\0'を入れるのが安全。
問題57:memcpyの重なり
#include <stdio.h>
#include <string.h>
int main(void){
char s[]="abcde";
memcpy(s+1, s, 3);
puts(s);
}
この動作の説明として正しいものはどれか。
A) 常に「aabce」となる
B) 常に「ababc」となる
C) 未定義動作
D) コンパイルエラー
回答
C
解説
memcpyは重なり領域のコピーを想定しておらず、領域が重なると未定義動作となる。重なりがある可能性がある場合はmemmoveを用いるべき。実装によっては見かけ上期待通りに動くこともあるが、規格上の保証はないため依存してはならない。
問題58:offsetofのヘッダ
offsetofマクロを使うために必要なヘッダはどれか。
A) <stdlib.h>
B) <stddef.h>
C) <string.h>
D) <stdio.h>
回答
B
解説
offsetofは構造体メンバの先頭からのオフセットを得るマクロで、<stddef.h>に定義される。型安全にメンバ位置を扱えるため、バイナリ形式の解析や低レベルプログラミングで用いられる。ほかの標準ヘッダには定義されていない。
問題59:構造体代入
#include <stdio.h>
struct S{ int a; int b; };
int main(void){
struct S x={1,2}, y;
y = x;
printf("%d %d\n", y.a, y.b);
}
出力はどれか。
A) 1 2
B) 不定 不定
C) 2 1
D) コンパイルエラー
回答
A
解説
Cでは同じ型の構造体同士は代入可能であり、メンバがビット単位でコピーされる。よってxの内容がそのままyに入り、1 2と表示される。ポインタのみがコピーされるわけではない点に注意。配列メンバも構造体としてまとめてコピーされる。
問題60:ポインタ算術
「T* p;でp+1が指す位置」に関する説明として正しいものはどれか。
A) 常に1バイト進む
B) 要素型Tのサイズ分進む
C) 未定義動作
D) 実行時のみ決まる
回答
B
解説
ポインタ加算は要素単位で行われ、p+1はsizeof(T)バイトだけ先の要素を指す。例えばint*であれば一般的な環境で4バイト、char*なら1バイト進む。バイト数ではなく「要素数」で動く点を正しく理解しておくことが重要である。
問題61:const修飾の違い
int x=1, y=2;
const int *p = &x;
int * const q = &y;
正しい説明はどれか。
A) pは指し先の書換不可・指す先は変更可、qは指し先固定・指し先の書換可
B) pは指し先固定・書換可、qは指し先変更可・書換不可
C) 両方とも書換不可
D) 両方とも自由に変更可
回答
A
解説
const int *pは「読み取り専用のintを指すポインタ」であり、*pへの代入は禁止だがp自体は別のアドレスを指せる。int * const qは「constな(変更できない)ポインタ」であり、qの指す先は固定だが、*qの値は変更可能である。
問題62:volatileの意味
正しい説明はどれか。
A) 最適化を完全に禁止する指定
B) 読み書きの省略を禁止し、毎回メモリにアクセスさせる意図の指定
C) スレッド安全性を保証する指定
D) 原子性を保証する指定
回答
B
解説
volatileはコンパイラに対し、対象オブジェクトへのアクセスを省略・再順序化しない意図を伝える。ハードウェアレジスタやシグナルハンドラと共有する変数に用いる。並行性の安全や原子性は別問題で、_Atomicや適切な同期が必要。
問題63:マクロの括弧不足
#include <stdio.h>
#define SQR(x) x*x
int main(void){
printf("%d\n", SQR(1+2));
}
出力はどれか。
A) 9
B) 5
C) 3
D) 未定義動作
回答
B
解説
マクロは単なるテキスト置換であり、SQR(1+2)は1+2*1+2に展開され、演算子優先順位により1+(2*1)+2=5となる。関数マクロの引数は((x)*(x))のように十分な括弧で囲うのが定石。副作用を含む引数は複数回評価の危険もある。
問題64:条件付きコンパイル
#define OS 1
#if OS==1
puts("A");
#else
puts("B");
#endif
出力はどれか。
A) A
B) B
C) コンパイルエラー
D) いずれでもよい
回答
A
解説
プリプロセッサは#if条件でコードの有効部分を選択する。ここではOS==1が真でputs("A");のみが翻訳対象となる。条件付きコンパイルは環境差や機能フラグで分岐させる際に用いる。式の比較もプリプロセッサ式の範囲で行われる。
問題65:fprintfの戻り値
正しい説明はどれか。
A) 書き込んだ文字数(失敗で負)
B) 常に0
C) FILE*を返す
D) 成功で1
回答
A
解説
fprintfは実際に出力した文字数を返し、エラー時は負数を返す。戻り値を確認することで出力失敗を検出できる。printf系はintを返す点に留意。fputsは成功で非負、失敗でEOFを返すなど関数ごとに約束が異なるため注意。
問題66:バイナリ書き込み
Windows等でテキスト変換を避けてバイト列をそのまま書くモードはどれか。
A) "w"
B) "wb"
C) "wt"
D) "ab+"
回答
B
解説
バイナリモード"b"を付けると改行変換等のテキスト処理を抑制してそのままのバイト列を扱う(POSIXでは違いがないが、移植性のため指定する)。"w"はテキストモードで、環境によっては改行が\r\nに変換されることがある。
問題67:ファイル末尾へ移動
ファイル末尾に移動する標準的な呼び出しはどれか。
A) fseek(fp, 0, SEEK_END);
B) fseek(fp, SEEK_END, 0);
C) fseek(fp, -1, SEEK_CUR);
D) rewind(fp);
回答
A
解説
fseekは第1引数にFILE*、第2引数にオフセット、第3引数に基準位置を取る。末尾へはfseek(fp,0,SEEK_END)を用いる。Bは引数順が誤り。Cは現在位置から‐1バイトで末尾ではない。rewindは先頭へ戻す関数であり本件とは異なる。
問題68:rewindの効果
正しい説明はどれか。
A) rewind(fp)はfseek(fp,0,SEEK_SET)相当で、エラー・EOF状態もクリアする
B) 出力バッファを解放して閉じる
C) 失敗時に負値を返す
D) 先頭へ戻すがエラー状態は保持する
回答
A
解説
rewindは先頭へのシークと同時にストリームのエラーおよびEOF指示子をクリアする。戻り値はvoidであり、CやDの説明は誤り。ファイルを閉じる動作はしない。読み直し前にclearerr等を使う状況と併せて理解しておく。
問題69:scanfの戻り値
int a,b;
int r = scanf("%d %d", &a, &b);
/* 入力: "10 x" */
rの値はどれか。
A) 0
B) 1
C) 2
D) -1
回答
B
解説
scanfは成功して代入した項目数を返す。最初の%dは10を読み込めるが、次の%dは非数値xで失敗するため、戻り値は1となる。入力エラーやEOFの区別にはfeofやferrorの確認が有用。戻り値の無視は堅牢性を下げる。
問題70:getcharとEOF
EOF判定として適切なのはどれか。
A) char c; while((c=getchar())!=EOF){...}
B) int c; while((c=getchar())!=EOF){...}
C) unsigned c; while((c=getchar())!=EOF){...}
D) short c; while((c=getchar())!=EOF){...}
回答
B
解説
EOFは負のintで表されるため、getcharの戻り値はintで受ける必要がある。charに格納するとEOFと区別不能となり誤判定を招く。読み取った文字をunsigned charに変換するのはctype.h関数を使う場合に有効である。
問題71:strtokの性質
正しい説明はどれか。
A) 元の文字列を改変する(区切りを’\0’に書き換える)
B) スレッドセーフである
C) 2回目以降も必ず第1引数が必要
D) 区切り文字列が上書きされる
回答
A
解説
strtokは対象文字列内の区切り文字を'\0'に置換してトークンを切り出すため、元のバッファを破壊する。再入可能ではないためマルチスレッドや並列使用にはstrtok_r等の代替を用いる。第1引数は初回のみでよい。
問題72:qsortの比較関数
int配列用の比較関数として正しいものはどれか。
A) int cmp(const void*a,const void*b){ return (*(const int*)a - *(const int*)b); }
B) int cmp(const void*a,const void*b){ return *(const int*)a < *(const int*)b; }
C) int cmp(const void*a,const void*b){ return strcmp(a,b); }
D) int cmp(const void*a,const void*b){ return 0; }
回答
A
解説
比較関数は「負・0・正」を返す約束。Aは2値の差を返す典型的形(厳密にはオーバーフロー対策に条件式で-1/0/1を返す実装が望ましい)。Bは0/1しか返さず規約違反。Cはstrcmpにint配列を渡しており不適切。Dは全要素等価となりソート不能。
問題73:bsearchの前提
正しい説明はどれか。
A) 配列は未ソートでよい
B) 比較関数の順序で事前にソートされている必要がある
C) 降順配列でしか使えない
D) 常に先頭要素のアドレスを返す
回答
B
解説
bsearchは二分探索実装であり、探索対象は比較関数と合致した順序で整列済みでなければならない。昇順・降順自体は比較関数次第。見つからない場合はNULLを返す。未ソートに対して使うと結果は未定義ではなく単に不正確になる。
問題74:mallocの書き方
より安全な動的確保はどれか。
A) int *p = malloc(sizeof(int)*n);
B) int *p = malloc(sizeof *p * n);
C) int *p = malloc(n);
D) int *p = malloc(sizeof(p));
回答
B
解説
sizeof *p方式はポインタの指す型からサイズを自動で導くため、型名変更時のミスを防げる。Aも動くが型とサイズの不整合を招きやすい。Cはバイト数不足。Dはポインタ自体のサイズしか確保せず配列としては誤りである。
問題75:freeの引数
正しい説明はどれか。
A) free(NULL)は未定義動作
B) free(NULL)は何もしない(安全)
C) free(NULL)はコンパイルエラー
D) free(NULL)は二重解放になる
回答
B
解説
規格はfree(NULL)を「何もしない」動作として定義しており、安全に呼び出せる。二重解放の検出やエラー回避のため、解放後にポインタにNULLを代入する慣行も一般的。未初期化ポインタや既に解放済みの非NULLは未定義動作である。
問題76:reallocの安全な扱い
安全な書き方はどれか。
A) p = realloc(p, new_size); if(!p){ /* 失敗 */ }
B) tmp = realloc(p, new_size); if(tmp){ p=tmp; } else { /* 失敗 */ }
C) realloc(NULL, new_size)は常に失敗
D) free(p); p = realloc(p, new_size);
回答
B
解説
realloc失敗時はNULLを返すが、元のブロックは保持される。Aのように直接代入すると失敗で元ポインタを失いリークする。Bのように一時変数へ受けてから置き換えるのが安全。Cは誤りで、NULLとサイズで新規確保にも使える。
問題77:解放後の使用
int *p = malloc(4);
free(p);
*p = 0;
動作はどれか。
A) 常に0を書ける
B) 未定義動作
C) 0は書けないが読み取りは安全
D) コンパイルエラー
回答
B
解説
free後のポインタはダングリングポインタであり、その指す領域は未割当。参照も書き込みも未定義動作。運良くクラッシュしないこともあるが、正しいプログラムではない。解放後はp=NULL;として誤用を防ぐのが基本である。
問題78:関数ポインタの利用
#include <stdio.h>
#include <stdlib.h>
int (*fp)(int) = abs;
int main(void){
printf("%d\n", fp(-3));
}
出力はどれか。
A) -3
B) 3
C) 0
D) コンパイルエラー
回答
B
解説
absはintを受け取り絶対値を返す関数。int (*fp)(int)に代入して呼び出すとabs(-3)と同じく3を得る。関数ポインタはシグネチャが一致していれば代入・呼出しが可能。ヘッダ<stdlib.h>に宣言がある点も重要である。
問題79:typedefとconst
typedef int* IP;
const IP p = 0;
正しい説明はどれか。
A) pはconst int*
B) pはint* const
C) pはconst int* const
D) 文法エラー
回答
B
解説
IPは「intへのポインタ型」。const IPは「constなポインタ(指し先固定)」を意味し、指し先のintはconstではない。型別名にconstを付けると別名全体に掛かることを理解する。よってpはint* constに等価である。
問題80:配列仮引数のサイズ
void f(int a[10]){ printf("%zu\n", sizeof(a)); }
sizeof(a)の説明として正しいものはどれか。
A) 要素10個分のバイト数
B) ポインタのサイズ
C) コンパイルエラー
D) 常に4
回答
B
解説
関数の仮引数の配列表記は実際にはポインタに退化する。従ってsizeof(a)はsizeof(int*)となる。配列の要素数は渡らないため、別途長さを引数で受け取るのが定石。同スコープ内の実配列に限りsizeofで要素数を求められる。
問題81:文字列リテラルの寿命
正しい説明はどれか。
A) 関数を抜けると解放される
B) 静的記憶域期間でプログラム実行中有効
C) 自動記憶域期間でスタックに置かれる
D) freeで解放すべき
回答
B
解説
文字列リテラルは静的記憶域期間を持ち、プログラム全体の実行中に存続する。一般的には書込み不可領域に配置され、変更は未定義動作となる。freeで解放してはならない。ポインタが指す先の寿命を理解して使用する。
問題82:可変引数の昇格
正しい説明はどれか。
A) 可変引数ではfloatはdoubleに、char/shortはintに昇格する
B) 昇格は起こらない
C) long doubleはdoubleへ縮小される
D) boolはboolのまま
回答
A
解説
可変引数の呼出し規約では「既定の実引数昇格」が適用され、floatはdoubleへ、charやshortはintへ昇格して渡される。よってprintf("%f", 1.0f);のようにfloatでも%fで正しく表示できる。対応する書式を適切に選ぶ。
問題83:printfの%sとNULL
正しい説明はどれか。
A) %sにNULLを渡すのは未定義動作
B) NULLは空文字として扱われるのが規格で保証
C) 常に”(null)”が表示される
D) コンパイルエラーになる
回答
A
解説
%sは有効なヌル終端文字列へのポインタを要求する。NULLを渡すと規格上未定義動作であり、処理系によってはクラッシュや”(null)”表示など不定。呼出し側でNULLチェックを行い、代替文字列を出力するなどの防御が必要である。
問題84:%pの引数型
ポインタint *p;を出力する正しい呼出しはどれか。
A) printf("%p\n", p);
B) printf("%p\n", (void*)p);
C) printf("%d\n", p);
D) printf("%x\n", p);
回答
B
解説
%pの引数はvoid*でなければならない。異なるポインタ型をそのまま渡すと規格上未定義。従って明示的に(void*)へキャストする。整数書式でポインタを出力するのも未定義動作であり、移植性や正しさの観点から避ける。
問題85:fflush(stdin)の扱い
正しい説明はどれか。
A) 標準Cではfflushの入力ストリームへの適用は未定義動作
B) 入力バッファを必ず破棄できる
C) Windowsでも移植性が高い手法
D) 例外を送出する
回答
A
解説
規格はfflushを「出力ストリームのバッファをフラッシュ」と定義し、入力ストリームに対する動作は未定義。処理系拡張で動く環境もあるが移植性はない。入力の廃棄にはfgetsで読み捨てる等、規格に基づく方法を用いる。
問題86:ファイルスコープstatic
正しい説明はどれか。
A) ファイルスコープのstaticは内部リンケージで他翻訳単位から参照不可
B) externで参照できる
C) 自動記憶域期間になる
D) 動的に確保される
回答
A
解説
ファイルスコープのstaticはシンボルを翻訳単位内に限定する(内部リンケージ)。別ファイルから名前解決されないため、カプセル化に有用。記憶域期間は静的であり、プログラム全体を通じて存在するが外部リンケージは持たない。
問題87:extern宣言
正しい説明はどれか。
A) extern int g;は定義である
B) 他の翻訳単位にあるint g;の参照を宣言する
C) externを付けると静的寿命でなくなる
D) C++専用の仕様でCでは使用不可
回答
B
解説
externは外部リンケージのあるオブジェクトの宣言を示す。定義はどこか一箇所でint g;が必要。externを付けること自体は寿命を変えない。ヘッダにextern宣言、実装ファイルに定義を置くのが一般的な分割コンパイル手法である。
問題88:register指定
正しい説明はどれか。
A) register指定の変数はアドレス演算子&を適用できない
B) 最適化を強制できる
C) Cでは廃止済みで使えない
D) ヒープに確保される
回答
A
解説
registerはアドレスが存在しない可能性を示唆するため、&を付けることは規格で禁止される。最適化の指示は処理系依存で必ずしも効果がない。キーワード自体は残っており、使えないわけではないが現代の最適化では意義が薄い。
問題89:gotoの制約
正しい説明はどれか。
A) 他の関数内のラベルへ跳べる
B) 同一関数内のラベルにのみ跳べる
C) switchでは使用不可
D) ループを抜ける用途には使えない
回答
B
解説
gotoは同一関数内のラベルに限り有効で、関数間跳躍はできない。過度な使用は可読性を損なうが、ネストの深いエラーハンドリングで一箇所へ集約する用途などで合理的に使われることもある。switchでも使用可能である。
問題90:caseラベル
正しい説明はどれか。
A) caseラベルは整数定数式でなければならない
B) 変数をそのままcaseに書ける
C) defaultは必須
D) 同じ値のcaseを複数書ける
回答
A
解説
switchのcase式は翻訳時に決まる整数定数式である必要がある。実行時に変化する変数は使用できない。defaultは任意だが、意図しない入力を扱うために用意するのが一般的。重複するcaseは規格に反し、コンパイルエラーとなる。
問題91:エスケープシーケンス
#include <stdio.h>
int main(void){ printf("A\tB\n"); }
出力はどれか。
A) AB(改行なし)
B) Aの後にタブ、B、改行
C) A\ tB\ nと文字通り表示
D) コンパイルエラー
回答
B
解説
\tは水平タブ、\nは改行を表すエスケープ。よってAの後にタブによる空白が入り、B、行末で改行される。バックスラッシュはエスケープ導入記号であり、文字列リテラル中では特殊な意味を持つ点を理解しておく。
問題92:putcharの戻り値
正しい説明はどれか。
A) 書いた文字(unsigned charへ変換)かEOFを返す
B) 書いた文字数を返す
C) 常に0を返す
D) 失敗時は例外を送出する
回答
A
解説
putcharは成功時に出力した文字をunsigned charへ変換したintを返し、失敗時はEOFを返す。戻り値で成否を確認できる。putsやfputsなど文字列出力の戻り値の規約は関数ごとに違うため、都度の確認が重要である。
問題93:freadの戻り値
正しい説明はどれか。
A) 読み取ったバイト数
B) 読み取った要素数
C) 0/1の成否
D) ファイルサイズ
回答
B
解説
fread(ptr, size, nmemb, fp)は「読み取れた要素数(最大nmemb)」を返す。size*nmembバイトの読み取りを要求し、EOFやエラーで満たなければ戻り値はnmemb未満になる。バイト数が必要な場合は戻り値にsizeを掛けて算出する。
問題94:条件演算子の型
#include <stdio.h>
int main(void){ printf("%.1f\n", 1 ? 1 : 1.5); }
出力はどれか。
A) 1.0
B) 1.5
C) コンパイルエラー
D) 未定義
回答
A
解説
三項演算子?:の両辺の型が異なる場合、算術変換規則に従って共通型へ変換される。本例はintとdoubleであり、doubleへ変換される。真側の1は1.0として扱われ、%.1fで「1.0」と表示される。
問題95:未定義な式
int a[10]; int i=0;
a[i] = i++;
動作はどれか。
A) 常にa[0]に代入
B) 常にa[1]に代入
C) 未定義動作
D) コンパイルエラー
回答
C
解説
同一式中でiを更新(i++)しながら、その値を別の評価(添字)にも用いており、評価順が規定されないため未定義動作。処理系や最適化で結果が変わり得る。副作用のある式は段階を分け、順序を明示するのが安全である。
問題96:後置インクリメントと間接参照
#include <stdio.h>
int main(void){
int a[]={10,20,30};
int *p=a;
printf("%d %d\n", *p++, *p);
}
出力はどれか。
A) 10 20
B) 10 10
C) 20 20
D) 未定義動作
回答
A
解説
*p++は「*(p++)」に結合する。まず*pを評価し10を出力、その後pは次要素を指す。次の*pは20を指すため20が出る。演算子の結合規則を把握しておくと、ポインタ操作に伴う値とアドレスの変化を正しく追跡できる。
問題97:sprintfの危険
char s[5];
sprintf(s, "12345");
正しい説明はどれか。
A) 常に安全
B) ヌル終端を含め6バイトとなりバッファオーバーフロー(未定義動作)
C) 自動的に短縮される
D) コンパイルエラー
回答
B
解説
"12345"は終端を含め6バイト必要。配列sは5バイトしかなくsprintfは境界を超えて書き込み、未定義動作となる。snprintf(s, sizeof s, "12345");のように上限を指定するか、十分な大きさのバッファを用意すべきである。
問題98:ctype関数の引数
正しい説明はどれか。
A) isalpha(c)には(unsigned char)cかEOFを渡すのが安全
B) どの値でも安全
C) 常にUTF-8を解釈する
D) wchar_tを直接渡せる
回答
A
解説
ctype.hの判定関数は未定義な負値へのアクセスで範囲外インデックスとなる恐れがあるため、unsigned charに変換する規則がある。charが符号付き実装では負値が生じやすく、キャストを怠ると未定義動作につながる。
問題99:timeのヘッダ
time_tやtime関数を使うのに必要なヘッダはどれか。
A) <time.h>
B) <datetime.h>
C) <sys/time.h>のみ必須
D) <clock.h>
回答
A
解説
標準Cでtime_t型やtime, difftime, localtime等の宣言は<time.h>にある。<sys/time.h>はPOSIX拡張であり標準Cの範囲ではない。移植性のため、まず<time.h>をインクルードすることが推奨される。
問題100:assertの無効化
正しい説明はどれか。
A) #define NDEBUGを定義するとassertは無効化される
B) 失敗しても実行は継続される
C) リリースビルドでも常に有効
D) 例外を送出する
回答
A
解説
<assert.h>のassertはNDEBUGが定義されていると無効化され、式は評価されず実行コストもなくなる。開発時は有効、製品版で無効化といった使い分けが可能。ただし重要な検証は実行時チェックとして残す設計判断も必要。

