ジャンル雑多なゲーム・ゲーム制作関連の色々な情報を取り扱っているブログ。最近はBlenderについてが中心。
[C, C++学習]C, C++言語再学習ノート-5日目- –再まとめ変数型、書式指定子、型サイズ修飾子、型符号修飾子、接尾子、型修飾子の宣言時の並べ方、ビット論理演算、ビットシフト

[C, C++学習]C, C++言語再学習ノート-5日目- –再まとめ変数型、書式指定子、型サイズ修飾子、型符号修飾子、接尾子、型修飾子の宣言時の並べ方、ビット論理演算、ビットシフト

再まとめ変数型、書式指定子、型サイズ修飾子、型符号修飾子、接尾子、型修飾子の宣言時の並べ方、ビット論理演算、ビットシフト 勉強しなおしの記録というか学習ノート-5日目-

Unreal Engin4すごい!(語彙力)ってなったので、少しいじりつつ、むかーしかじった事あるC,C++の学びなおしの学習ノート。この連載記事の詳しい趣旨と注意事項は1日目をご覧ください。

※2019年5月28日 加筆・修正を行いました。

目次

データ型やら型修飾子やら書式指定子のことを訂正・補足

1日目であんまり触れずに「型が色々あるよ!」っていう軽い(しかも一部誤解を与えかねない)説明してしまっていましたが、(というより自分の中でも型修飾子とか書式指定子とかをあんまり覚えてなかったのと一部記憶違いをしてしまってましたorz)、「こういうのがあって、データ型は基本は4つで、型に修飾子がいろいろ引っ付いたり、あとは、こういう数値のときに、書式指定子ってこれを使うみたい!」っていうのを今更ながら補足というか、訂正(そして関係各位(データ型様並びに型修飾子様方)に深く謝罪を申し上げます)。

文字・文字列を扱うデータ型について –char

説明対応する指定子
char文字 ※文字列ではなく、「’」で囲った1文字の半角英数だけ%c
char*, char []文字列 ※「””」で囲った文字なら、基本なんでもおっけー%s

文字

この”%c”というのを”%d”にすると文字ではなく、数値が表示されます。これは、char型変数は数値を格納していて、書式指定子”%c”でASCIIコードというコード表から、その格納されている数値に対応する文字に変換しているからなのです。

外部ページ:ASCIIコード表

ちなみに、1バイトでは日本語文字(例えば’あ’)は格納できません。

上にリンク張ってあるASCIIコードをご覧くださればお分かりになるかと思われますが、アルファベットは大文字小文字併せても52文字しかなく、更にいくつか記号を入れても、0~127の全128個だけ確保しておくだけで事足りてしまいます。

日本語文字は漢字なども含めれば種類が膨大なので、とても1バイト(256個)には納めきれないのです…。
だから、どうしてもchar型2文字分の配列(2バイト=256*256=65536個)で1文字という形での格納となってしまうわけですね。(データの世界では量が多すぎるとこういう扱いになってしまうのです…漢字、便利なこともいっぱいあるんですが仕方ないですね)

#include <stdio.h>

void main() {
	char ch = 'a';
	printf("%c", ch); // 実行結果:a
}

文字列

文字列は、当たり前ですが、文字が複数格納されているわけなので、配列で格納されます。

  • ガッツリ配列とわかる格納方法
char st[] = { 'a','b','c' };
printf("%s\n", st); // 実行結果:abc
  • 失敗例※’あ’は無理ですオーバーフローします。違う文字で表示されます。
char st[] = { 'あ','b','c' };
printf("%s\n", st); // 実行結果:・bc
  • 別パターンで文字列の格納方法
char st[] = "abcdeあいうえお";
printf("%s\n", st); // 実行結果:abcdeあいうえお
  • ちなみに、一文字一文字確認する方法 ※1バイトずつで見るので、日本語は違う文字になってしまいます
char st[] = "abcdeあいうえお";
printf("%c\n", st[4]); // 実行結果:e
  • おまけ:後でやるポインタ使って参照する方法
char* st = "abcdeあいうえお";
printf("%s\n", st); // 実行結果:abcdeあいうえお

参考にさせていただいたページ:
ASCIIコード表
田村仁研究室 日本工業大学創造システム工学科
【C言語入門】文字・文字列(char)の使い方

