Open4
調べもの:C 言語

雑に AI に聞きまくる。
Q. C 言語の慣習を知りたいんだけど、定数と構造体の定義はヘッダーファイルに書くのが一般的なの?
例
net.h
#ifndef NET_H
#define NET_H
#include <stddef.h>
#include <stdint.h>
#ifndef IFNAMSIZ
#define IFNAMSIZ 16
#endif
#define NET_DEVICE_TYPE_DUMMY 0x0000
#define NET_DEVICE_TYPE_LOOPBACK 0x0001
#define NET_DEVICE_TYPE_ETHERNET 0x0002
#define NET_DEVICE_FLAG_UP 0x0001
#define NET_DEVICE_FLAG_LOOPBACK 0x0010
#define NET_DEVICE_FLAG_BROADCAST 0x0020
#define NET_DEVICE_FLAG_P2P 0x0040
#define NET_DEVICE_FLAG_NEED_ARP 0x0100
#define NET_DEVICE_ADDR_LEN 16
#define NET_DEVICE_IS_UP(x) ((x)->flags & NET_DEVICE_FLAG_UP)
#define NET_DEVICE_STATE(x) (NET_DEVICE_IS_UP(x) ? "up" : "down")
struct net_device {
struct net_device *next;
unsigned int index;
char name[IFNAMSIZ];
uint16_t type;
uint16_t mtu;
uint16_t flags;
uint16_t hlen; /* header length */
uint16_t alen; /* address length */
uint8_t addr[NET_DEVICE_ADDR_LEN];
union {
uint8_t peer[NET_DEVICE_ADDR_LEN];
uint8_t broadcast[NET_DEVICE_ADDR_LEN];
};
struct net_device_ops *ops;
void *priv;
};
struct net_device_ops {
int (*open)(struct net_device *dev);
int (*close)(struct net_device *dev);
int (*transmit)(struct net_device *dev, uint16_t type, const uint8_t *data, size_t len, const void *dst
;
};
extern struct net_device *net_device_alloc(void);
extern int net_device_register(struct net_device *dev);
extern int net_device_output(struct net_device *dev, uint16_t type,
const uint8_t *data, size_t len, const void *dst);
extern int net_input_handler(uint16_t type, const uint8_t *data, size_t len,
struct net_device *dev);
extern int net_run(void);
extern void net_shutdown(void);
extern int net_init(void);
#endif
net.c
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include "platform.h"
#include "net.h"
#include "util.h"
/* NOTE: if you want to add/delete the entries after net_run(), you need to
* protect these lists with a mutex. */
static struct net_device *devices;
struct net_device *net_device_alloc(void) {}
/* NOTE: must not be call after net_run() */
int net_device_register(struct net_device *dev) {}
static int net_device_open(struct net_device *dev) {}
static int net_device_close(struct net_device *dev) {}
int net_device_output(struct net_device *dev, uint16_t type,
const uint8_t *data, size_t len, const void *dst) {}
int net_input_handler(uint16_t type, const uint8_t *data, size_t len,
struct net_device *dev) {}
int net_run(void) {}
void net_shutdown(void) {}
int net_init(void) {}

なぜこの分離が重要なのか?
- 再利用性:複数の
.c
ファイルから同じ構造体や定数を使える - コンパイル効率:ヘッダーファイルの変更時のみ関連ファイルを再コンパイル
- インターフェース明確化:公開するものと内部実装を分離
- 重複定義の回避:
#ifndef
ガードで重複インクルードを防止
例外的なケース
- static const定数:ファイル内でのみ使用する場合は
.c
に書く - 巨大な配列:初期化データは
.c
に書くことが多い - インライン関数:小さな関数は
static inline
でヘッダーに書くことも
この慣習により、インターフェース(.h
)と実装(.c
)を明確に分離し、保守性の高いコードが書ける。

extern
とは
Q. 
extern
の基本的な意味
extern
は**「この変数や関数は他の場所で定義されている」**ことをコンパイラに伝えるキーワードです。
実際の例で確認
net.h
での使用例
// 51-54行目
extern struct net_device *net_device_alloc(void);
extern int net_device_register(struct net_device *dev);
extern int net_device_output(struct net_device *dev, uint16_t type,
const uint8_t *data, size_t len, const void *dst);
これは:
- 宣言(declaration): 「こういう関数があります」
-
定義(definition): 実際の実装は
net.c
にある
net.c
での実際の定義
// 14行目
struct net_device *net_device_alloc(void) {}
// 17行目
int net_device_register(struct net_device *dev) {}
extern
の役割
1. 関数の場合
// ヘッダーファイル(.h)
extern int my_function(int x); // 宣言
// ソースファイル(.c)
int my_function(int x) { // 定義
return x * 2;
}
2. 変数の場合
// file1.c
int global_counter = 0; // 定義
// file2.c
extern int global_counter; // 宣言(他のファイルで定義済み)
void increment() {
global_counter++; // 使用可能
}
extern
がない場合との違い
extern
なし(ヘッダーファイルで)
❌ // header.h
int my_function(int x); // これでも動くが...
extern
あり(推奨)
✅ // header.h
extern int my_function(int x); // 明示的に外部定義を示す
extern
を使うのか?
なぜ1. 明示性
「この関数/変数は外部で定義されている」ことが一目で分かる
2. リンク時の問題回避
複数のファイルで同じヘッダーをインクルードしても重複定義エラーが起きない
3. C言語の慣習
特に関数プロトタイプでは extern
を明示的に書くのが一般的
実際のリンク過程
# コンパイル時
gcc -c net.c -o net.o # net_device_alloc の実装をオブジェクトファイルに
gcc -c main.c -o main.o # net_device_alloc の呼び出しをオブジェクトファイルに
# リンク時
gcc net.o main.o -o program # extern宣言と実際の定義を結び付ける
まとめ
extern
は**「宣言だけで、実装は別の場所にある」**ことを示すキーワードです。これにより:
- ヘッダーファイルでインターフェースを公開
- 実装は対応する
.c
ファイルに分離 - 複数のファイルから同じ関数/変数を安全に使用
C言語のモジュール化の基本的な仕組みとなっている。