見出し画像

さっくりC言語_02 //変数、printf

という訳で、さっくりC言語はーじまるよー(第2回目にして早くもネタ切れ)

今回の目標は『printfの基本的な使い方を知ろう!』です。
printfは可変長の引数を持つ少々特殊な関数で、その性能を引き出すためには変数の概念をを知る必要があります。

前回の記事


1.変数

という訳で、今回は変数という物のお話になります。
さて、変数とはそもそも何でしょうか? これはざっくり言ってしまうと変動する値の事をさします。

例えば今日の気温を示す変数temperature君がいたとしましょう。このtemperture君は当たり前ですが日時や時刻によって全く違う値になります。

//20XX/XX/5日での事
->temperature
14.5
------------------------------------------------
//20XX/XX/12日での事
->temperature
16.5

そして、この変動する値には便宜上の名前を付ける必要があります。この便宜上の名前を数学ではxやらyやらと名付ける訳です。

さて、この変数は数字を示すわけですが、その数字自体がコンピューター上では何処かで保管されていなければなりません。この保管された場所をメモリー(記憶)と呼ぶわけですね。

※CPUで実際に計算が行われる時も、数字を何処かに保管しなければなりません。現実世界でも4*5とかの計算をするとき、この4とか5とかの数字を紙に、または頭の中に保管していますよね。この、CPU上で計算に使うメモリの事をレジスタと呼びます。

ただし、コンピューター上ではこのメモリは、0と1だけで表現されています。ここで問題です、あなたは次の数字が一体何を示しているのか理解できますか?

0 10000001 01010000000000000000000

はい、答えはIEEE754表現で5.25です。
分かりませんよね普通…。と、いう訳で、その数字が一体なにを示しているのかを予め明示しておく必要があります。(型推量をやってくれる言語もあるので一概に言えませんが)

そのために、という概念があります。

2.型

型とは、そのメモリがどの様なデータの種類の事です。C言語においては主に次のような型が存在します。

int:整数型
[integer]整数
符号付き(つまりマイナスからプラスまで表現できる)整数を表現するデータ。2バイトや4バイト程度の大きさを持ちます。2^16から2^32程度の幅を持ち、2バイトで-2^15~2^15、4バイトで-2^31~2^31程度の範囲の数を表現できます。

const:定数
const[定数]
修飾子の一種。この修飾子を型の前につけると、その型を後から変更することが出来なくなる。プログラム内で変更されては困る変数につける。

static:静的な変数
static[静的な]
修飾子の一種。メモリを確保する際、その存在期間がプログラム全体にわたった保持されることを示した物。関数やクラス内で使用すると、その関数、クラスに対して一意な変数として確保される。

unsigned:符号なし
[unsigned]符号なし
符号付けない整数を表現するデータ。純粋な整数がマイナスの範囲を含まないため、表現できる幅が二乗ほど増える。unsigned intなら2^16や2^32程の表現幅を持つ。

short:小さい
[short]短い
一般の型より小さい型。ぶっちゃけ環境によるとしか言いようがない。

long:大きい型
[long]長い
一般の型より大きい型。ぶっちゃけ環境によるとしか言いようがない。

long long:凄く大きい型
[long long]長い長い
一般のlong型より大きい型。ぶっちゃけ環境によるとしか言いようがない。

char:文字型
[character]文字
文字を表現する型。一般に1バイトと規定されている。(らしい)その実態は実は数字で、数字に対応する文字は表現される。

float:単精度浮動小数点
[float]浮いてる、浮動小数点の浮く
小数点を含む範囲を表現できるデータ型。多分4バイト位。単精度と言うだけあって後述のdoubleより精度は悪いが、それでもそこそこの範囲をきちんとカバーしてくれる。

double:倍精度浮動小数点
[double]倍の、倍精度の倍
小数点を含む範囲を表現できるデータ型。多分8バイト位。倍精度と言うだけあって表せる小数点の精度が高い。その反面メモリがちょっとだけ大きくなってしまう。

まあ、はいこんな感じです。

型の大きさはsizeof(型)で調べることができます。次のような感じですね。

 #include <stdio.h>

int main() {
	int i = 0;
	short s = 0;
	long l = 0;
	long long ll = 0;
	float f = 0;
	double d = 0;
	char c = 0;

	printf("int byte: %zu\n", sizeof(i));
	printf("short byte: %zu\n", sizeof(s));
	printf("long byte: %zu\n", sizeof(l));
	printf("long long byte: %zu\n", sizeof(ll));
	printf("float byte: %zu\n", sizeof(f));
	printf("double byte: %zu\n", sizeof(d));
	printf("char byte: %zu\n", sizeof(c));

	return 0;
}


型にはデータを保存することができます。データを保存するためのメモリには好きな名前を付けることができます。例えば、

int Name;