整数を扱うデータ型について –int

説明対応する指定子
int符号付整数%d, %o, %x

“d”はお馴染みだと思いますが、”o”は8進数で、”x”は16進数として整数値を表示します。

int n = 10;
printf("10進法での10を変換\n");
printf("10進法: %d\n", n);
printf("8進法: %o\n", n);
printf("16進法: %x\n", n);

実行結果が↓になります。

10進法での10を変換
10進法: 10
8進法: 12
16進法: a

ちなみに、数字の前に”0″をつけると8進数に、”0x”をつけると16進数になるというのを使うと組み合わせて使ってみると当然こうなる訳です↓

printf("10進法: %d\n", 10);
printf("8進法: %o\n", 010);
printf("16進法: %x\n", 0x10);

実行結果↓

10進法: 10
8進法: 10
16進法: 10

浮動小数点数を扱うデータ型について –float, double

浮動小数点数を、Aを仮数部、Bを基数、Cを指数部と言う形で、A * B^Cという形で表現した場合。

float型であれば4バイト(32ビット)で、符号部に1ビット、仮数部に23ビット、指数部に8ビットを使用し、

double型であれば8バイト(64ビット)で、符号部に1ビット、仮数部に52ビット、指数部に11ビット使用しています。

ここら辺の詳しい計算方法を詳しく知りたい方は、↓のページがわかりやすいかと思いますので、そちらをどうぞ。

参考にさせていただいたページ:
ビットで表す数字の世界~浮動小数点編~
仮数部、指数部、基数:意味と計算方法 – 具体例で学ぶ数学
浮動小数点

説明対応する書式指定子
float浮動小数点数%f, %e, %g
double倍精度浮動小数点数%lf, %le, %lg

書式指定子で、それぞれ、”f”,”e”,”g”がついていて、更にdoubleではそれに”l”がついているのはお気づきでしょうか?

これは、まず、”f”は実数を表し、”e”は実数の指数表示、”g”は実数の最適表示を表し、最後に”l”は精度を倍にするという意味を持つ指定子だからです。

float flo = 3.14f;
double dou = 3.14;
printf("%f\n", flo); //実行結果:3.140000
printf("%lf\n", dou); // 実行結果:3.140000

違うのは精度なので、これではちょっと違いがわかりませんよね(^_^;)

ということで、後述の、「それぞれのデータ型の最大値、最小値、データサイズの調べ方」の項で違いを比べられるようにしてみます。

参考にさせていただいたページ:
【C言語入門】浮動小数点数(double・float)の使い方|侍エンジニア塾ブログ
出力書式のまとめ 変換指定子 – printf出力書式まとめ – 碧色工房
フォーマット指定子一覧
C言語入門 – printf関数の変換指定子 – Webkaru

変数型の修飾子、接尾子

度々、説明もなしに一緒くたに使っていましたが、”unsigned”や、”signed”、”long”、”short”について書いてみたいと思います。

修飾子説明対応する接尾子対応する書式指定適応する型
unsigned符号が付かない(正の数の領域を多く確保する)u, U%uchar, int
signed符号あり(正の数、負の数の両方の領域を確保する)char, int
long付ける型よりも同じか、データ領域を多く確保するl, L%lint, double
short付ける型よりもデータ領域を小さく確保する%hint

また、”long”は”long int”と略すことができ、”short”は”short int”と略せます。

参考にさせていただいたページ:
接尾子
型修飾子
データ型の修飾
フォーマット指定子一覧

それぞれのデータ型の最大値、最小値、データサイズの調べ方

それぞれのデータ型の最大値、最小値を調べられるデフォルトのマクロが用意されていたりします。マクロに関しましては、先日書いた記事がありますので、よろしければそちらもご参照ください。

浮動小数点数以外の型のマクロは「limits.h」、浮動小数点数の型のマクロは「float.h」をインクルードする必要があります。

また、sizeof()関数という、それぞれの変数に確保されているデータサイズを確認できる変数がありますので、これを使って、具体的なそれぞれの型のデータサイズを比べてみます。

書式:sizeof(変数);

#include <stdio.h>
#include <limits.h>
#include <float.h>

