C言語のprintfで文字列を出力する方法

347, 2021-11-17

目次

C言語のprintfで文字列を出力する

C言語ではprintf()関数で文字列を出力することができます。
この記事ではその方法について具体的に解説します。

printf()による文字列の出力はC言語の処理では基礎的な技術になります。
これを把握しておくことでprintf()関数の使い方も覚えていくことが可能になります

C言語の文字列には普通の文字列(マルチバイト文字列)とワイド文字列がありますが、この記事では両者をともに取り上げます。
また、フォーマット整形関数のsprintf(), snprintf()についても解説します。
具体的には↓を見ていきます。

  • 普通の文字列を出力する

  • ワイド文字列を出力する

  • フォーマット指定子で出力を調整する

  • sprintfの使い方

  • snprintfの使い方

普通の文字列を出力する

C言語で扱う一般的な文字列(マルチバイト文字列)をprintf()で出力するには、フォーマット指定子の「%s」を使います
フォーマット指定子とは、printf()の引数をどのように出力するか、という指定です。
この指定子には文字列では「%s」を使いますが、整数などでは「%d」などを使います。

必要なヘッダファイル

C言語でprintf()を使うにはstdio.h」をインクルードしておく必要があります
printf()stdio.hでプロトタイプ宣言されているからです。
そのため↓のようなコードを最初に書いておく必要があります。

#include <stdio.h>

Hello, World!の出力

printf()を使って「Hello, World!」という文字列を出力する方法です。
↓のようなコードを書きます。

#include <stdio.h>

int main(void) {
    const char *hw = "Hello, World!";

    printf("%s", hw);
    // Hello, World!

    return 0;
}

まず最初に「stdio.h」をインクルードしています。
これはprintf()を扱うためです。

それからmain関数内では最初にhwという変数を定義しています。
この変数には文字列定数「Hello, World!」が代入されています。

それからprintf()を呼び出してhwを出力しています。
printf()第1引数にはフォーマット文字列を渡します。
↑の場合、フォーマット文字列は「"%s"」というのがそうです。

フォーマット文字列に対してprintf()第2引数以降の引数が展開されて、それが標準出力に出力されます
↑の例で言うとhwという文字列変数が%sに展開されます。

↑のコードをコンパイルして実行すると結果は「Hello, World!」になります。

複数のHello, World!の出力

printf()の第1引数のフォーマット文字列には、複数のフォーマット指定子を指定できます
たとえば↓のようにすると複数の「Hello, World!」を出力できます。

#include <stdio.h>

int main(void) {
    const char *hw = "Hello, World!";

    printf("%s %s", hw, hw);
    // Hello, World! Hello, World!

    return 0;
}

↑の場合をもう少し詳しく見てみます。
printf()の第1引数にはフォーマット文字列を指定します。
そして第2引数以降にはフォーマット指定子に展開する変数や値を指定します。

↑の場合、フォーマット文字列は「"%s %s"」です。
そして引数はhwhwの2つです。
最初のフォーマット文字列中のフォーマット指定子「%s」は、第2引数のhwを展開します。
そして次のフォーマット指定子「%s」は、第3引数のhwを展開しています。

Hello, World!だとわかりづらいと思うので↓のコードを見てください。

#include <stdio.h>

int main(void) {
    const char *cat = "Cat";
    const char *dog = "Dog";

    printf("%s %s", cat, dog);
    // Cat Dog

    return 0;
}

↑の例で言うと、変数catは最初のフォーマット指定子「%s」に展開されます
そして変数dogは次のフォーマット指定子「%s」に展開されます
出力結果は「Cat Dog」になります。

このようにprintf()のフォーマット指定子の順番と、引数の順番は密接に関連しています
フォーマット指定子と引数の種類が合っていなかったりすると、期待した出力が得られない場合もあります。
そのためタイポなどには注意してください。

フォーマット文字列と引数を合成する

printf()フォーマット文字列にはフォーマット指定子以外にもあらゆる文字を書くことができます
そのためフォーマット文字列の文字と引数の文字を合成することが可能です
たとえば↓のコードを見てください。

#include <stdio.h>

int main(void) {
    const char *name = "Taro";

    printf("My name is %s. I'm fine.", name);
    // My name is Taro. I'm fine.

    return 0;
}

↑の場合、「"My name is %s. I'm fine."」というフォーマット文字列に、nameという変数を合成しています。
結果は「My name is Taro. I'm fine.」になります。

改行の出力

printf()改行を出力したい場合はエスケープシーケンスという文字を使います
これは「\n」という文字がそうです。
厳密には改行には種類があり、環境ごとに違ったりするのですが、ここでは「\n」で統一して解説します。

#include <stdio.h>

int main(void) {
    const char *hw = "Hello, World!";

    printf("%s\n", hw);
    // Hello, World!

    return 0;
}

↑のコードではフォーマット指定子「%s」のあとにエスケープシーケンス「\n」を書いています。
こうすると「Hello, World!」という出力のあとに改行が出力されます。

改行を使って複数行の「Hello, World!」を出力することも可能です。
↓のようにです。

#include <stdio.h>

int main(void) {
    const char *hw = "Hello, World!";

    printf("%s\n%s\n%s\n", hw, hw, hw);
    // Hello, World!
    // Hello, World!
    // Hello, World!

    return 0;
}

↑のコードをコンパイルして実行すると「Hello, World!」という文字列が3行に渡って出力されます
フォーマット文字列はその分見辛くなってます。
これを嫌って複数行の出力を行う場合は、printf()自体を複数書くという人も多数います。

タブの出力

タブを出力したい場合も改行と同様にエスケープシーケンスを使います
タブのエスケープシーケンスは「\t」です。

#include <stdio.h>

