Copilot無償版
演習シナリオ
C言語でデバイスを制御するプログラムを作成したいと考えています。
ひとことでC言語と言っても、所属する業態や法人により記述作法が定義されています。
この演習では MISRA C(Motor Industry Software Reliability Association C) を
サンプルとして、作法にのっとったコードの生成を試してみます。
演習手順
①作法を認識させます。
この演習では、作法の文書化からとりかかります。
組織内に文書化されているものがあればそちらを流用してください。
次のプロンプトを実行してください。
MISRA Cにのっとったコーディング作法について生成してください。
このあとCのコーディングをしてもらいたいので、
AIに作法を網羅させることができるような粒度でお願いします。
次のような出力を得ることができます。
🔧 MISRA C コーディング作法(実装向けガイド)
- 基本原則
安全性:未定義動作や未規定動作を避ける。
可読性:コードの意図が明確であること。
保守性:変更や拡張が容易であること。
移植性:異なる環境でも動作すること。
- 型と宣言に関する作法
int や char などの基本型はサイズが環境依存のため、stdint.h の int32_t などを使用。
暗黙の型変換は禁止。明示的なキャストを行う。
typedef は構造体や列挙型に対してのみ使用し、基本型には使わない。
変数の宣言は使用するブロックの先頭で行う(C89スタイル)。
- 制御構造に関する作法
goto 文は禁止。
switch 文には必ず default 節を含める。
if 文の条件式は括弧で囲む。
複雑な条件式は分割して可読性を高める。
for / while ループは終了条件が明確であること。
- 関数に関する作法
関数は1つの責務に限定する(単一責任原則)。
再帰は禁止(スタックオーバーフローのリスク)。
戻り値の型は明示し、void も明示する。
引数はすべて明示的に型指定し、未使用引数は削除するか (void) にする。
グローバル変数の使用は最小限に。
- ポインタとメモリに関する作法
ポインタの算術演算は禁止。
NULL チェックは必ず行う。
ポインタのキャストは慎重に。異なる型間のキャストは原則禁止。
配列アクセスは境界チェックを徹底。
動的メモリ確保(malloc / free)は原則禁止(組み込みでは非推奨)。
- 演算と式に関する作法
副作用のある式(例:x++ + x)は禁止。
ビット演算は符号なし型で行う。
演算子の優先順位に依存せず、括弧で明示する。
複数の変数を一度に代入する式は禁止(例:a = b = c)。
- 定数とマクロに関する作法
マジックナンバーは禁止。#define または const を使用。
マクロ関数は禁止。inline 関数を使用。
#define は型安全性がないため、可能な限り const に置き換える。
- プリプロセッサに関する作法
条件付きコンパイル(#ifdef)は最小限に。
インクルードガード(#ifndef)を必ず使用。
#include は必要なヘッダのみを明示的に記述。
- コメントとドキュメンテーション
すべての関数に目的・引数・戻り値の説明を記述。
複雑な処理には処理の意図をコメントで補足。
コメントはコードと乖離しないように保守する。
- MISRA C 適合チェック
静的解析ツール(例:PC-lint, Coverity, QAC)を使用して規約違反を検出。
MISRA C のバージョン(2004, 2012, 2023)に応じたルールセットを確認。
例外(Deviation)を設ける場合は、文書化とレビューを徹底。
② 作法にのっとってコーディングを実施します。
最初に、先に生成された作法定義またはあなたの組織で準備されている作法定義を認識させます。
次のプロンプトを実施してください。
# 背景
あなたはC言語のコーディングを実施するプロフェッショナルです。
入力に記載された処理のコードを生成してください。
# 命令
先に生成した作法の定義を遵守してコードを生成してください。
/// あなたの組織で定義された文面があればここに貼り付け。 ///
/// その場合この命令の先頭行は削除してください。 ///
定義から外れる場合は、外れた個所を明記し、外れた理由を記載してください。
# 入力
"""
デバイスに内蔵されている温度センサーの値を読み取る。
稼働温度範囲外になったとき、アラートを発信する。
危険温度範囲内になったとき、アラートを発信してデバイスの機能を無効化する。電源は停止しない。
稼働温度範囲内に戻ったとき、アラートを発信してデバイスの機能を有効化する。
"""
# 出力
すぐにコンパイル可能なC言語コードとして出力してください。
先頭に内容が見て取れる簡潔なプログラム名の案を記載してください。
生成結果を確認してください。
次のような結果が生成されます。
🔧 プログラム名案:temperature_monitor.c
目的:温度センサーの値を監視し、稼働温度範囲外・危険温度範囲に応じてアラートと機能制御を行う。
✅ MISRA C準拠コード(C89スタイル)
/* temperature_monitor.c
* 温度センサー監視とアラート・機能制御
* MISRA Cに準拠
*/
#include <stdint.h>
#include <stdbool.h>
/* 定数定義 */
#define TEMP_MIN_OPERATING (0) /* 稼働温度下限 [℃] */
#define TEMP_MAX_OPERATING (70) /* 稼働温度上限 [℃] */
#define TEMP_MIN_CRITICAL (-10) /* 危険温度下限 [℃] */
#define TEMP_MAX_CRITICAL (85) /* 危険温度上限 [℃] */
/* デバイス状態 */
typedef enum
{
DEVICE_DISABLED = 0,
DEVICE_ENABLED
} device_state_t;
/* 関数プロトタイプ */
static int16_t read_temperature(void);
static void send_alert(const char *message);
static void enable_device(void);
static void disable_device(void);
/* グローバル変数(最小限) */
static device_state_t device_state = DEVICE_ENABLED;
/* メイン処理関数 */
int main(void)
{
int16_t temperature = 0;
while (true)
{
temperature = read_temperature();
if ((temperature < TEMP_MIN_CRITICAL) || (temperature > TEMP_MAX_CRITICAL))
{
if (device_state != DEVICE_DISABLED)
{
send_alert("CRITICAL: Temperature out of safe range. Device disabled.");
disable_device();
device_state = DEVICE_DISABLED;
}
}
else if ((temperature < TEMP_MIN_OPERATING) || (temperature > TEMP_MAX_OPERATING))
{
send_alert("WARNING: Temperature out of operating range.");
/* デバイスは無効化しない */
}
else
{
if (device_state != DEVICE_ENABLED)
{
send_alert("INFO: Temperature back to normal. Device enabled.");
enable_device();
device_state = DEVICE_ENABLED;
}
}
}
return 0;
}
/* 温度センサーから値を取得(モック関数) */
static int16_t read_temperature(void)
{
/* 実際にはADCやI2Cなどで取得 */
return 25; /* 仮の値 */
}
/* アラート送信(モック関数) */
static void send_alert(const char *message)
{
/* 実際にはUARTやログ出力など */
(void)message;
}
/* デバイス機能を有効化(モック関数) */
static void enable_device(void)
{
/* 実際にはGPIO制御など */
}
/* デバイス機能を無効化(モック関数) */
static void disable_device(void)
{
/* 実際にはGPIO制御など */
}
⚠️ MISRA Cからの逸脱と理由
| 逸脱箇所 | 内容 | 理由 |
|---|---|---|
while (true) |
無限ループに true を使用 |
true は <stdbool.h> による定義であり、MISRA Cでは無限ループに for(;;) を推奨するが、可読性を優先して使用。必要に応じて修正可能。 |
const char *message |
ポインタ使用 |
const により読み取り専用であり、ポインタの算術演算は行っていないため安全と判断。 |
自己診断プロンプトは人間が書いたコードの診断にも流用可能です。
③ AIに自己診断させてみる
生成されたコードに落ち度がないか自己診断させてみます。
次のプロンプトを実行してください。
# 命令
生成されたコードについて、次の視点から診断を実施してください
・セキュリティ脆弱性が含まれていないこと
・コメントの理解が容易であること
・無限ループの可能性が排除できていること
・その他あなたの知見から指摘できること
# 出力
命令内の各項目に対して、表形式で生成してください。
次の5列でお願いします。
診断日時(JST)、診断項目、診断結果、その理由、解消案、その他指摘事項
**④ AIにコードを修正させてみる **
診断結果を取り入れてコードの改善を実施してみます。
次のプロンプトを実行してください。
診断結果に基づいて、以前生成したコードを改善してください。
どこが改善されたのか、改善前後の差分がわかるようにコメントも追記してください。
生成結果を確認してください。
次のような結果が生成されます。
以下は、以前生成した temperature_monitor.c のコードを、診断結果に基づいて改善したものです。
改善点にはコメントで差分を明示しており、MISRA C準拠性・保守性・安全性が向上しています。
🔧 改善版プログラム:temperature_monitor.c(改善済)
/* temperature_monitor.c
* 温度センサー監視とアラート・機能制御
* MISRA C準拠 + 診断結果に基づく改善
*/
#include <stdint.h>
#include <stdbool.h>
/* 定数定義 */
#define TEMP_MIN_OPERATING (0) /* 稼働温度下限 [℃] */
#define TEMP_MAX_OPERATING (70) /* 稼働温度上限 [℃] */
#define TEMP_MIN_CRITICAL (-10) /* 危険温度下限 [℃] */
#define TEMP_MAX_CRITICAL (85) /* 危険温度上限 [℃] */
/* デバイス状態 */
typedef enum
{
DEVICE_DISABLED = 0,
DEVICE_ENABLED
} device_state_t;
/* アラートレベル(改善点①:文字列→enum化) */
typedef enum
{
ALERT_INFO = 0,
ALERT_WARNING,
ALERT_CRITICAL
} alert_level_t;
/* 関数プロトタイプ */
static int16_t read_temperature(void);
static void send_alert(alert_level_t level, const char *message); /* 改善点① */
static void enable_device(void);
static void disable_device(void);
/* グローバル変数(最小限) */
static device_state_t device_state = DEVICE_ENABLED;
/* メイン処理関数 */
int main(void)
{
int16_t temperature = 0;
/* 改善点②:while(true) → for(;;) に変更(MISRA C準拠) */
for (;;)
{
temperature = read_temperature();
if ((temperature < TEMP_MIN_CRITICAL) || (temperature > TEMP_MAX_CRITICAL))
{
if (device_state != DEVICE_DISABLED)
{
send_alert(ALERT_CRITICAL, "Temperature out of safe range. Device disabled.");
disable_device();
device_state = DEVICE_DISABLED;
}
}
else if ((temperature < TEMP_MIN_OPERATING) || (temperature > TEMP_MAX_OPERATING))
{
send_alert(ALERT_WARNING, "Temperature out of operating range.");
/* デバイスは無効化しない */
}
else
{
if (device_state != DEVICE_ENABLED)
{
send_alert(ALERT_INFO, "Temperature back to normal. Device enabled.");
enable_device();
device_state = DEVICE_ENABLED;
}
}
}
return 0;
}
/* 温度センサーから値を取得(モック関数) */
static int16_t read_temperature(void)
{
/* 実際にはADCやI2Cなどで取得 */
return 25; /* 仮の値 */
}
/* アラート送信(改善点①:レベルを引数に追加) */
static void send_alert(alert_level_t level, const char *message)
{
/* 実際にはUARTやログ出力など */
(void)level;
(void)message;
}
/* デバイス機能を有効化(モック関数) */
static void enable_device(void)
{
/* 実際にはGPIO制御など */
}
/* デバイス機能を無効化(モック関数) */
static void disable_device(void)
{
/* 実際にはGPIO制御など */
}
✅ 改善点まとめ
| 改善項目 | 内容 | コメントで差分明示 |
|---|---|---|
| 改善① | アラート種別を enum に変更し、保守性・拡張性向上 |
alert_level_t の導入と send_alert() の引数変更 |
| 改善② | 無限ループ構文を while(true) → for(;;) に変更 |
for (;;) に変更済み、コメントで理由記載 |
この演習はこれで終了です。
みなさまの業務にご活用ください。