void main() {
	char ch = 'a';
	short sh = 1;
	int i = 1;
	long lo = 1L;
	float fl = 0.1f;
	double dou = 0.1;
	printf("//char/////////////\nMax: %d\tMin: %d\t\tsize: %dバイト\n", CHAR_MAX, CHAR_MIN, sizeof(ch));
	printf("/*signed char*****\nMax: %d\tMin: %d\t\tsize: %dバイト\n", SCHAR_MAX, SCHAR_MIN, sizeof(ch));
	printf("/*unsigned char*****\nMax: %d\tMin: %d\t\tsize: %dバイト\n", UCHAR_MAX, 0, sizeof(ch));
	printf("//short////////////\nMax: %hd\tMin: %d\t\tsize: %dバイト\n", SHRT_MAX,SHRT_MIN, sizeof(sh));
	printf("/*unsigned short*****\nMax: %hu\tMin: %d\t\tsize: %dバイト\n", USHRT_MAX,0, sizeof(sh));
	printf("//int//////////////\nMax: %d\tMin: %d\t\tsize: %dバイト\n",INT_MAX,INT_MIN, sizeof(i));
	printf("/*unsigned int*****\nMax: %u\tMin: %d\t\tsize: %dバイト\n",UINT_MAX,0, sizeof(i));
	printf("//long//////////////\nMax: %ld\tMin: %d\t\tsize: %dバイト\n",LONG_MAX,LONG_MIN, sizeof(lo));
	printf("/unsigned long*****\nMax: %lu\tMin: %d\t\tsize: %dバイト\n",ULONG_MAX,0, sizeof(lo));
	printf("//float/////////////\nMax: %g\tMin: %e\t\tsize: %dバイト\n", FLT_MAX, FLT_MIN, sizeof(fl));
	printf("//double////////////\nMax: %lg\tMin: %le\t\tsize: %dバイト\n", DBL_MAX, DBL_MIN, sizeof(dou));
	printf("//long double////////////\nMax: %lg\tMin: %le\t\tsize: %dバイト\n", LDBL_MAX, LDBL_MIN, sizeof(dou));
}

実行結果↓

