変数

変数と代入

プログラミング言語で必ずあるものといえば、真っ先に変数が上がるでしょう。変数とは処理途中のデータ(数字など)を保管しておく箱のようなものです。

int a;
a = 10;
int numof_allocated_array_at_a_time = 15;
char ch = getchar();
宣言
1行目では変数を宣言し定義しています。宣言は「こんな変数があるよ」とコンパイラに教える作業で、定義は「ここで変数を確保するよ」とコンパイラに教えることです。
定義して初めて実体を持ち、変数を使えるようになるわけです。
まとめると、変数を保管する場所を確保し、場所に名前をつけた、という状態です。
代入
数学の代入とは少し違います。2行目の場合はaに10という値を記憶する、という意味になります。ここでつまずくとプログラミングができなくなるので、しっかり抑えてください。
なお、多くの言語で代入は「=」ですが、R言語のように「<-」を用いる言語もあります。
x <- 123
初期化
初期化、という言葉はかなり多くの人が誤解しています。0を代入することを初期化、と考える人が後を絶ちません。
1行目で変数を宣言し定義しましたが、この状態ではaにどんな値が入っているかは不定です。
2行目のように、プログラマがその変数に何が入っているかわかるようにすることを初期化といいます。
ポインタの話が出てこない限りは、とりあえず定義した後初めて値を代入すれば、初期化している、と思って構いません。
ポインタが絡んだ時のことは、その時にゆっくり解説します。

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

既にちらっと書いてますが、整数型にはsignedとunsignedがあります(浮動小数点型にunsignedはない)。
御存知の通り、PCと言うのはすべてのデータを2進数で表しています。この2進数の一桁をbitと言うわけですが、
1bitを使って正の数か負の数かわかるようにしているのがsigned、すべて正の数とみなすのがunsignedになります。
正の数か負の数かのフラグを使わない分、unsignedの最大値はsignedの最大値のほぼ2倍になります。

description_of_signed/unsigned

例: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行目が同値である保証もない(処理系定義)

typedef

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に触りたいと思っている方、よく覚えておきましょう。かならずお世話になるでしょう。

stdint.h

また、先程から散々言ってるように、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は変数を「定数」にします。

const unsigned int len = 20;
len = 21;//エラー

constをつけるときは定義と初期化は同時である必要があります。だって「定数」だもん、後から変更できないんだから初期化も一緒にやらないとダメだよね。

逆に言うと定義と初期化を同時に行うときは殆どの場合constをつけるべき場面です。

変数をなるべくconstにするようにプログラミングすると多くの場合見やすくわかりやすいものになりやすいです。

必然的に関数に分けたりするようになりますからね。

整数リテラル

unsigned int len = 20;

のように、何気なく20と数値を書いていますが、この数値は「リテラル」と呼ばれます。どう見ても小数ではないのでこれは「整数リテラル」ですね。

8進数的記法
int e = 01234;// 10進数では、668
このように「0」のあとに数字を書きます。8進数リテラルは、10進数と区別しにくいので、気をつける必要があります。
10進数的記法
int t = 1234;
16進数的記法
int x = 0x1234 ;// 10進数では、4660
このように「0x」のあとに数字を書きます。
2進数的記法(C言語非公式拡張, C++14)
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