column

技術情報・基礎知識

組み込みソフトウェアの基本 組み込みソフトウェアの開発言語の選び方|C・C++・Rust・アセンブラを徹底比較

組み込みソフトウェア開発において、どの言語を選ぶかはプロジェクトの土台を決める重要な意思決定です。実行性能、メモリ効率、安全性、開発生産性、ツールチェーンの充実度、エンジニアの確保しやすさといった要素が複雑に絡み合うため、一概に「この言語が最強」と言い切ることはできません。長らくC言語が主流の座を守ってきた一方で、近年はC++の採用領域が広がり、メモリ安全性を強みに持つRustが組み込み領域に進出しはじめ、アセンブラも依然として特定の場面で必要とされ続けています。本記事では、組み込みソフトウェアで使われる主要言語の特徴を比較しながら、プロジェクトの性質に応じた選び方を整理します。

組み込みソフトウェアで使われる主要言語の全体像

組み込みソフトウェアの世界で実際に使われている言語は、PCアプリやWeb開発に比べると大きく絞り込まれます。理由は単純で、メモリ数十KB級のマイコンで動かす必要があったり、リアルタイム性を保証する必要があったり、ハードウェアレジスタを直接叩く必要があったりと、言語に求められる条件が厳しいからです。インタプリタ型の言語や、ガベージコレクションを前提とする言語は、こうした制約のもとでは選びにくくなります。

そのなかで現場の選択肢として挙がるのは、C、C++、Rust、アセンブラの4つです。C言語は今でも組み込み開発のデファクトスタンダードであり、案件数・人材数・ツール対応のいずれにおいても圧倒的な厚みを持ちます。C++はオブジェクト指向や型安全性を取り入れたい場面で選ばれ、車載やネットワーク機器、産業用コンピュータといった大規模な組み込みソフトウェアで採用が広がっています。Rustはメモリ安全性と現代的な言語機能を武器に、新規プロジェクトでの採用例が増えはじめている新興勢力です。アセンブラは汎用的に使う言語ではなく、起動直後の初期化処理や、極限まで切り詰めたい数命令程度の処理で局所的に書かれます。

なお、PythonやMicroPython、Luaといったスクリプト言語をマイコン上で動かす取り組みもあり、教育用途やプロトタイピングでは存在感がありますが、量産品の制御ロジックを担う本流の選択肢にはなりにくいのが実情です。本記事では、量産製品の組み込みソフトウェアを書くための実用言語として、C・C++・Rust・アセンブラの4つに絞って比較していきます。

C言語:依然として組み込み開発の主流

C言語は1972年に登場した古い言語ですが、組み込み開発の世界では今も中心的な位置を占め続けています。ハードウェアに近い記述ができ、生成されるバイナリのサイズが小さく、実行時オーバーヘッドがほぼゼロに近いという特徴が、リソースの限られた組み込み機器の要件に極めてよく合致するためです。

Cが選ばれ続ける理由は性能だけではありません。あらゆるマイコンメーカーが純正のCコンパイラやSDKを提供しており、新しいチップでも開発環境にすぐアクセスできる安心感があります。RTOSや通信スタック、暗号ライブラリといったミドルウェアもCで書かれているものが圧倒的多数で、既存資産との接続に困らないのも大きな利点です。さらに、安全性が要求される領域ではMISRA Cというコーディング規約が確立されており、自動車・医療・産業機械などの分野で品質を担保するための共通基盤になっています。エンジニア人口の多さも見逃せず、新規メンバーをアサインしやすい点はマネジメント上のメリットといえます。

一方で、C言語は書き手に大きな自由を与える代わりに、責任もすべて書き手に委ねます。ポインタの誤用、配列の境界外アクセス、未初期化変数の参照、暗黙の型変換、整数オーバーフローといった落とし穴が随所にあり、いずれも実行時には何のエラーも出さずに動き続けてしまいます。組み込み機器でこうしたバグが量産後に発覚すると、リコールや長期間のフィールド対応に発展しかねません。静的解析ツールやコーディング規約で守りを固めながら使う言語、と理解しておくのが現実的です。

C++:抽象度を上げたい大規模組み込みでの選択肢

C++は、Cの性能特性をほぼそのまま引き継ぎつつ、クラス、テンプレート、名前空間、例外処理、RAII(リソース獲得は初期化時に)といった現代的な機能を備えた言語です。組み込み領域では、車載ECUのうち比較的処理量の多いもの、産業用コンピュータ、ネットワーク機器、医療機器など、ソフトウェア規模が数十万行を超えるような大規模プロジェクトで採用されることが多くなっています。

C++の魅力は、複雑なソフトウェアをモジュール化して構造化できる点にあります。Cでも構造体と関数ポインタを駆使すれば擬似的なオブジェクト指向は書けますが、可読性と保守性の観点でC++のクラスには敵いません。テンプレートを使えば、コンパイル時に型を解決して効率的なコードを生成でき、「抽象度の高い記述」と「ランタイムオーバーヘッドの最小化」を両立させやすくなります。RAIIの考え方も、リソース管理ミスを構造的に減らす助けになります。

ただし、C++の自由度の高さは諸刃の剣でもあります。例外処理、動的メモリ確保、仮想関数、RTTI(実行時型情報)といった機能はランタイムコストやコードサイズの増加を招くため、組み込みではプロジェクト方針として「どこまで使うか」を明確に決めておく必要があります。AUTOSARやMISRA C++のように、組み込み向けにC++の使用範囲を制限するガイドラインが整備されているのも、こうした事情を反映したものです。コンパイラごとに対応している言語仕様のバージョンが異なる点にも注意が必要で、最新規格の機能をそのまま使えるとは限りません。エンジニアに求められる知識量がCより多くなる点も含め、メンバー構成と相談しながら採用を判断することになります。