int main(void) {
    const char *hw = "Hello, World!";

    printf("%s\t%s\n", hw, hw);
    // Hello, World!    Hello, World!

    return 0;
}

↑の場合、最初の「%s」のあとにタブを出力するエスケープシーケンス「%t」が書かれています。
タブの後には再び「%s」が書かれています。

ワイド文字列を出力する

C言語で日本語などを扱うワイド文字列を出力するにはwprintf()を使います
wprintf()は基本的にはマルチバイト文字列を扱うprintf()と同じように扱うことができます。
ただしフォーマット文字列をワイド文字列で指定する必要があるなど、独特なクセがあります。

必要なヘッダファイル

C言語でwprintf()を扱うには「wchar.h」をインクルードしておく必要があります。
wcharwide characterの略です。

#include <wchar.h>

Hello, World!の出力

wprintf()で「Hello, World!」を出力するには↓のようにコードを書きます。

#include <stdio.h>
#include <wchar.h>

int main(void) {
    const wchar_t *hw = L"Hello, World!";

    wprintf(L"%ls", hw);

    return 0;
}

↑の場合、hwというのがワイド文字列へのポインタを保存した変数です。
wprintf()printf()と同様に第1引数にフォーマット文字列を取ります。
このフォーマット文字列には「L」プレフィクスを付けておくのを忘れないようにしましょう

第2引数以降にフォーマット文字列へ展開する引数を書きます。
これもprintf()と同じです。

フォーマット指定子の%ls」はワイド文字列を出力する指定子です
wprintf()の引数がワイド文字列である場合はこの指定子を指定するようにします。
ちなみに「%s」でも出力することはできますが、これは内部でワイド文字列からバイト文字列への変換が発生します。
その分処理が遅くなる(といってもほとんどの場合、問題にはなりませんが)ので指定子には「%ls」を使ったほうがいいでしょう。

改行とタブの出力

改行とタブの出力はprintf()の場合と同じです。
↓の項目を参照してください。

フォーマット指定子で出力を調整する

printf()などの関数のフォーマット指定子には、出力を調整するための指定があります
文字列の場合、それは大きく分けて↓の2つになります。

  • 表示桁数の調整

  • 右詰め、左詰めの調整

表示桁数の調整

文字列を出力する際に表示桁数を調整する場合は↓のようなコードを書きます。

    // 表示桁数の調整
    printf("[%20s]\n", "Hello, World!");  // [       Hello, World!]
    printf("[%.5s]\n", "Hello, World!");  // [Hello]

↑のように「%20s」とすると表示桁数は20桁になります。
Hello, World!」という文字列は13桁なので7桁余分に確保されます
%.5s」とすると5桁のみが出力されます。
Hello, World!」という文字列の場合、先頭から5桁取り出すと「Hello」のみ残ります

右詰め、左詰めの調整

文字列を右詰め、左詰めに調整したい場合は↓のようなコードを書きます。

    // 右詰め・左詰めの指定
    printf("[%-20s]\n", "Hello, World!");  // [Hello, World!       ]
    printf("[%+20s]\n", "Hello, World!");  // [       Hello, World!]

%」以降に「-」を指定すると文字列が左詰めになります。
+」を指定した場合は右詰めになります。
%-20s」では文字列の桁数は20桁になっています。
Hello, World!」の文字数は13文字で13桁になるので7桁分あまります。
-」を指定した場合は7桁の余りが右側に出来て、「+」を指定した場合は余りが左側に出来ます。

sprintfの使い方

printf()と関連が深いsprintf()についても解説します。
sprintf()はフォーマットで引数を整形して、バッファに保存する関数です。
↓のように使います。

#include <stdio.h>

int main(void) {
    char buf[100];

    sprintf(buf, "Hello, %s", "World!");

    printf("buf[%s]\n", buf);
    // buf[Hello, World!]

    return 0;
}

sprintf()の第1引数にはバッファを指定します。
第2引数にはフォーマット文字列、第3引数以降にはフォーマットに指定する引数を指定します。
バッファにはフォーマットで整形された結果が保存されます

便利なsprintf()ですが、この関数は上級者以外は使わないほうがいいです
なぜかと言うとsprintf()はバッファのサイズを指定できません。
そのためバッファーオーバーフローなどの脆弱性が生まれる可能性があるからです。

また上級者であってもフォーマットの整形結果の文字列長を想像するのは非常に難しいことです。
そのためsprintf()を使うと簡単に脆弱性が埋め込まれる可能性があります。
ですので、sprintf()は使わずに後述するsnprintf()の使用を検討してください。

snprintfの使い方

snprintf()sprintf()にバッファのサイズを指定できるにしたものです。

#include <stdio.h>

int main(void) {
    char buf[100];

    snprintf(buf, sizeof buf, "Hello, %s", "World!");

    printf("buf[%s]\n", buf);
    // Hello, World!
    return 0;
}

snprintf()の第1引数にはバッファ、第2引数にはバッファのサイズを指定します。
第3引数にはフォーマット文字列、第4引数以降にはフォーマットへ展開する引数を指定します。

snprintf()はバッファのサイズを指定することができるため、sprintf()と比べてバッファーオーバフローになる確率が低いです。
確率が低いというのは、実際のバッファのサイズと指定したバッファのサイズが異なっている場合は、バッファーオーバーフローになる可能性があるからです。
しかし開発者がそのようなミスをしなければsnprintf()比較的に安全に使うことができます
セキュアなプログラムにしたい場合はsprintf()ではなくこちらのsnprintf()を積極的に使ったほうがいいでしょう。

おわりに

今回はC言語のprintf()で文字列を出力する方法を解説しました。
printf()を使った文字列の出力はC言語の処理でも基礎的なものです。
おさえおいて損はないです。

printf()で文字列を出力しよう

フォーマット指定子はマブダチだぜ