【C言語】出力時の符号付与は printf() の”フラグ”で指定せよ

C言語

ようこそ、KiRiOのブログへ!

本記事では「新・明解C言語中級編」の自由課題について解説します。


自由課題のとおり、この参考書には解答が載っておりません。
そこで、本記事で私なりの解答を共有しますので、ぜひ解答の参考にしてみてください。

今回解説する課題は「演習1-7」で、課題内容は以下のとおりです。

演習1-7List 1-8(p.23)のプログラムでの入力履歴表示では、正解との差が0であっても符号をつけて表示するため、少々みっともない。
0に対しては符号をつけないように変更せよ。

設計

まず初めに、List 1-8のプログラムを以下に記します。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define MAX_STAGE 10

int main(void)
{
	int i;
	int stage;
	int no;
	int ans;
	int num[MAX_STAGE];

	srand(time(NULL));
	ans = rand() % 1000;

	puts("0〜999の整数を当ててください。");

	stage = 0;

	do
	{
		printf("残り%d回。いくつかな:", MAX_STAGE - stage);
		scanf("%d", &no);
		num[stage++] = no;

		if (no > ans)
		{
			puts("\aもっと小さいよ。");
		}
		else if (no < ans)
		{
			puts("\aもっと大きいよ。");
		}
	} while(no != ans && stage < MAX_STAGE);

	if (no != ans)
	{
		printf("\a残念。正解は%dでした。", ans);
	}
	else
	{
		printf("正解です。%d回で当たりましたね。\n", stage);
	}

	puts("--- 入力履歴 ---");
	for (i = 0; i < stage; i++)
	{
		printf(" %2d : %4d %+4d\n", i + 1, num[i], num[i] - ans);
	}

	return 0;
}

List 1-8は、以下のような流れで処理を実行していきます。

  1. 最大入力回数(10回)分の要素数を持つ配列を宣言
  2. 0〜999の乱数(正解値)を生成
  3. プレーヤが0〜999の整数を入力
  4. 入力値を配列に格納し、履歴として保存
  5. 入力値と正解値を比較して、大小結果を表示
  6. 10回以内に正解できれば、入力回数ともに結果を表示
  7. 10回以内に正解できなければ、正解値ともに結果を表示
  8. 配列に格納された値を入力履歴として一覧表示、その際正解地との差分も合わせて表示

このうち、入力履歴表示にあたる箇所はプログラムの50行目で、正解との差を表しているのは「%+4d」であることがわかります。
%4d」ですと「int型の整数を4文字分の幅(最小フィールド幅)を使って右詰めで表示する」という意味になります。

そして、最小フィールド幅「4」の前についている「+」は「フラグ」と呼ばれるものです。
フラグには「-」「+」「空白」の3種類があり、それぞれ以下のような意味を持ちます。

フラグ 意味
- 変換結果をフィールド内に左詰めにする。
指定がなければ右詰めとなる。
+ 符号付き変換をされる数値の前に「+」または「-」を付ける。
指定がなければ負の値のみに「-」が付けられる。
空白 符号付き変換の結果が符号で始まらない場合、または符号付き変換の結果の文字数が0の場合、数値の前に「空白」を付ける。

すなわち、課題内容にもありますとおり「%+4d」で表示しますと、入力値と正解値との差分が「0」であっても「+0」と表示されてしまいます
ここで「%4d」と表記してしまいますと、今度は差分が正の値のときに符号が付与されなくなってしまいます。

そこで指定するのが、3つ目のフラグ「空白」です。
上表にもありますとおり、フラグに「空白」を指定すると、符号付き変換の結果(表示値)が符号で始まらない場合、数値の前に「空白」が付与されます
また、+」と「空白」の両方を指定すると「空白」フラグは無効となります
すなわち、入力値と正解値との差分が「0」である場合のみ、フラグに「空白」を指定すればよいのです。

実装

設計部分でも述べましたとおり、入力履歴の表示形式(フラグ)を「差分の有無」によって以下のように変えます。

  • 入力値 ≠ 正解値:%+4d
  • 入力値 = 正解値:%4d

そして、この「入力値と正解値との比較結果」を条件式としたifを用いて履歴表示を分岐させます。
以上を実装したものが下記プログラムとなります。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define MAX_STAGE 10

int main(void)
{
	int i;
	int stage;
	int no;
	int ans;
	int num[MAX_STAGE];

	srand(time(NULL));
	ans = rand() % 1000;

	puts("0〜999の整数を当ててください。");

	stage = 0;

	do
	{
		printf("残り%d回。いくつかな:", MAX_STAGE - stage);
		scanf("%d", &no);
		num[stage++] = no;

		if (no > ans)
		{
			puts("\aもっと小さいよ。");
		}
		else if (no < ans)
		{
			puts("\aもっと大きいよ。");
		}
	} while(no != ans && stage < MAX_STAGE);

	if (no != ans)
	{
		printf("\a残念。正解は%dでした。", ans);
	}
	else
	{
		printf("正解です。%d回で当たりましたね。\n", stage);
	}

	puts("--- 入力履歴 ---");
	for (i = 0; i < stage; i++)
	{
		if (num[i] != ans)
		{
			printf(" %2d : %4d %+4d\n", i + 1, num[i], num[i] - ans);
		}
		else
		{
			printf(" %2d : %4d % 4d\n", i + 1, num[i], num[i] - ans);
		}
	}

	return 0;
}

以上で完了となります。
プログラムを実行して、0に対してのみ符号が付かない数当てゲームとして機能しているか確認してみてください。

まとめ

それでは、本記事で説明した内容を以下にまとめます。

    printf()による符号の付与はフラグを用いて設定する

    1. -
    2. 変換結果をフィールド内に左詰めにする。指定がなければ右詰めとなる。

    3. +
    4. 符号付き変換をされる数値の前に「+」または「-」を付ける。
      指定がなければ負の値のみに「-」が付けられる。

    5. 空白
    6. 符号付き変換の結果が符号で始まらない場合、または符号付き変換の結果の文字数が0の場合、数値の前に「空白」を付ける。

printf()のフラグについては、こちらの記事も参考にしてみてください。

C言語での出力について
C言語で標準出力(通常は画面)に対し書式&#20184...

ここまで、記事を読んでいただき、ありがとうございました。

最後に、今回取り上げた課題が記載されている本を紹介します。
今回扱った課題以外にも、様々なプログラム開発について記載されているため、この記事とあわせて読んでいただくと、より理解が深まると思います。


また、参考として以下の本もご紹介しておきます。

こちらもオススメです。2021年12月14日に最新版が発売されました!

それでは、また別の記事でお会いしましょう!

コメント

タイトルとURLをコピーしました