C言語プログラミング能力認定試験2級 向けトレース教材
2級向け教材:状態遷移+エラー監視モデル
▶ 想定シナリオ(組み込み実務)
装置は RUN / ERROR / RESET の3状態を持つ
センサ値が異常なら ERROR へ遷移
ERROR が連続3回なら RESET 実行
▶ 状態定義(定数)
#define RUN 1
#define ERROR 2
#define RESET 3
▶ Cコード(2級レベル・状態遷移)
int sensor[10] = {20, 22, 150, 23, 160, 170, 25, 26, 27, 28};
int i;
int state = RUN;
int errCount = 0;
int resetCount = 0;
for(i = 0; i < 10; i++){
if(state == RESET){
errCount = 0;
state = RUN;
resetCount++;
}
if(state == RUN){
if(sensor[i] < 10 || sensor[i] > 100){
state = ERROR;
}
}
if(state == ERROR){
errCount++;
if(errCount >= 3){
state = RESET;
}else{
state = RUN;
}
}
}
▶ 状態遷移の流れ
RUN → (異常) → ERROR → (3回未満) → RUN
RUN → ERROR → (3回到達) → RESET → RUN
▶ トレース観点(試験で見る場所)
state の変化タイミング
errCount の加算位置
RESET 処理は次ループで実行される
2級向け教材:最大値検出+位置記録
▶ 想定シナリオ
センサログから最大値とその位置を記録
▶ Cコード
int data[8] = {5, 12, 9, 30, 7, 18, 30, 4};
int i;
int max = data[0];
int pos = 0;
for(i = 1; i < 8; i++){
if(data[i] > max){
max = data[i];
pos = i;
}
}
▶ 重要ポイント
初期値を配列[0]にする
同値(maxが同じ)の場合は更新しない
最初に出た最大値の位置が残る
2級で狙われやすいミス
❌ if と else の対応ミス
if(a)
if(b) x++;
else
y++;
→ else は内側の if に対応する(超頻出)
カウンタ初期化位置ミス
for(...){
int sum = 0; // ← ここに置くと毎回0に戻る
}
break の意味
while(1){
if(err) break;
}
→ ループだけ抜ける、関数は終わらない
試験での解き方(2級版)
状態変数を最優先で追う
カウンタ更新位置を丸で囲む
条件分岐ごとに別ルートで追跡
RESETや初期化は次ループか即時か確認
実務につながる発展テーマ
デバウンス処理
移動平均フィルタ
簡易ウォッチドッグ
フラグ駆動メインループ
割り込みとメイン処理の関係
▶ 考え方(超重要)
メイン処理:ずっと回り続ける
割り込み:急に割り込んで処理される
割り込みでは短く・速く・フラグだけ立てる
▶ 実務モデル
タイマ割り込みで1秒ごとにフラグON
メインループで処理実行
▶ Cモデルコード(擬似割り込み)
int timerFlag = 0; // 割り込みから立てられる
void timer_interrupt(void){
timerFlag = 1;
}
int main(void){
while(1){
if(timerFlag == 1){
timerFlag = 0;
/* 周期処理 */
}
}
}
▶ 試験で狙われるポイント
フラグを初期化し忘れる → 無限実行
割り込みで重い処理を書くのはNG
2. グローバル変数と static の意味
▶ なぜstaticが必要?
関数を抜けても値を保持したい
でもグローバルにはしたくない
▶ 比較例
❌ 通常のローカル変数
void func(void){
int cnt = 0;
cnt++;
printf("%d", cnt);
}
→ 何回呼んでも1
✅ static変数
void func(void){
static int cnt = 0;
cnt++;
printf("%d", cnt);
}
→ 呼ぶたびに 1,2,3…
▶ 実務モデル(周期回数カウント)
void task(void){
static int cycle = 0;
cycle++;
if(cycle >= 10){
cycle = 0;
/* 10周期ごとの処理 */
}
}
試験ポイント
初期化は最初の1回だけ
staticは関数内でも寿命はプログラム全体
3. バッファ管理(リングバッファ前段)
※完全なリングバッファの前に必ず出てくる形
想定シナリオ
受信データを順に保存
バッファが満杯なら停止
Cコード(線形バッファ)
#define BUF_SIZE 5
int buf[BUF_SIZE];
int writePos = 0;
void push(int data){
if(writePos < BUF_SIZE){
buf[writePos] = data;
writePos++;
}
}
読み出し処理
int i;
for(i = 0; i < writePos; i++){
printf("%d ", buf[i]);
}
なぜリングにするのか
線形だと:
すぐ満杯
ずらす処理が必要
→ だから次に出てくるのがリングバッファ
割り込み × バッファ × フラグ 組み合わせモデル
実務っぽい全体像
割り込み:受信データ格納
メイン:データ処理
モデルコード
#define BUF_SIZE 5
int buf[BUF_SIZE];
int writePos = 0;
int dataFlag = 0;
void rx_interrupt(int data){
if(writePos < BUF_SIZE){
buf[writePos] = data;
writePos++;
dataFlag = 1;
}
}
int main(void){
int i;
while(1){
if(dataFlag == 1){
dataFlag = 0;
for(i = 0; i < writePos; i++){
/* データ処理 */
}
}
}
}
実務の鉄則
割り込み:保存だけ
メイン処理:計算・判定
これを逆にすると暴走の元。
2級 → 実務で一気に差がつく理解ポイント
| 項目 | 意味 |
|---|---|
| フラグ | 割り込みとメインの橋渡し |
| static | 状態保持の基本 |
| バッファ |