Contents
要点サマリ(C言語 面接向け)
ここでは面接で短時間に伝えるべき要点を箇条で示します。各項目は面接で触れる際の一言解説と実務影響(注意点)を併記しています。
- 型とサイズ: size_t は非負のバッファ長に使い、符号付き演算は明示的に signed 型を用いる。注意: 符号混在比較で誤答しないこと。
- UB(未定義動作): 符号付きオーバーフロー、未初期化読み出し、i = i++ のような未シーケンス副作用は UB。実務影響: 最適化で想定外の挙動になる。
- 入出力安全: fgets/snprintf を基本に戻り値チェック。注意: fgets の改行除去とバッファ長検査を必ず行う。
- 並行性: 単一変数の高頻度更新は atomic、それ以外は mutex。説明時はメモリオーダの意味を一例で示す。
- 規格と実装: C23 の機能はコンパイラ依存。面接では「導入前にビルド環境で -std=c2x/-std=c23 を試す」と述べる。
面接では「前提(型・境界)→安全策(テスト/ツール)→設計判断(atomic/mutex)」の順で短く説明してください。
面接で即使えるチェックリスト(C言語)
このセクションは面接中に即答できる確認項目と、説明テンプレを用意しました。言うべき順序と最短の発話例を含みます。
境界条件と最小テストケース
境界や例外ケースを列挙して、面接で言及する項目をまとめます。
- NULL ポインタ、長さ 0、長さ 1、最大長 -1、メモリ確保失敗
- 負値や符号付き/符号なし混合の比較
- 並行処理ではデータレースと整合性境界(atomic だけで良いか)
実装時の短い発話テンプレ(面接向け)
実装の方針を説明する短文テンプレートを用意しました。簡潔に述べることを優先してください。
- 「前提として NULL を許容しない仕様にし、呼び出し側でチェックします。」
- 「バッファは size_t で管理し、strncpy ではなく snprintf を使い切り捨てを検出します。」
- 「並行更新は atomic を使い、複合的不変条件は mutex で保護します。」
面接ではまず前提を確認し、境界と検査の方針を 1 分以内に述べられるよう練習してください。
C11 / C17 / C23 の実務影響とコンパイラ対応(確認手順含む)
規格差と実装差は実務での採用判断に直結します。ここでは主要な差分と、手元の環境で挙動を確認する具体手順を示します。
C11 の実務的要点
C11 で導入された機能のうち実務で重要な点を短くまとめます。
による原子型とメモリモデル、_Static_assert、_Alignas/_Alignof、_Generic が主要機能です。 - 実務影響: 単純な共有変数は atomic で済むが、複合不変条件は mutex を選ぶ判断が必要です。
C17 の位置づけ
C17 は後方互換性が中心で言語拡張は限定的です。
- 実務的には既存コードの動作変更はほとんどなく、移行コストは低いと説明できます。
C23 の実装状況と確認方法(重要)
C23 の採用は「機能が利用可能か」「標準ライブラリ実装があるか」を確認してから行ってください。実装状況はコンパイラ・標準ライブラリで差があります。
- まず手元でコンパイラに C23 モードを渡してコンパイルできるか試してください。
- 例(GCC/Clang): gcc -std=c2x -x c - -fsyntax-only でサンプルをチェック
- 例(MSVC): cl はコンパイラ固有の対応状況をドキュメントで確認してください
- 規格マクロでの検出例:
-
コンパイル時に STDC_VERSION を出力して判別する方法
c
#include <stdio.h>
int main(void) { printf("%ld\n", (long)__STDC_VERSION__); return 0; } -
ただしマクロ値はコンパイラ実装に依存するため、必ずコンパイラの公式ドキュメントで確認してください。
- 実用的な確認手順:
- 使いたい機能の最小テストを用意して -std=c2x/-std=c23(コンパイラ依存)でビルドし、警告・エラーを確認する。
- feature-test マクロやコンパイラのリリースノートを参照する(例: GCC、Clang、MSVC の公式ドキュメント)。
注意: C23 の機能は部分的にしか実装されていない場合があります。導入前に CI 上でビルドとテストを回してください。
参考(公式・一次情報)
以下は一次情報の代表例です。実装確認や詳細仕様の参照に使ってください。
- WG14(C 規格作業部会): https://www.open-std.org/jtc1/sc22/wg14/
- GCC ドキュメント(C ダイアレクト): https://gcc.gnu.org/onlinedocs/gcc/C-Dialect-Options.html
- Clang ドキュメント: https://clang.llvm.org/
- MSVC 言語互換性: https://learn.microsoft.com/ja-jp/cpp/overview/visual-cpp-language-conformance
- C 標準参照(cppreference): https://en.cppreference.com/w/c
- SEI CERT C/CWE(セキュリティ指針): https://wiki.sei.cmu.edu/confluence/display/c/
C23 は便利な機能が増えていますが、面接では「環境依存を認識しているか」を示す説明を必ず入れてください。
ポインタ・文字列・動的メモリ:UB の検出と回避
ポインタ/配列/動的メモリは UB を誘発しやすい領域です。ここでは代表的な問題と検出・回避手順、実行環境依存の注意点を示します。
代表的な未定義動作と検出ツール
各 UB と対応ツール・フラグを簡潔にまとめます。
- 境界外アクセス(バッファオーバーラン)
- 検出: AddressSanitizer(ASan)、valgrind
-
コンパイル例(GCC/Clang):
sh
gcc -std=c17 -O1 -g -fsanitize=address,undefined -fno-sanitize-recover=all -o prog prog.c -
実務影響: 入力検査と長さ管理を必須にすること
- use-after-free / double free
- 検出: ASan、valgrind
- 対策: free 後にポインタを NULL にする、所有権ルールを明文化
- 未初期化変数の読み出し
- 検出: MemorySanitizer(clang)、valgrind --track-origins=yes
- 注意: MemorySanitizer はプラットフォームとコンパイラに依存するため利用要件を確認すること
- strict aliasing 違反
- 対策: 型の再解釈は memcpy、char* によるアクセスは例外
注意: 各 sanitizer はプラットフォームや最適化レベルで検出能力が異なります。導入前に CI で実行可能か確認してください。
stdio と文字列処理の安全な使い方
文字列操作の安全パターンを示します。コード例と検査手順を明示します。
- fgets の使用と改行除去:
-
使い方:
c
#include <string.h>
char buf[128];
if (fgets(buf, sizeof buf, stdin)) {
buf[strcspn(buf, "\n")] = '\0';
} -
推奨: 戻り値チェックとバッファサイズの明示を必ず行う
- snprintf を使ったコピーと切り捨て検出:
-
使い方:
c
int n = snprintf(dst, sizeof dst, "%s", src);
if (n < 0) { /* エラー処理 */ }
else if ((size_t)n >= sizeof dst) { /* 切り捨て検出 */ } -
gets と strcpy は使用禁止と説明すること。面接では代替 API を即答できるように。
文字列とメモリは必ず長さと戻り値を検査し、サニタイザを CI に組み込む運用を提案できると好印象です。
整数・ビット演算・構造体:移植性と安全実装
整数演算や構造体配置は移植性とパフォーマンスに直結します。ここでは実務で押さえるべき具体策を示します。
符号付きオーバーフローと検出
符号付きオーバーフローは未定義です。検出と回避の実装例を示します。
-
ビルトインを使う例(GCC/Clang):
c
#include <stdbool.h>
bool add_overflow_int(int a, int b, int *res) {
return __builtin_add_overflow(a, b, res);
} -
ビルトインが使えない場合の手法:
c
#include <limits.h>
if (b > INT_MAX - a) { /* overflow */ }
else *res = a + b;
注意: コンパイラ最適化による影響を理解して説明できることが重要です。
大きな積と __int128 の注意点
大きな積の中間でのオーバーフロー回避は実務的に重要ですが、__int128 は実装依存です。
-
実装チェック例:
c
#if defined(__SIZEOF_INT128__)
typedef __int128 i128;
#endif -
推奨パターン:
- 可能なら __builtin_mul_overflow を使う。
- __int128 を使う場合は「GCC/Clang の拡張である」こと、MSVC では非対応である可能性を明示する。
- ポータブル対応が必要なら多倍長ライブラリ(GMP 等)や安全な範囲チェックを使う。
注意: 面接で __int128 を挙げる場合は「拡張である」ことを必ず付け加えてください。
構造体・アラインメント確認法
構造体のサイズと配置は互換性に関わります。確認用 API を示します。
-
オフセットの確認と static assert:
c
#include <stddef.h>
_Static_assert(offsetof(struct S, member) == 4, "offset mismatch"); -
_Alignof / _Alignas を使った説明ができること
- packed 属性の注意: attribute((packed)) はコンパイラ拡張であり、非アラインアクセスのパフォーマンス低下や例外を招く可能性があると説明する。
構造体設計は移植性要件を仕様段階で決め、_Static_assert 等で CI に組み込む運用を提案してください。
並行処理・メモリモデルと関数設計(実務で説明するポイント)
並行性の説明では、実装例と判断基準、検出手段を簡潔に示すことが差につながります。
atomic の初期化と実装依存性
atomic の初期化方法と移植性の注意点を示します。実行時初期化と静的初期化を区別して説明してください。
-
静的初期化(コンパイル時):
c
#include <stdatomic.h>
atomic_int cnt = ATOMIC_VAR_INIT(0); -
実行時初期化:
c
atomic_int cnt;
void init(void) { atomic_init(&cnt, 0); } -
実務注記:
- ATOMIC_VAR_INIT/atomic_init は C11 標準の方法です。
- atomic が lock-free かは atomic_is_lock_free(&cnt) で確認できますが、結果は実装依存です。
注意: atomic 型の初期化やロックフリネスはアーキテクチャによって異なるため、面接では「実装依存である」と明示してください。
メモリオーダの要点と簡潔な例
主要なメモリオーダと簡単な使い分け例を示します。
- memory_order_relaxed: 順序保証なし、単純カウンタ向け
- memory_order_release / acquire: 発行側と購読側の順序を確保するパターンで使用
-
memory_order_seq_cst: 全体で最も強い順序付け
-
例(簡単なカウンタ):
c
void inc(void) {
atomic_fetch_add_explicit(&cnt, 1, memory_order_relaxed);
}
注意: 面接では「なぜそのオーダを選んだか」を一文で説明できるように準備してください。
関数設計のチェックポイント(面接で触れる点)
関数設計で必ず触れるべき点を列挙します。
- プロトタイプの一致、可変長引数の昇格ルール、va_copy の扱い
- restrict の正しい使い方と誤用による UB のリスク
- 再入可能性と副作用の範囲を明示すること
並行処理は「どのレベルで同期を行うか」を明確にして説明することが重要です。atomic/mutex の選択理由を一文で言えるように準備してください。
模擬問題・最小テストケース・面接用発話テンプレ
ここでは実務面接で出題されやすい問題と最小限のテストケース、面接での説明テンプレを示します。解答は簡潔に、境界を必ず示してください。
問題 A: 文字列反転(インプレース)
- 要件: null 終端の可変文字列を反転する
- 最小テストケース:
- 入力: "abc" → 出力: "cba"
- 入力: "" → 出力: ""
- 入力: NULL → no-op
-
実装(例):
c
void reverse(char *s) {
if (!s) return;
char *i = s, *j = s;
while (*j) ++j;
if (j == s) return;
--j;
while (i < j) {
char tmp = *i; *i++ = *j; *j-- = tmp;
}
} -
面接発話: 「前提は null 終端で可変。空文字と NULL をチェックし O(n) 時間、O(1) 空間で処理します。」
問題 B: 単方向リストの反転(インプレース)
- 要件: 先頭ノードを反転して新しい先頭を返す
- 最小テストケース:
- 1 要素、複数要素、NULL
- 面接発話: 「反復法を用いてポインタ操作のみで O(n)/O(1) を達成します。再帰はスタック消費が増えるので避けます。」
問題 C: 加算でのオーバーフロー検出
- 要件: add_with_overflow(a,b,&res) を実装
- 最小テストケース:
- a=1,b=2 → res=3, return false
- a=INT_MAX,b=1 → overflow, return true
- 実装ヒント: __builtin_add_overflow を使うか範囲チェックを行う
- 面接発話: 「signed overflow が未定義であるためビルトインか範囲チェックで検出します。」
問題 D: 高スループットカウンタ(並行)
- 要件: 高スループットで複数スレッドから増分可能なカウンタ
- 実装案:
- 単純: atomic_fetch_add を使用(簡潔)。必要ならスケーラビリティ向けにシャーディングを導入。
- テスト: 多スレッドで N 回増分し合計が期待値になることを検証
- 面接発話: 「atomic で lock-free が期待できない場合はシャード化して競合を減らします。mutex は複合的不変条件で利用します。」
模擬問題では前提・境界・テストケース・時間/空間計算量を必ずセットで示してください。短い発話テンプレを用意して練習しましょう。
まとめ(C言語 面接向け短縮版)
面接では前提確認と境界条件の明示が評価を左右します。未定義動作(UB)は最適化で顕在化するため検出ツール(ASan, UBSan, TSan, MemorySanitizer)を使い、CI に組み込む運用を提案してください。C23 を含む規格機能はコンパイラ実装差があるため、実際に -std=c2x/-std=c23 でサンプルをビルドして動作を確認する習慣を持つと実務で役立ちます。面接では「前提→安全策→設計判断」の順で簡潔に述べ、必要なら実行コマンドやテストケースを示すと効果的です。
参考(公式一次情報へのリンクは本文中に記載しています)