プログラミング言語で必ずあるものといえば、真っ先に変数が上がるでしょう。変数とは処理途中のデータ(数字など)を保管しておく箱のようなものです。
int a; a = 10; int numof_allocated_array_at_a_time = 15; char ch = getchar();
x <- 123
1行目で場所を確保し、その領域に名前(識別名)をつけて (宣言)、2行目でその領域に値を書き込んだ (初期化)、といったほうがわかりやすい・・・のかな?
1行目と2行目をまとめて書いたのが3行目です。4行目のように関数の戻り値を代入することもできます(関数については後述)
C言語はとくに型にうるさい言語として知られています。(JavaScriptやperlなど型を明示できない言語もある)。
じゃあその「型」ってなんでしょうか?「型」とはコンパイラに変数をどういう風に扱ったらいいのか教えるものです。
基本型名 | 整数型/浮動小数点型 | 型の大きさ | signedの最大値を求めるマクロ | signedの(実数の)最小値を求めるマクロ | unsignedの最大値を求めるマクロ | 使用例 |
---|---|---|---|---|---|---|
void | 不定 | 不定 | 不定 | 存在しない | 存在しない | ほぼ例外なく関数かポインタとともに用いるので省略 |
bool型(C99,C++) | 論理型 | 不定 | 存在しない | 存在しない | 存在しない | bool is_alphabet = true; |
char | 整数型 | 1byte | SCHAR_MAX | SCHAR_MIN | UCHAR_MAX | char delim = ','; |
short(short int) | 整数型 | 不定 | SHRT_MAX | SHRT_MIN | USHRT_MAX | short rect_x = 320; |
int | 整数型 | 不定 | INT_MAX | INT_MIN | UINT_MAX | int a = 3; |
long(long int) | 整数型 | 不定 | LONG_MAX | LONG_MIN | ULONG_MAX | long size = 217; |
long long | 整数型 | 不定 | LLONG_MAX | LLONG_MIN | ULLONG_MAX | long long size = 21740; |
float | 浮動小数点型 | 不定 | FLT_MAX | -FLT_MAX | 存在しない | float r = 312; |
double | 浮動小数点型 | 不定 | DBL_MAX | -DBL_MAX | 存在しない | double bmi = 6.41284; |
FLT_MAXって「魔法科高校の劣等生」の「four leaves technology」みたいでかっこいい!・・・話がそれた。
以上にC言語(C99)の基本型を上げてみた。各型によって大きさ(表せる値の範囲)や表せるものが違う。
INT_MAXとかマクロとかって何?ってなると思うが、そう遠くなくお世話になるだろうから載せておく。解説は後ほど。
C言語で型の大きさについての規定はほとんどなく、
程度しかない。小数点型に関しては特に規定はないが、大抵IEEE 754に従うので、浮動小数点型についてはIEEE 754を参照すると良い。
まあ、ほとんどcharかdoubleかintしか使わない。ほんと。
厳密に型の大きさが定まっている必要が出ることは少なく、boolかcharかdoubleかintしか出番はないはず、それ以外を選択しようとしているなら、本当に必要か考えなおそう。
先ほどの表でINT_MAX
とかINT_MIN
とかいろいろ有りましたが、
要するにその型に入れられる最大値とか最小値です。limits.h
をインクルードすることで参照できます。
型の大きさが処理系によって異なることが日常のC言語ですが、時には求められないと困ることが有ります。メモリーの動的確保をするときなんかですが。
そこでsizeof
演算子です。使ってみましょう。
printf("%d\n", sizeof(int));
sizeof演算子には必ずしも()
は必要ありませんが、あったほうがはるかに見やすいのでつけましょう。
既にちらっと書いてますが、整数型にはsignedとunsignedがあります(浮動小数点型にunsignedはない)。
御存知の通り、PCと言うのはすべてのデータを2進数で表しています。この2進数の一桁をbitと言うわけですが、
1bitを使って正の数か負の数かわかるようにしているのがsigned、すべて正の数とみなすのがunsignedになります。
正の数か負の数かのフラグを使わない分、unsignedの最大値はsignedの最大値のほぼ2倍になります。
例:1byte=8bit,int型の大きさ(sizeof(int))を4byteと仮定すると、
INT_MAX: 2147483647
UINT_MAX: 4294967295
char型以外では、signedやunsignedを省略するとsignedとして扱われます。char型の場合は処理系定義となります。
int tmp = 5; signed int tmp = 5; unsigned int tmp = 5; char temp = 6; signed char temp = 6; unsigned char temp = 6;
1行目と2行目は同値。4行目と5行目が同値である保証はないし、だからといって4行目と6行目が同値である保証もない(処理系定義)
C言語にはtypedefという機能があります。さっそく使ってみましょう。
typedef unsigned long DWORD; typedef DWORD COLORREF; unsigned long window_head = 0x00FFCCFF; COLORREF window_head = 0x00FFCCFF;
typedefとは、"すでにある"型に別名をつける機能です。
1行目ではunsigned long型にDWORD型という別名をつけています。
2行目ではDWORD型にCOLORREF型という別名をつけています。
こんな風に別名を付けて何がありがたいかというと、型名を調べるだけでそのデータの格納方法がわかるということです。
3行目では何をしているのかさっぱりですが、4行目を見るとなんか色が関係することをやってるんだな、と察せます。
さらに「COLORREF」でぐぐれば、その詳細もわかります。
https://msdn.microsoft.com/en-us/library/windows/desktop/dd183449%28v=vs.85%29.aspx
When specifying an explicit RGB color, the COLORREF value has the following hexadecimal form:0x00bbggrr
ちなみに1行目と2行目はいずれもWindef.hで定義されているものの引用です。Win32APIに触りたいと思っている方、よく覚えておきましょう。かならずお世話になるでしょう。
また、先程から散々言ってるように、C言語では型の大きさについて、明確な規定はほぼありません。これでは厳密に何bitかを使いたいとき(HTTP通信など)に困ってしまいます。
そこでstdint.h/cstdintの出番です。これはC99で追加された標準ライブラリで、主に以下の型が使えます。
int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t
これらは厳密に型の大きさが決まっています。(ゆえにすべての処理系で実装されているわけではない)
uint8_t color_r = 187; int64_t receive_timestamp;// 受信タイムスタンプ //中略 int64_t sum = sum1 + sum2;
あるって知ってると何かと便利。
ある変数をその後で変更する予定がないときはconstをつけるようにするべきです。constは変数を「定数」にします。
const unsigned int len = 20; len = 21;//エラー
constをつけるときは定義と初期化は同時である必要があります。だって「定数」だもん、後から変更できないんだから初期化も一緒にやらないとダメだよね。
逆に言うと定義と初期化を同時に行うときは殆どの場合constをつけるべき場面です。
変数をなるべくconstにするようにプログラミングすると多くの場合見やすくわかりやすいものになりやすいです。
必然的に関数に分けたりするようになりますからね。
unsigned int len = 20;
のように、何気なく20と数値を書いていますが、この数値は「リテラル」と呼ばれます。どう見ても小数ではないのでこれは「整数リテラル」ですね。
int e = 01234;// 10進数では、668このように「0」のあとに数字を書きます。8進数リテラルは、10進数と区別しにくいので、気をつける必要があります。
int t = 1234;
int x = 0x1234 ;// 10進数では、4660このように「0x」のあとに数字を書きます。
int a = 0b11;// 10進数では、3 int d = 0B1011 ;// 10進数では、11このように「0b」または「0B」のあとに数字を書きます。
なにが言いたいかというと、うっかりして0から数字を書くなよ!ということです。
自動変数ってなんなんだってばよっ!となってると思いますが、「いままで出てきたような」変数です。
スコープとは、変数を参照できる範囲を指します。自動変数はスコープに入ると必要なメモリ等が自動的に割り当てられ、 スコープから出ると開放されます。実際のスコープの範囲については見てもらったほうが早いと思います。
{ int a = 10; printf("%d\n", a); } printf("%d\n", a);//エラー
int a = 10;の次の行からブロック ({}) が終わるまでがaのスコープになります。
スコープの中で確保された変数はスコープの外にでるときに開放されます。消えてなくなるわけです。 よって5行目はエラーになります。
printf関数を使うのは初めてなので補足します。今回はintの内容を10進数で表示します。詳しくはおいおい解説します。
この自動的に割り当てられるという性格から、自動変数にはいくつかの特徴があります。
まず、自動変数は効率的です。通常、割り当て、開放は関数に入る/出る時点で他の処理と合わせて自動的に行わるため、 コストがほとんどかかりません。更に、コンパイラは必要なければメモリに保存せず、レジスタを使うことができます。 コンピュータはメモリからレジスタに値を読み込み、レジスタを使って演算を行い、 その結果をメモリに格納するという動作を繰り返します。自動変数であればそのメモリへのアクセスが必要か判断できるわけです。
一方で、その性格からくる欠点もあります。一番の問題はメモリの割り当てに失敗したことを検知できないということです。 その場合の動作は予測不能です。多くのOSはプログラムを強制終了させます。
また、関数が終わるまで開放できないため、必要なくなった変数はそれまでメモリを無駄に専有します。 こうした理由から、自動変数として大きなデータや容量がコンパイル時にわからないものを扱うことはあまり想定されていません。 このような場合には、ヒープを扱う関数を使います。それについては今後説明します。
ちなみにJavaScriptはC言語とスコープの範囲が大きく異なるので、十分注意してください。
JavaScriptのスコープ入門 | BIBORO
http://www.biboro.org/snippet/426