記事一覧へ戻る

C言語プログラミング能力認定試験2級 向けトレース教材

C言語初級 |

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級版)

  1. 状態変数を最優先で追う

  2. カウンタ更新位置を丸で囲む

  3. 条件分岐ごとに別ルートで追跡

  4. 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状態保持の基本
バッファ