Rust:メモリ安全性で組み込みに進出する新興言語

Rustは2010年代に登場した比較的新しい言語で、Mozillaが設計を主導し、現在は独立した財団のもとで開発が進められています。所有権、借用、ライフタイムといった独自の型システムによって、メモリ安全性とスレッド安全性をコンパイル時点で保証できる点が最大の特徴で、ガベージコレクションを必要とせずに安全性を実現しているため、組み込み領域とも親和性の高い言語として注目されています。

組み込みRustのエコシステムも整備が進んでおり、embedded-halと呼ばれるハードウェア抽象化レイヤーの仕様や、各マイコン向けのペリフェラルアクセスクレートが提供されています。no_std環境(標準ライブラリを使わない環境)でも開発できる仕組みが整っており、ベアメタル開発からRTOS連携まで、徐々に実用例が積み重なってきている段階です。Linuxカーネルが一部にRustの採用を始めたことや、自動車・産業機器の領域で評価導入が増えていることも、組み込みRustへの関心を押し上げています。

一方で、Rustを組み込みに採用するうえでの課題も無視できません。学習コストはCやC++よりも明確に高く、所有権モデルや非同期処理の考え方に慣れるまで時間が必要です。エンジニア人口もまだCに比べて圧倒的に少なく、量産プロジェクトでチームを組むには採用面の難しさが伴います。マイコンメーカーの公式サポートもCとは比較になりません。多くの場合、純正SDKはC前提で書かれており、Rustから利用するためには自前でバインディングを整える必要が出てきます。新規プロジェクトで導入を検討する価値は十分にありますが、既存資産の流用が多い案件や、短納期で確実性が求められる案件では、現時点では慎重な判断が必要です。

アセンブラ:限定的だが置き換えの効かない場面で活躍

アセンブラは、CPUの命令そのものを記述する低水準言語です。汎用的なアプリケーションロジックをアセンブラで書くことは現代ではほぼなく、組み込み開発でも全体の99%以上はC・C++・Rustといった高級言語で書かれます。それでもアセンブラが完全に消えないのは、いくつかの場面で置き換えの効かない役割を担っているからです。

ひとつはマイコン起動直後の初期化処理です。スタックポインタの設定、ベクタテーブルの配置、データセグメントの初期化といった、Cランタイムが立ち上がる前に行うべき処理は、アセンブラで記述する必要があります。多くの場合はマイコンメーカー提供のスタートアップコードをそのまま使えますが、特殊なメモリ構成やカスタムブートを行う場合には自分で書き換えることもあります。

もうひとつは、極限の性能が要求される処理や、命令単位での精密なタイミング制御が必要な場面です。DSP的な信号処理ループや、暗号処理のサイドチャネル攻撃対策、CPU固有の特殊命令の活用といった用途で、関数の一部だけをアセンブラで記述する「インラインアセンブラ」が使われます。コンパイラの出力を読んで、ボトルネックになっている数命令だけをアセンブラで置き換える、という使い方が現実的なスタイルです。

アセンブラはCPUアーキテクチャに完全に依存するため、移植性は皆無です。ARM Cortex-M向けに書いたコードはRISC-Vでは動きませんし、同じARMでもCortex-AシリーズとCortex-Mシリーズでは命令セットが異なります。書ける人材も限られるため、保守の観点からも「使う場所を最小限に抑える」のが鉄則です。

言語選定のチェックリスト

ここまでの内容を踏まえ、組み込みソフトウェアの言語選定で確認しておきたい観点を整理しておきます。

  • 対象マイコンのコンパイラ・SDKがどの言語を公式にサポートしているか
  • 既存資産(社内の過去プロジェクト、流用するミドルウェア)がどの言語で書かれているか
  • 規模感(数千行クラスか、数十万行クラスか)と、必要となる構造化のレベル
  • リアルタイム性・メモリ制約・コードサイズの厳しさ
  • 規格対応(機能安全、医療機器規格など)と、対応するコーディング規約の有無
  • チームのスキル構成と、新規メンバーを採用しやすいか
  • 長期保守を見越したときの言語の安定性とエコシステムの将来性

これらを総合して判断することになりますが、ひとつのプロジェクトで複数言語を組み合わせるのも珍しくありません。起動処理だけアセンブラ、本体ロジックはC、PCと連携するツール側はC++やPythonで書く、といった役割分担は現場でよく見られるパターンです。「どれか一つを選ぶ」というより、「主軸言語を決めたうえで、必要に応じて他言語を局所的に併用する」という発想で考えると、現実的な選定がしやすくなります。

用途や要求仕様によって最適な言語選択を

組み込みソフトウェアの開発言語は、性能・安全性・開発生産性・人材確保・既存資産との整合性といった要素のバランスで決まります。Cは依然として主流であり、迷ったらまずCという判断は今でも合理的です。大規模化する組み込みソフトウェアで構造化と保守性を重視するならC++が現実解になり、メモリ安全性を最初から組み込みたい新規プロジェクトではRustが有力な選択肢に育ってきました。アセンブラは限定的ですが、起動処理や極限の最適化など、置き換えの効かない場面で今も必要とされています。プロジェクトの性質と将来の保守体制を見据えて、最適な言語を選びたいところです。

関連する技術情報・基礎知識