こうすることで、int型のNameというメモリが確保されます。このNameには整数の値を保存することができ、データを保存するときにはイコールを使います。

Name = 12;

イコールを使った式を別名代入文と言います。この代入によってNameと言うメモリに12が保存されます。つまり、Nameはこのとき整数の12を示します。

>>Name #インタプリタ風の表記
12

一つ注意が必要なのは文字です。文字列は""で表現され、文字単体は''で表現されます。

char character = 'a';
char string[] = "AAAA";

こんな感じ。文字列は配列やらなんやらで表現されますがここでは特に触れないことにします。

3.エスケープシーケンス

文字列を表現する際に、特殊な出力を行う文字が存在します。これをエスケープシーケンスと言うそうです。例えば文字を改行する、文字列の最後を表現するなどがあります。

エスケープシーケンスには次のようなものがあります。

\a:アラートを鳴らす
\b:一文字戻る
\f:ページ送り
\n:改行
\r:行の先頭に戻る
\t:水平タブ、空白で間を開ける
\v:垂直タブ 次の行に下す
\\:「\」の表示
\?:「?」の表示
\':「'」の表示
\":「"」の表示
\0:ヌル文字、文章終了の合図

実際にコードを入力してみて動作を確認してみましょう。

 #include <stdio.h>

int main() {

	printf("AAA \a BBB \n");
	printf("CCC \b DDD \n");
	printf("EEE \f FFF \n");
	printf("GGG \r HHH \n");
	printf("III \t JJJ \n");
	printf("KKK \v LLL \n");
	printf("MMM \\ NNN \n");
	printf("OOO \? PPP \n");
	printf("QQQ \' RRR \n");
	printf("SSS \" TTT \n");
	printf("UUU \0 VVV \n");
	printf("\nXXX %% YYY \n");

	return 0;
}


実際に動かすと次のような結果になります。


AAA  BBB
CCC DDD
EEE
 FFF
 HHH
III      JJJ
KKK
 LLL
MMM \ NNN
OOO ? PPP
QQQ ' RRR
SSS " TTT
UUU
XXX % YYY

とりあたって良く使用されるのは、\nと\0です。\nは文字を改行するさいに使用し、\0は文字列の操作を手動で行うとき、たまに使うことがあります。

4.フォーマット指定子

さて、それではprintfの最も重要な機能のお話になります。まずはprintfの定義を見てみましょう。


int printf(
   const char *format [,
   argument]...
);


printfは関数の中でも少々特殊な能力を持っていて、なんと、複数の引数を持つことができます。ここでは少しだけその仕組みについて見てみましょう。

コンピュータはメモリを保持するとき、スタックという仕組みを利用することが有ります。別名後入れ先出とも言いまして、メモリデータを山のように積み上げていく仕組みの事です。


スタックの動き

凄い雑な絵ですがこんな感じです。

printfはスタックを利用して、文字通り変数を山のように積んで引数を取ることが出来るのです。この引き取った数を文字列に変換して画面に表示してくれます。

ただし一つだけ問題があります。それは、その変数の型が分からず、従ってメモリの大きさが分からないことです。この分からない型を明示的に示す方法として、フォーマット指定子と言う物を書き込む必要があります。


まず先頭には%を入力します。次に符号、全体の幅、少数点の幅、指定子の順番で記入します。

%[符号][全体の幅].[小数点の幅]<フォーマット指定子>

フォーマット指定子は型ごとに使い分けます。

%d: int,short型
%u: unsigned型
%l:  long型
%o: 8進数表記、int型、short型
%x: 16進数表記、int型、short型
%c: char型
%s: 文字列、char*型
%f: 実数を出力、floatやdouble型
%e: 実数を指数表示、floatやdouble型

細かいことは上記のドキュメントを見て頂けると幸いです。

 #include <stdio.h>

int main() {
	int i = 12;
	float f = 1.25;
	double d = 1.125;
	char c = 's';
	char s[] = "string";

	printf("%d %u %o %x\n", i, i, i, i);
	printf("%f %lf %.1e %.2e\n", f, f, f, f);
	printf("%f %lf %.1e %.2e\n", d, d, d, d);
	printf("%c %s\n", c, s);

	return 0;
}

出力はこんな感じです。

12 12 14 c
1.250000 1.250000 1.2e+00 1.25e+00
1.125000 1.125000 1.1e+00 1.12e+00
s string


5.結び

はい、大体こんな感じです(語彙喪失)
いや、細かい話を調べると結構泥沼に嵌るので、あんまり詳しいことは説明できませぬ。申し訳ない。

自作で可変長の関数を作ろうとしたときには、va_list,va_start,va_arg,vprint,va_endを使ったりと色々出来るのですが、まあめんどくさいです。

C++ですと、coutで終わるから楽でいいですよね……。

次回


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