C ドキュメント
簡潔で実例中心の C リファレンス。概念を読み、コードを見て、Coddy のジャーニーで実践しましょう。
はじめに
- C言語とはCは1972年に生まれた小さく速いコンパイル型言語で、今なおOS、データベース、そしてほぼすべての組み込み機器を動かしています。それが何であり、どこで動き、2026年に学ぶ価値がある理由を紹介します。
- C のインストール (GCC)どの環境でも動く C コンパイラを入れる: Windows なら MinGW-w64、macOS なら Apple の clang、Linux なら gcc - さらにインストールが成功したかの確認方法と、最初のファイルのコンパイルまで。
- コンパイルと実行.c ファイルを動くプログラムに変える方法: gcc コマンド、プリプロセス・コンパイル・リンクが実際に行っていること、最初の日から使う価値のあるフラグ、そして失敗したときのエラーの読み方。
- C言語の構文どの C プログラムも同じ骨組みを持っています。インクルード、main 関数、セミコロンで終わる文、そして波かっこで囲まれたブロックです。それぞれの役割と、コンパイラが実際に強制する規則を見ていきます。
- コメントC言語には 2 つのコメント記法 - 1 行の // と複数行の /* */ - があり、それぞれ異なる歴史と、ネストにまつわる 1 つの罠を抱えています。両方の使い方と、コメントする価値があるもの・ないものを見ていきます。
変数と型
- 変数C言語で変数を宣言する方法、宣言と初期化の違い、命名の規則、まとめて宣言すること、そして未初期化の変数が初心者の最頻出バグである理由。
- データ型C言語のすべての変数には、サイズと保持できるものを決める型があります。ここでは基本型のすべて、実際のサイズ、signed と unsigned、sizeof、limits.h、そしてオーバーフロー時に何が起こるかを扱います。
- 定数C言語で変化しない値に名前を付ける3つの方法 - const キーワード、#define マクロ、enum 定数 - のそれぞれの実体と、どれをいつ選ぶか。
- 演算子知っておく価値のある C の演算子をひと通り(算術・代入・比較・論理・インクリメント)、さらに整数除算の罠、負の数での剰余の振る舞い、そして信頼できる優先順位表。
- 真偽値C言語には C99 まで真偽型がありませんでした。ゼロが偽、それ以外はすべて真、比較は int を返す - そして stdbool.h が bool・true・false を上乗せしてくれます。この設計が生む = と == のバグも解説します。
- 型キャストC言語は絶えず型のあいだで値を変換します - キャストで自分から頼んだときもあれば、勝手に行われることのほうが多いのです。ここでは昇格のルール、明示的なキャストの構文、そして静かにデータを失う変換を扱います。
制御フロー
- if / elseC言語はどう判断するのか: if 文、else、else if の連鎖、ネスト、三項演算子 - さらに真偽の規則(0 が偽、それ以外はすべて真)と、条件を静かに壊す `=` と `==` のバグ。
- switchC言語のswitch文が定数のリストから1つの分岐を選ぶ仕組み - caseラベル、breakが重要な理由、意図的なフォールスルーとうっかりのフォールスルー、default、charやenumでの分岐、そしてswitchがelse-ifの連鎖に勝る場面。
- for ループC言語のforループでコードを繰り返す方法 - 3つのパートからなるヘッダー、増加・減少のカウント、sizeofの技を使った配列の走査、ネストしたループ、無限ループ、そして誰もがはまる off-by-one と符号なし型のバグ。
- while ループC言語のwhileループが条件の変化まで繰り返す仕組み - 条件が先という規則、番兵ループ、EOFまでの入力読み取り、while(1)、そして for と while の相互変換。
- do-while ループC言語の本体優先のループ: do-while が判定の前に1回の実行を保証する仕組み、なぜセミコロンで終わるのか、そのために作られたメニューと再試行のパターン、そして普通の while ループとの違い。
- break と continuebreak はループや switch から抜け、continue は次の周回へ飛ばします。for・while・do-while それぞれでの振る舞い、フラグや goto でネストしたループから脱出する方法、そしてループを追いにくくする誤用。
関数
- 関数C言語で関数を定義して呼び出す方法 - 戻り値の型、引数、戻り値、void 関数、分割(デコンポジション)がなぜ大切か、そして小さな名前付きの部品から組み立てた完全な実例。
- 関数パラメータC言語はすべてを値で渡します - 関数が受け取るのはコピーであって、元の変数ではありません。それが実際に何を意味するか、ポインタで参照渡しをどう模倣するか、なぜ配列だけ振る舞いが違うのか、そして複数の結果を返す方法。
- 関数プロトタイプなぜC言語は呼び出す前に関数の形を知る必要があるのか - プロトタイプの書き方、implicit declaration エラーの直し方、ヘッダファイルにプロトタイプを置くこと、そして f(void) と f() の本当の違い。
- 再帰C言語の関数が自分自身を呼ぶ仕組み - それを止めるベースケース、階乗とフィボナッチの詳解、素朴なフィボナッチが壊滅的に遅い理由、スタックオーバーフローの正体、そしてループのほうが良い場面。
- スコープC言語の変数がどこから見えていつまで生きるか - ブロックスコープ、関数の引数、ファイルスコープのグローバル変数、呼び出しをまたいで生き残る static ローカル変数、static 関数、シャドーイング、そしてグローバル変数が問題を起こす理由。
ポインタ
- ポインタポインタとはメモリアドレスを格納する変数です。& と * の演算子、ポインタの宣言と参照外し、ポインタの型が重要な理由、そしてポインタが存在する理由そのものを示す swap() 関数まで、土台から組み立てます。
- ポインタ演算ポインタに 1 を足しても 1 バイト進むのではなく、その型の次のオブジェクトへ移動します。ptr+1、インクリメントとデクリメント、ポインタ同士の差、比較、ポインタによる配列の走査、そして「末尾の 1 つ先」のルールを解説します。
- ポインタと配列C では、配列名は使われるほとんどの場所で先頭要素へのポインタに変わります。この減衰、arr[i] が文字どおり *(arr+i) である理由、配列のサイズを関数へ別途渡さなければならない理由、そして配列へのポインタとポインタの配列の違いを解説します。
- 関数ポインタ関数にもアドレスがあり、関数ポインタはそれを格納します。このページでは宣言の構文を読み解き、typedef で読みやすくする方法を示し、コールバック、独自の比較関数を使う qsort、そしてディスパッチテーブルへと積み上げていきます。
- NULL ポインタNULL は、ポインタが何も指していないときに保持するアドレスです。NULL の正体、参照外しがクラッシュする理由、0 や未初期化ポインタとの違い、そして null 由来のバグをコードから締め出す防御的なパターンを解説します。
配列と文字列
- 配列C言語で配列を宣言・初期化する方法、ゼロ始まりのインデックス、sizeof による長さの計算、要素のループ、そして末尾を越えて読むことがエラーメッセージではなく未定義動作になる理由。
- 多次元配列C で 2 次元配列を宣言・初期化し、ループで走査する方法、行優先(row-major)配置がメモリ上で実際に何を意味するか、2 次元配列を関数に渡すときに列数が必要な理由、そして行列の実例。
- 文字列Cに文字列型はありません - 文字列とは '\0' バイトで終わる char の配列です。このページはその考え方を組み立て、続いてリテラル、表示、走査、そして = で文字列をコピーできない理由を扱います。
- 文字列関数<string.h> を動かしながら巡る:strlen で測り、strcpy と strncpy でコピーし、strcat でつなぎ、strcmp で比較し、strchr と strstr で探す - そしてどれもあふれさせないためのバッファの大きさの決め方。
- 文字列と数値の変換C言語でテキストと数値を相互に変換する:atoi がエラーを報告できない理由、endptr と errno を使った strtol・strtod の正しい使い方、snprintf で数値を文字列にする方法、そして '0' を使った1桁の技。
動的メモリ
- 動的メモリヒープが存在する理由、malloc が実行時に決まるサイズのメモリを渡す仕組み、p = malloc(n * sizeof *p) のイディオム、NULL のチェック、ちょうど1回だけ解放すること、そして動的配列を最初から最後まで。
- calloc と realloccalloc はゼロ初期化されたメモリを返し、サイズの掛け算を安全に行います。realloc はすでに中身を詰めたブロックを拡張します。このページでは両方と、失敗した realloc がメモリを漏らさないための一時ポインタのイディオム、そして malloc + memset より calloc が勝る場面を扱います。
- メモリリークリークとは本当は何なのか、C のプログラムがそれを生む 3 つの経路、それを防ぐ所有権の規律、そして残りを valgrind と -fsanitize=address で見つける方法 - 最後にリークのあるプログラムを一歩ずつ直します。
- スタックとヒープデータが実際にどこに置かれるか:スタック上の自動記憶域、ヒープ上の動的記憶域、そして第3の領域である静的記憶域 - ローカル変数を返すことで生じる古典的なダングリングポインタのバグと、選び方の指針とともに。
構造体・共用体・列挙型
- 構造体C言語の構造体で関連する値を1つの型にまとめる方法 - 宣言、ドット演算子によるメンバへのアクセス、指示付き初期化子、構造体の配列、入れ子、そして誰もが驚く「渡すとコピーされる」規則。
- 構造体とポインタC言語で構造体を指す方法 - アロー演算子、(*p).x にかっこが必要な理由、変更やコピー回避のためにポインタで構造体を渡すこと、構造体を malloc すること、そして連結リストのノードの作り方。
- typedeftypedef が既存のC言語の型に新しい名前を与える仕組み - タグ付き・タグなしの typedef struct の定石、関数ポインタと配列のための typedef、そして typedef が害になる唯一の場合:ポインタを隠すこと。
- 共用体C言語の共用体が同じバイト列に複数の型を格納する仕組み - サイズが最大のメンバになる理由、最後に書いたメンバを読まなければならない理由、そしてタグ付き共用体(enum と union)がそれを安全にする方法。
- 列挙型(enum)C言語でenumを定義する方法 - 自動採番と明示的な値、switchと組み合わせてコンパイラに case の漏れを検出させる方法、enumを文字列に変換する方法、そして読みやすさを保つ命名規約。
入出力
- printfC言語で printf が実際にどう動くのか - 書式文字列のモデル、毎日使う変換指定子、桁揃えのための幅と精度、浮動小数点をきれいに表示する方法、戻り値、そして printf(user_input) がセキュリティホールである理由。
- scanfC言語で scanf を使って入力を読む方法 - & が必要な理由、空白の扱い、%s のバッファオーバーフローと幅指定による対策、戻り値の確認、改行が残るバグ、そして堅牢な代替としての fgets。
- 書式指定子C言語のすべての書式指定子を一か所に - %d、%s、%c、%f、%p などの意味、長さ修飾子 l・ll・h・z、幅・精度・フラグ、そして printf と scanf で異なるもの。
- ファイル操作C言語でファイルを読み書きする方法 - fopen のモード表、NULL チェック、fprintf と fputc での書き込み、fgets・fscanf・fgetc での読み込み、定番の1行ずつのループ、そしてバイナリモードを使うべき場面。
- コマンドライン引数C プログラムがコマンドラインを読む方法 - argc と argv の構造、引数のたどり方、argv[0] に入っているもの、atoi ではなく strtol で数値に変換すること、そしてそれらをまとめた小さな電卓。
プリプロセッサ
- プリプロセッサプリプロセッサは、コンパイラが見るより前にソーステキストを編集します。ヘッダを貼り込み、マクロを置換し、コードを差し込んだり外したりします。ディレクティブの一族と、その出力を覗く方法をまとめます。
- マクロ#define によるオブジェクト形式・関数形式のマクロ - なぜすべての引数と本体全体に括弧が要るのか、多重評価の罠、do-while(0) による複数行マクロ、そして関数や const のほうが適している場面。
- ヘッダファイルC言語のプログラムを複数のファイルに分ける方法: .h に入れるもの、.c に入れるもの、二重インクルードを防ぐインクルードガード、複数ファイルのコンパイル、そして extern によるグローバル変数の共有。
- 条件付きコンパイル#ifdef、#ifndef、#if、#elif、#else でビルドごとに違うコードをコンパイルする方法 - デバッグ用スイッチ、プラットフォーム分岐、コマンドラインから -D でマクロを定義する方法、そして #if 0 でコードを無効にする方法。
標準ライブラリ
- 数学関数math.h のひと通り - sqrt、pow、fabs、floor と ceil、round、fmod、三角関数と対数の一族、INFINITY と NAN - さらに定番の undefined reference エラーを招く -lm リンカフラグ。
- 乱数C言語で rand() と RAND_MAX を使って乱数を生成する方法、srand(time(NULL)) でちょうど一度だけ種を与える理由、% による範囲への写像とそこで生じるわずかな偏り、乱数の double、そして再現可能な系列。
- 標準ライブラリ各標準ヘッダーが何を与えてくれるか - stdio.h、stdlib.h、string.h、math.h、time.h、ctype.h、limits.h、stdbool.h、stdint.h、assert.h - をそれぞれ短い動く例とともに。
エラーとデバッグ
- セグメンテーション違反セグフォルトは、プログラムが所有していないメモリに触れたという意味です。ほぼすべてのセグフォルトを占める5つの原因を、最小の例とその修正とともに紹介し、さらに gdb と AddressSanitizer で正確な行を突き止める方法も示します。
- よくあるエラーC言語のプログラマーが誰でも一度は通る間違い - セミコロンの抜け、== のつもりの =、暗黙の宣言、& のない scanf、== による文字列比較、整数除算、未初期化変数、そして off-by-one - を症状・原因・対処法の形でまとめました。
- 未定義動作未定義動作とは、C標準が何の要求も課さないコードのことです - だから最適化器があなたの検査を削除することも含め、何が起きてもおかしくありません。何がそれを引き起こすのか、「自分のマシンでは動く」が何も証明しない理由、そしてそれを捕まえるフラグを紹介します。