固定幅整数型(stdint.h)を使う
| 設計内容分類 | 組み込みソフトウェアにおけるC言語記述 |
|---|
実現したい仕様・課題
int・long等のサイズはC言語の仕様上、処理系(マイコンとコンパイラ)に依存します。intが16bitのマイコンと32bitのマイコンの間でコードを移植すると、桁あふれや通信フレームのレイアウトずれといった発見しにくい不具合が発生します。レジスタ幅や通信仕様のようにビット幅が決まっているデータを、幅を保証して扱いたいというのが課題です。
設計のポイント
ビット幅が意味を持つ変数には、標準ヘッダstdint.hが定義する固定幅整数型(uint8_t/uint16_t/uint32_t、int8_t/int16_t/int32_t 等)を使用します。これらは「符号の有無」と「ビット幅」が型名に明示されており、どの処理系でも同じ幅が保証されるため、ハードウェアレジスタの定義、通信フレームのフィールド、保存データのレイアウト、ビット演算の対象など、幅の決まったデータを移植性を保って記述できます。uint8_tで宣言した変数は必ず8bit(0〜255)であり、「charは符号付きか符号なしか」という処理系差の問題も回避できます。使い分けの目安は、「ハード・プロトコル・保存形式に関わる変数は固定幅整数型、ループカウンタ等の内部ロジックだけで完結する変数は int や size_t のままでもよい」です。sizeof演算子の結果やメモリサイズにはsize_tを使います。printfで固定幅整数を出力する場合、%dや%luを直書きすると幅の対応が処理系依存になるため、inttypes.hのマクロ(PRIu32等)を使うと厳密です。古いコード資産でBYTE/WORD/DWORDのような独自typedefが使われている場合も、新規コードからstdint.hの標準型へ統一していくことで、マイコン変更に強いコード資産へ移行できます。C99以降のコンパイラであれば追加コストなしで使える、移植性向上の最も手軽で効果的な手段です。
【コード例】
#include <stdint.h>
// 通信フレーム:幅が仕様で決まっているデータ
typedef struct __attribute__((__packed__)) {
uint8_t stx; // 1バイト
uint16_t length; // 2バイト
uint32_t data; // 4バイト
uint8_t sum; // 1バイト
} FRAME;
uint8_t status; // レジスタ値(8bit保証)
uint32_t tick_ms; // 32bitカウンタ
補足情報
設計のポイント 一覧
組み込みソフトウェアにおけるC言語記述のポイント
- リングバッファで受信データを取りこぼさない
- switch-caseによるステートマシンの実装
- 割り込みハンドラは短く書く
- constを活用する(ROM配置と書き換え防止)
- ビット演算によるレジスタ・フラグ操作
- 固定幅整数型(stdint.h)を使う
- volatile修飾子を正しく使う
- 構造体の宣言にpackedを指定する
- ソースコードのエンディアン変換
- sizeofを使用する
- 特殊文字の記述にエスケープ文字を使用する
- 変数の初期化漏れを防ぐ
- 外部バスの使用
- sprintf関数を使う
- 列挙型(enum)を使う
- 構造体を使う
- 標準関数を使う
- 条件演算子を使う
- インクリメント演算子を使う
- C言語記述の際はアプリ層とドライバ層に分ける



