見出し画像

続C言語教室 - 第5回 文字出力あれこれ

さて前回まででキーボードやファイルからの入力については一通り済んだと思います。入力ばかりで出力のほうが疎かになっていたので、今回それを済ませてしまいましょう。

入力における getc(fgetc) に相当する1文字出力が putc(fputc) です。引き数にint型の文字コードとFILE構造体へのポインタを渡します。

#include <stdio.h>

void main() {
  FILE* fp = fopen("sample.txt", "w");
  putc('A', fp);
  fclose(fp);
}

これで sample.txt には0x41の1バイトが出力されます(改行やEOFコード無し)。putc がマクロなので関数を呼び出したいのであれば fputc を使うというのも getc と同じです。

putc

fputc

そして画面というか標準出力に1文字を出力するのが putchar です。引き数はint型の文字コードです。

#include <stdio.h>

void main() {
  for (int i = 'A'; i <= 'Z'; i++)
    putchar(i);
}

ABCDEFGHIJKLMNOPQRSTUVWXYZ

画面への出力結果

次に gets に相当する1行出力が puts です。こちらにもマクロではない fputs があります。一般論としては副作用があるような書き方さえしなければ気にしなくて良いのですが、実は puts と fputs には違いがあるので注意してください。

#include <stdio.h>

void main() {
  FILE* fp = fopen("sample.txt", "w");
  puts("Hello World", fp);
  fclose(fp);
}

Hello World

ファイルへの出力結果

この時、puts は文字列の末尾に改行文字を追加しますが、fputs は改行文字を追加しません。まったくもってどうしてこんな微妙な違いがあるのでしょうね。まあ出力の際にもエラーが発生することはあるのですが、入力と違ってあくまで「異常」が発生したときだけなので、しっかりチェックすることは稀です。

そして、みんな大好き printf の仲間たちです。これはもうご存知とは思いますが、画面に出すのが printf 、ファイルに出すのが fprintf で、違いはfrpintfの最初の引き数はFILE構造体へのポインタで、次の引き数が書式文字列だというところだけです。さらに書式を適用した文字列を返す sprintf があります。こちらは最初の引き数が fprintf のFILE構造体へのポインタの代わりに結果を返すchar* を渡します。C言語ですからこのポインタにはどんな結果でも格納できる十分なサイズの領域を確保してから渡さなければなりません。

#include <stdio.h>
#pragma warning(disable : 4996)
void main() {
  char buf[16];
  sprintf(buf, "%f", 3.1415926);
  puts(buf);
}

3.141593

出力結果(%fの仕様により末尾が四捨五入されている)

さあ勘の良い人は気が付きましたね。このように関数を呼び出す前に「適当」な大きさの領域を用意するというやり方は、今やご法度です。VisualStudio では、sprintf を使うとエラーとなるので、これを回避するpragma を指定しています。このように sprintf はオススメできない関数なので snprintf という関数を使います。

#include <stdio.h>

void main() {
  char buf[16];
  snprintf(buf, 16, "%f", 3.1415926);
  puts(buf);
}

3.141593

出力結果

2番目の引き数に確保した領域の大きさを渡し、この関数は領域が余れば、それを0で埋め、領域を超えれば処理を打ち切り、指定したサイズを超えて何かを書き込むことはありません。もっとも戻ってきた文字列から処理が最後まで進んだのか打ち切られたのかを判断するのは少しばかり難しいです。現実的な解としては末尾に改行コードが入るような書式を入れておいて戻り値の最後が改行になっているかで判断するみたいな方法かなぁとは思います。

printf 系列にはまだバリエーションがあって、書式文字列の後に%の数だけ変数を並べるのではなく va_list という可変長引き数を扱うマクロを使う vprintf なんていうものもあったりします。これはva_list が出てきたときに覚えれば間に合います。

さて入力のときにストリーム入出力関数はバッファを持っている云々という話をしました。入力したものは改行が入力されるまでは処理が始まらないという話でしたね。出力も同じようにバッファされていて一定のサイズまでデータがたまるか改行が出力されると始めてファイルに書き出されます。最後にファイルがクローズされるときに必ずバッファの内容がすべて出力されるので、入力の時のように気にするような事態にはなりません。まあ実際には書き出されると言ってもOSのディスクへの書き込みキューに渡されるだけで物理的なディスクへの書き込みがいつされるのかは誰もわからないのですけどね。

入力のときと異なり出力に関しては、予めその内容を調べることができるので、注意して書けばエラーになるようなことは滅多にありません。もちろん最初にファイルが書き込み可能で開けなかったり、ディスクが一杯になったときは別ですが、そんな時に出来ることはどうせ何もありません。メモリもですがリソースが無くなったときって、どうアガイてもコードで出来ることはないので、大人しく処理を中断するのが関の山です。

というところで、次回はそのエラーについて少し追ってみましょうか。

ヘッダ画像は、以下のものを使わせていただきました。https://www.irasutoya.com/2014/08/blog-post_51.html

#C言語 #プログラミング講座 #文字出力 #バッファオーバーラン #バッファ処理 #putc #puts #putchar #sprintf #snprintf

この記事が気に入ったらサポートをしてみませんか?