はじめに
【C言語】 は、1970年代に開発された汎用プログラミング言語で、現在の多くの言語(C++、Java、C#、Go など)の基礎となっています。
特徴としては、OS、デバイスドライバ、組み込みシステムなど、「ハードウェアに近い低レベルな処理」が可能な点です。
【注意】このメモは、C言語を全くしらない人間が、ChatGPTに聞いて適当に書いている、C言語の基本的な記載方法のまとめです。
(内容は確認しながら書いているつもりですが、間違ってたらごめんなさい)
関数の構造
C言語における関数定義の書式
記憶クラス 型 関数名(型を含めた仮引数の並び) {
処理内容
return 値;
}
📌 記憶クラス(storage class specifier)
→ 変数や関数の寿命(どれくらい生きているか) や 可視性(どこから参照できるか) を指定する仕組み
👉 auto
意味: 自動変数(ローカル変数)
寿命: 関数やブロックに入った時に作られ、抜けたら消える
可視性: そのブロック内だけ
特徴: デフォルトなので普段は省略される /
👉 static
意味: 静的記憶域期間を持つ
寿命: プログラムの開始から終了まで有効
可視性: 宣言されたスコープに限定される
特徴: 値が関数を抜けても保持される
👉 extern
意味: 外部変数を参照する
寿命: プログラム全体で有効
可視性: 他ファイルからもアクセスできる
特徴: 変数の実体は別の場所にあり、ここでは「宣言」だけ
📌 型(戻り値の型)
👉 int / double / char など
📌 関数名
→ 開発者がつける名前
📌 仮引数の並び
→ それぞれの引数の型+名前
main 関数について
C言語のエントリーポイント(最初に実行される関数)は必ず main です。
int main(void) {
// 処理内容
return 0;
}
📌 int main(void)
→ 引数を取らないことを 明示
📌 int main()
→ 古い書き方。規格上は「引数が不定」と解釈されるため、引数チェックがされず バグの原因 になりやすい。
📌 戻り値の意味(終了ステータス):
0 … 正常終了
0 以外 … 異常終了(OSや呼び出し元にエラーを知らせる)
C言語のソースコードは、コンパイル前にプリプロセッサ(前処理系) による処理を受けます。この段階で行われるのが プリプロセッサ命令 です。
主なプリプロセッサ命令
📌 #include
他のファイルを取り込む命令。
よく使うのは標準ライブラリのヘッダファイル:
/* 標準入出力関数 (printf, scanf など) */
#include <stdio.h>
/* 数値変換, 動的メモリ管理, 乱数 など */
#include <stdlib.h>
👉 ソースコードにそのヘッダファイルの中身がコピーされるイメージです。
📌 #define
マクロ定義。置換マクロとも呼ばれ、文字列の置換を行います。
#define TRUE 1
#define FALSE 0
while (TRUE) {
printf("ずっと繰り返します\n");
break; // breakで抜けないと無限ループ
}
TRUE は 1 に置き換わる
FALSE は 0 に置き換わる
実際にコンパイルされるときは
while (1) { ... }
に展開されます。
👉 while(1) とは?
while (1) {
// この処理が無限に繰り返される
}
・条件が常に「真 (1)」なので 無限ループ
・ただし、break; や return; を入れればループを途中で抜けられる
📌 C言語では「ブーリアン型を使わない」
C言語(C99以前)には 真偽値(bool型)が標準で存在しません。
その代わりに、整数で真偽を表現します。
0 → 偽 (false)
0以外 → 真 (true)
なので、条件式には整数の計算結果をそのまま使います。
if 文での使い方
int x = 5;
if (x) {
printf("xは0ではありません\n");
}
・if (x) は xが0かどうかを判定している
・x = 5 のとき → 0以外 → 真 → 実行される
・x = 0 のとき → 偽 → 実行されない
if (!0) {
printf("!0 は真です\n");
}
0 は偽
!0 は「NOT 0」なので真
→ この場合、必ず実行される
比較演算子の評価結果
int a = 3, b = 5;
if (a < b) {
printf("aはbより小さい\n");
}
a < b の結果は「真 → 1」または「偽 → 0」
その整数値を if が判定に利用する
C99以降
C99 からは をインクルードすることで 真偽値型 bool が使えるようになりました。
#include <stdbool.h>
int main(void) {
bool flag = true;
if (flag) {
printf("flagは真です\n");
}
flag = false;
if (!flag) {
printf("flagは偽です\n");
}
return 0;
}
true → 1
false → 0
実際には #define で定義されている(プリプロセッサで展開される)
📌 引数付き置換マクロ(マクロ関数)
#define は引数をとることで、関数のように振る舞うマクロを定義できます。
#include <stdio.h>
// マクロ全体と引数は () で囲む
#define DOUBLE(x) ((x) * 2)
int main(void) {
printf("5 の 2倍 = %d\n", DOUBLE(5)); // 10
printf("(3+4) の 2倍 = %d\n", DOUBLE(3+4)); // 14
return 0;
}
これで DOUBLE(5) → ((5) * 2) → 10 に展開される
#include <stdio.h>
// 条件演算子を使った例(2つの値の大きい方を返す)
#define MAX(a, b) ((a) > (b) ? (a) : (b))
int main(void) {
// 条件演算子
printf("大きい方 = %d\n", MAX(10, 20)); // 20
printf("大きい方 = %d\n", MAX(5+3, 2*6)); // 12
return 0;
}
実行結果
大きい方 = 20
大きい方 = 12
カッコを忘れるとどうなるか
#define MAX(a, b) a > b ? a : b
このとき
printf("%d\n", MAX(5+3, 2*6));
はプリプロセッサ展開で
「5+3 と 2*6 の大きい方」を返したいところですが、
5+3 > 2*6 ? 5+3 : 2*6
となり、優先順位の関係で意図しない式評価になります。
結果は
5+3 > 2*6 ? 5+3 : 2*6
→ 8 > 12 ? 8 : 12
→ 12
のように一見正しく見えることもありますが、
複雑な式を渡すと誤動作の原因になります。
📌 C言語の条件付きコンパイル(#if / #else / #elif / #endif、#ifdef / #ifndef)
C言語では「マクロの値」や「マクロが定義されているかどうか」で、コンパイルするコードを切り替えることができます。
これは「条件付きコンパイル」と呼ばれ、デバッグ用コードや環境ごとの処理を分けたいときに便利です。
1. #if / #elif / #else / #endif
マクロの値を条件にして、処理を分けます。
#include <stdio.h>
#define VERSION 2 // マクロ定義
int main(void) {
#if VERSION == 1
printf("バージョン1用の処理\n");
#elif VERSION == 2
printf("バージョン2用の処理\n");
#else
printf("その他のバージョン\n");
#endif
return 0;
}
📌 実行結果
バージョン2用の処理
👉 #if の後には 定数式 を書きます。
👉 #elif(else if)や #else を使って条件分岐できます。
👉 #endif で条件付きコンパイルを終了します。
2. #ifdef / #ifndef
マクロが 定義されているかどうか で処理を分けます。
#include <stdio.h>
#define DEBUG // DEBUG というマクロを定義
int main(void) {
#ifdef DEBUG
printf("デバッグ情報を表示\n");
#endif
#ifndef RELEASE
printf("リリース版ではない\n");
#endif
return 0;
}
📌 実行結果
デバッグ情報を表示
リリース版ではない
👉 #ifdef MACRO : マクロが 定義されていれば コンパイルされる
👉 #ifndef MACRO : マクロが 未定義なら コンパイルされる
3. 応用例:デバッグ用とリリース用
#include <stdio.h>
// #define DEBUG // コメントを外せばデバッグモード
int main(void) {
#ifdef DEBUG
printf("詳細なデバッグ情報を出力\n");
#else
printf("通常動作\n");
#endif
return 0;
}
📌 DEBUG を定義すれば
詳細なデバッグ情報を出力
📌 定義しなければ
通常動作
💡 ポイントまとめ
#if / #elif / #else / #endif → マクロの値で条件分岐
#ifdef / #ifndef → マクロが定義されているかどうかで条件分岐
デバッグ用・OSごと・バージョンごとの処理を切り替えるのに便利
📌 プロトタイプ宣言
C言語では、関数を使う前に「プロトタイプ宣言」を書く必要があります。
これは「この関数が存在する」ということをコンパイラに伝えるための宣言です。
#include <stdio.h>
/* プロトタイプ宣言 */
void hello(void);
int main(void) {
hello(); // ここで関数を呼び出す
return 0;
}
/* 関数の定義 */
void hello(void) {
printf("Hello, World!\n");
}
👉 ポイント
ヘッダファイル(.h) にまとめて書き、#include で読み込むのが一般的
printf など標準関数のプロトタイプも stdio.h に書かれており、#include で利用できる。