//char/////////////
Max: 127        Min: -128               size: 1バイト
/*signed char*****
Max: 127        Min: -128               size: 1バイト
/*unsigned char*****
Max: 255        Min: 0          size: 1バイト
//short////////////
Max: 32767      Min: -32768             size: 2バイト
/*unsigned short*****
Max: 65535      Min: 0          size: 2バイト
//int//////////////
Max: 2147483647 Min: -2147483648                size: 4バイト
/*unsigned int*****
Max: 4294967295 Min: 0          size: 4バイト
//long//////////////
Max: 2147483647 Min: -2147483648                size: 4バイト
/unsigned long*****
Max: 4294967295 Min: 0          size: 4バイト
//float/////////////
Max: 3.40282e+38        Min: 1.175494e-38               size: 4バイト
//double////////////
Max: 1.79769e+308       Min: 2.225074e-308              size: 8バイト
//long double////////////
Max: 1.79769e+308       Min: 2.225074e-308              size: 8バイト

参考にさせていただいたページ:
色々なデータ型の最大値、最小値
C言語関数辞典 – limits.h
C言語関数辞典 – float.h

変数の型や修飾子の宣言時の並べ方

記憶クラス符号サイズ
auto, staticsigned, unsginedshort, longchar, int, float, double

記憶クラス指定子に関しましては、もしわからなければ参考までに先日書きました記事のリンクを置いておきますのでよろしければご覧ください。

ちなみに、書式指定子の方は、こうなります。↓

フラグ最小フィールド幅精度長さ指定子
%+, -, #, 0小数点より以上の桁数を指定する数値.小数点以下にあたる桁数を指定する数値l, hd, o, x, e, g, a, c, s, u, f

参考にさせていただいたページ:
データ型の修飾
出力書式のまとめ 変換指定子 – printf出力書式まとめ – 碧色工房
printf関数のオプション指定

ビット演算

ビット論理演算

もし論理演算をご存じなくても、論理演算という字から、勘付く方も多いかと思われますが、”1″と”0″で、論理演算をするわけです。具体的に、どういった演算子があるのかを表でご紹介します。

演算子説明
&ビットごとの論理積
|ビットごとの論理和
^ビットごとの排他的論理和
~ビットごとの反転(1の補数)

& –論理積(and)

実際に触ってみれば、大体わかるかと思うので、とりあえず例としてコードを実行してみてください。

分かりやすいかなと思うので、16進数を位で縦にそろえて表示させてみました。

printf(" %x\n&%x\n-----------\n %x\n", 0xfffff , 0xf0f0f,0xfffff & 0xf0f0f);

実行結果↓

 fffff
&f0f0f
-----------
 f0f0f

つまり、どちらも”1″であるならば、”1″になる。どちらかいっぽうでも”0″なら”0″。両方”0″も”0″になるというものになります。

| –論理和(or)

printf(" %x\n|%x\n-----------\n %x\n", 0xfffff , 0xf0f0f,0xfffff | 0xf0f0f);

実行結果↓

 fffff
|f0f0f
-----------
 fffff

どちらか一方でも”1″であれば、”1″になります。

^ –排他的論理和(xor)

printf(" %8x\n^%8x\n-----------\n %8x\n", 0xfffff , 0xf0f0f,0xfffff ^ 0xf0f0f);

実行結果↓

    fffff
^   f0f0f
-----------
     f0f0

どちらか一方が”1″、もう一方が”0″の場合のみ、”1″になるというものです。両方同じ値だと、”0″になります。

~ –1の補数(ビットの反転)

printf("(~%10x)\n =%10x\n", 0xf0f0f0,(~0xf0f0f0));

実行結果↓

(~    f0f0f0)
 =  ff0f0f0f

各ビットが悉く反転しているのがお分かりいただけたでしょうか?

ちなみに、10進数のものをビット反転させると以下のようになります。

printf("(~%10d)\n =%10d\n", 1000,(~1000));

実行結果↓

(~      1000)
  =     -1001

ビットシフト

演算子説明
<<ビット左シフト
>>ビット右シフト

<< –左シフト

ビットを、指定された数だけ左へ移動させます。

printf("  %8x << %d \n= %8x\n", 0xf0f0f, 4, 0xf0f0f << 4);
printf("  %8o << %d \n= %8o\n", 070707, 3, 070707 << 3);

実行結果↓

     f0f0f << 4
=   f0f0f0
     70707 << 3
=   707070

つまり、書式を「x << n」すると、xの保持しているビットをそのまま、n個分左へ移動させ、そのn個分を0で埋めているわけです。

15は2進数で表せば”1111″で、4ビット分で表しているわけですので、4ビット分動かせば、”f0f0f”がそのまま、4ビット分ずれて、”f0f0f0″になっているわけです。

そして、7は2進数で表せば”111″で、3ビット分なので、3ビット動かせば、”70707″が”707070″になるわけです。

>> –右シフト

上の左シフトの逆バージョンと言えば、大体想像つくかと思いますが、例のごとく、実際に実行して試してみてください。

printf("  %8x >> %d \n= %8x\n", 0xf0f0f, 4, 0xf0f0f >> 4);
printf("  %8o >> %d \n= %8o\n", 070707, 3, 070707 >> 3);

実行結果↓

     f0f0f >> 4
=     f0f0
     70707 >> 3
=     7070

多分予想通りかとは思いますが、一応説明です。

この右シフトは書式を「x >> n」で表すとすると、 xの保持しているビットをそのまま、n個分右へ移動させ、そのn個分を0で埋めているわけです。

なので、”f0f0f”は4ビット右へずれて、一番右の”f”はアンダーフローして、なくなり、”f0f0″に。

“70707”は3ビット右へずれて、一番右の”7″はアンダーフローしてなくなり、”7070″になります。

参考にさせていただいたページ:
複雑な演算子
ビット演算

あとがき

ビットの扱いやっぱり好き。

すみませんいきなり何言ってるんだってかんじですよねw でも、ビット演算のところ、昔やったときも、変に興奮したり、結構お気に入りなんですよ!だから、今回記事に書けて嬉しかったりします(>∀<*)

さて、今日はUE4弄れなかったので、明日は触りたい。。。けど、せっかくだからこの勢いのまま明日はポインタをさらってしまいたい気も。。。悩みます(´д`;)

ご意見・ご感想・ご質問、ここ違ってるよとか、もっといい方法あるよといったご指摘などございましたら、お手数ですがコメント欄やtwitterアカウントほろほろり(@_horo_horori)へお願いしますm(_ _)m

Pocket