組み込みの埋まってるとこ

システム寄りの組み込みCプログラミングBLOG

printfで何も表示されない原因と対応方法

printfで何も出力されないときに考えられる原因とその対処方法を一般的なことから少し専門的な分野までまとめてみます。

Windows環境でprintfが出力されない

Windowsではprintf出力はいったんバッファに蓄えられ、バッファの容量を超えた時点で出力されます。 そのため出力する文字数が少ないときは直ちに出力されません。 例えばMinGWgccコンパイルした場合はprintfの出力が4096バイトを超えるまではバッファされ出力されません。

一方でMSYSのgccコンパイルした場合は改行コードが現れる度に出力されるようになります。 MinGWWindowsネイティブでコンパイルするのに対し、MSYSはPOSIX準拠であることがこうした違いとなって現れているのでしょう。

バッファにたまっていて出力されない

printfの出力は比較的遅い処理になるためある程度溜まってからまとめて出力することがあり、 これが原因でprintfがすぐに出力されないことがあります。

改行文字で出力させる

バッファのモードがラインモードになっているときは改行文字('\n')が現れるまで出力がバッファされその間出力されなくなります。 標準Cライブラリの仕様として標準出力はラインバッファモードになっていることが多いので、誰でも一度は経験するトラブルではないかと思います。

これが原因で出力されないときは、printfの出力に改行文字を追加するとあっさりと出力されるようになります。

printf("Hello world\n");  /* \nが実は重要 */

強制的にフラッシュする

バッファがブロックモードかラインモードになっていて、 出力タイミングがはっきりしないことが問題になっているときは 強制的に出力する方法で解決できます。

バッファに溜まっている出力を流しだすことをフラッシュと言いますが、 プログラムで強制的にフラッシュするにはfflush関数を使います。

fflush(stdout);

fflushとstdoutの宣言はstdio.hにあります。

バッファを無効にする

いちいちフラッシュするのが面倒なときはバッファモードを無効にすることがあります。 あまり使われない方法ですが2~3文字の少ない文字数を頻繁に出力するようなプログラムでは有効なこともあります。

setvbuf(stdout, NULL, _IONBF, 0);

setvbufの宣言はstdio.hにあります。

文字コードが違っていて出力が表示できない

プログラムに埋め込んでいる文字コードと出力先が対応している文字コードが同じでない場合は文字化けしたり最悪何も表示されないことがあります。 printfで出力しようとしている文字が0-9,A-z の範囲のアスキーコードだけであれば問題になることはほとんどありませんが、全角文字のようなマルチバイト文字出力しようとしているときは注意が必要です。稀なケースでフォントが無いこともあります。

文字コードの対応を調べる

まずprintfのダブルクォーテーション(")で囲まれた文字列がコンパイルした後 どんな文字コードになっているかを意識します。 基本的にはプログラムソースを保存するときに指定した文字コードになっているはずです。

例えばプログラムソースをUTF-8で保存している場合はprintfが出力する文字コードUTF-8になるため出力先がUTF-8を表示可能でなければ正しく表示できないことになります。

printf(u8"3バイトのUTF-8は私のターミナルで表示できますか?\n");

文字コードを指定する

プログラムソースを保存するときに指定できる文字コードコンパイラによって決まっていて自由に選ぶことはできません。デバッグしていてどうもおかしいと思ったらプログラムソースの文字コードが違っていたというこもともままあります。

ロスコンパイラを使ってる等の理由であえてプログラム実行時にprintfの出力する文字コード、 言い換えるとオブジェクトファイルの中に埋め込まれる文字コードを変更したい場合は コンパイラコンパイルオプションによって指定できることがあります。 gccでは-fexec-charsetで指定できます。

gcc -finput-charset=utf-8 -fexec-charset=cp932 modern.c -o legacy_ja

標準出力の出力先が無い

printfの出力先はCライブラリ的には標準出力(stdout)になっていますが、 実際に標準出力がどこになっているかはプログラムを実行する環境によって変化します。

実行環境で用意されたライブラリの関数を使う

特にウインドウを表示するプログラムではプログラム実行時に標準出力の出力先が変わったりなくなったりしていることがあります。 この場合printfに代わってデバッグ用に文字列を出力する関数がそのプログラムのライブラリによって用意されているはずなのでそちらを使うようにします。

WindowsではOutputDebugStringが有名です。 この関数は出力先がデバッガのウインドウになりますが、 言語ランタイムサポートやデバッガやの設定により出力先が変わったり拾えなかったりして表示できなくなるトラブルがたまにあるので注意です。 またMinGWで開発していて純正のデバッガがないときはDebugViewというツールで拾う方法もあるようです。 どうしてもコンソールに出力したいときは使い方が難しいですがWriteConsoleという関数もあります。

組み込みで標準出力ドライバをつくる

組み込みプログラムではターゲットボードに標準出力のドライバがないためにprintfが出力されないケースが山のようにあります。 最初からMyPrintfでUARTへ出力するようなライブラリを用意しているプロジェクトも普通によく見かけます。

しかし既にUARTへ出力することができているのであれば、 標準Cライブラリのローレベルをポーティングして printfを使えるようにすることは難しいことではないはずです。 興味があればライブラリのマニュアルを見るとポーティング方法の説明が書かれていると思います。 自力でドライバを作らないまでも、 JTAGデバッガを経由して統合開発環境のコンソールにprintfを出力する方法が説明されていることもしばしばあります。

int _write(int fileno, const char *buf, int size)
{
  if (fileno==1 || fileno==2) {
    puts_uart(buf, size);
    return size;
  }
  return 0;
}

まとめ

一般にprintfが出力されない原因は標準Cライブラリによる出力のバッファです。 この場合は改行コードを出力するか、明示的にfflushで流しだすかのどちらかで出力することができます。

printfの出力先は普通は標準出力という名前のコンソールですが、 それが環境によりそうではなくなってしまっていることもよくあります。 この場合システム的な知識がないと解決が難しくなりますが、 出力先がどこなのか検討がつくだけでも検索などで調べやすくなるのではないかと思います。