変数についてより詳しく
C言語勉強会 第九回
kumar8600
June 2, 2013
C言語勉強会 第九回
kumar8600
June 2, 2013
強制的に別の型に変換 したいときに用いる。
(型)式
(double)i
左辺の型と右辺の型が異なっている場合は、 左辺の型に変換 される。
次の例では、 a は小数部の1415が切り捨てられて「3」になる。
int a;
double x = 3.1415;
a = x;
式の中で異なる型の定数や変数が現れたときは、 精度の高い型に統一 される。
char < int < long < float < double下の式では x は 1 になってしまう。
int a = 3;
int b = 2;
float x = a / b;
こうすれば右辺はfloat型の精度で計算され、 x は 1.5 になる。
float x = (float)a / b;
以下のキーワードを整数型の前後に付加することで符号の有無を指定する。
| キーワード | 意味 |
|---|---|
| signed | 符号あり |
| unsigned | 符号なし |
符号無しのint型変数を宣言するには以下のように書く。
unsigned int x;
signedが指定される。(ただしchar型は処理系による)それは、符号なしの時のほうが、扱える値の範囲が正の方向に長いので、そのほうが便利な場面があるからだ。
変数の型によって、扱える値の範囲は変わる。
| 符号 | 型 | 容量 | 値の範囲 |
|---|---|---|---|
| signed | char | 1 | -128 ~ 127 |
| unsinged | 0 ~ 255 | ||
| signed | short | 2 | -32,768 ~ 32,767 |
| unsigned | 0 ~ 65,535 | ||
| signed | int※ | 4 | -2,147,483,648 ~ 2,147,483,647(±約21億) |
| unsigned | 0~4,294,967,295(約42億) |
| 符号 | 型 | 容量 | 値の範囲 |
|---|---|---|---|
| signed | long | 4 | -2,147,483,648 ~ 2,147,483,647(±約21億) |
| unsigned | 0~4,294,967,295(約42億) | ||
| signed | long long | 8 | -9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807(±約922京) |
| unsigned | 0~18,446,744,073,709,551,615(約1844京) |
short, long, long long は ~ intの略である。精度が低い順に、
floatdoublelong doubleがある。
それぞれが、具体的にどの程度の範囲の値を扱えるかは環境に依存する。(整数型もそうだが)
とりあえずdouble型を使うと高速だし十分な精度があると思う。
詳しくはこれ見てください
sizeofに渡された型や変数のメモリサイズを返す。
sizeof 変数名
または
sizeof (型名or変数名)
int integerSize = sizeof (int);
これは int 型は4バイトのメモリを使うという意味だ。
(int型のメモリサイズは環境依存)
int i = 10;
int integerSize = sizeof (i);
変数名の場合、()を書かなくても良い。逆に型名のときは必須なので注意。
(int型のメモリサイズは環境依存)
int arr[10];
int arrSize = sizeof(arr);
これは、サイズが4であるint型変数を10個持っているからである。
(int型のメモリサイズは環境依存)
配列の要素数 = sizeof(配列名) / sizeof(配列の要素の型)
すなわち
配列の要素数 = sizeof(配列名) / sizeof(配列名[0])
配列の要素の型のサイズ * 配列の要素数 だからである。式に直接、定数を書き込むとき、接尾語を付けることでその型を指定出来る。
float x = 3.0f / 2.0f; // 3.0と2.0がfloat型であることを示している。
| 接尾語 | 型 |
|---|---|
| なし | int 以上の符号あり整数型 |
| u または U | unsigned int 以上の符号なし整数型 |
| l または L | long int 以上の符号あり整数型 |
| ll または LL | long long int |
| 接尾語 | 型 |
|---|---|
| なし | double |
| f または F | float |
| l または L | long double |
423 : int
423L : long
423.0f : float
423.0 : double
{と}に囲まれた領域をブロックと呼ぶ。
プログラムの一部分でしかアクセスできない変数のこと(グローバル変数の逆)
#include <stdio.h>
void print_a() {
int o = a;
printf("%d\n", o);
}
int main(void) {
int a = 10; /* ここから変数 a にアクセスできる */
print_a();
return 0;
} /* ここで変数 a は開放され、消滅する */
先程の例では a にアクセスできるのはmain関数の終わりまでである。
こういったある変数や関数が特定の名前で参照される範囲のことを、 スコープ と呼ぶ。
スコープ外で同名の別の変数を新たに宣言することが可能である。
このスコープがなるべく狭くなるようにプログラムを書くと、ミスの少ないコードを書ける。特にグローバル変数は必要最小限の量にしよう。
全てのスコープからアクセスできる変数。どこの関数の中でもない、プログラムの最も外側の部分に書く。
#include <stdio.h>
int a = 3;
int main(void) {
printf("%d\n", a);
return 0;
}
externを利用することでアクセスできるようになる。ブロックの外側で宣言された変数よりも、内側で宣言された変数が優先される。
#include <stdio.h>
int a = 3;
int main(void) {
int a = 10;
printf("%d\n", a);
return 0;
}
今までの内容を理解しているなら、下のプログラムを実行するとどう出力されるかわかるはずだ。
#include <stdio.h>
int main(void) {
int a = 10;
if(1) {
int a = 20;
printf("%d\n", a);
}
printf("%d\n", a);
return 0;
}
main関数ではじめに a を宣言し、10で初期化した。その後、if文の中でもう一度 a という名前の変数を宣言したが、これらが全く別の変数であることを意識出来れば、なんの迷いもない問題だ。
20(改行)10。変数の保存方法を更に細かく指定する
通常の変数の宣言の前につける。
記憶クラス指定子 型名 変数名;
または
型名 記憶クラス指定子 変数名;
static int a;
または
int static a;
プログラム中の宣言されたブロック内でのみ使用できる変数。
つまり、 今まで使ってきた普通の変数 。
auto 型名 変数名;
または
型名 変数名;
auto 変数名と書いたからである。型名が無いが、B言語には整数型しかなかった。特定の区域内に到達すると自動的に変数領域を確保し 、 その区域を脱すると自動的にその領域を開放して消滅する 点が自動
void print_a() {
int o;
o = 10;
printf("%d\n", o);
}
この関数を呼び出した場合、
関数を呼び出すと同時にメモリーのどこかあいてるところに int型変数o のための領域を確保し、
関数を実行しおわると先ほど確保した 変数o の領域を開放し消滅する。
砕いていうと、関数を呼び出すたび変数を作り、抜けるとき変数を消してるだけである。
どこに宣言していようとも関係なく、プログラムの実行開始時にメモリ上に作られ、初期値も与えられる変数 。
static 型名 変数名;
#include <stdio.h>
void printCalledNum() {
static int n = 0;
n++;
printf("%d回目の呼び出し\n", n);
}
int main(void) {
printCalledNum();
printCalledNum();
printCalledNum();
return 0;
}
静的変数は、具体的な初期値を与えなかった場合、必ず 0 で初期化されることが保証されている。
また、グローバル変数も、具体的な初期値を与えなかった場合、必ず 0 で初期化される。
グローバル変数にstaticをつけると、今まで説明した意味と全く違う意味になる。
変数の実体が、別のところに存在していることを表現する
extern int a;
コンピューターには主記憶装置である「メモリ」の他に、CPUに内蔵されていて、メモリと比べるととてつもなく速く、低容量な「 レジスタ 」がある。実はCPUによる計算は、メモリに入っている値を一度レジスタにコピーしてから行われている。(なんか最近は違うらしい)
そんな高速なレジスタに変数を格納したいとき、この指定子を使う。
register int a;
register変数は「出来れば」レジスタに変数を格納する。容量が足りないとき等は結局いつもどおりメモリを使うので注意。
あと、register変数のアドレスを得ることは出来ない。
データ型に新しい名前(別名)をつける。
構造体を宣言する際にも何気なく使ってきたと思う。
例えば、int に自分の好きな別名をつけるには、
typedef int Seisu;
このように書く。この例では int に Seisu という別名をつけた。なので以降、
Seisu n;
と書くことでも int 型変数 n を宣言できる。
unsigned int に uint という別名をつける
typedef unsigned int uint;
ある構造体を名前をつけ宣言する。
typedef struct {
char name[20];
double height;
double weight;
} HUMAN;
データ型を用途に応じてきめ細かく指定する。
変数を宣言する際、型の前後に付加して使う。
型修飾子 型名 変数名;
または
型名 型修飾子 変数名;
const int a;
int const a;
関数の仮引数で型修飾子を使うことも出来る。
void func(const int a) {
...
}
指定した変数が定数であることを指定する。
const 型名 変数名;
または
型名 const 変数名;
#include <stdio.h>
int main(void) {
const int a = 10;
a += 2;
printf("%d\n", a);
return 0;
}
a を a += 2で、値を書き換えようとしているため、このプログラムはコンパイル時にエラーする。定数の初期化は宣言時にしか行えないので注意。
ポインタ定数の場合特別な挙動をする。これを積極的に使うと便利である。ポインタを紹介してないから今は書かない。
わざわざ変数を定数にするのは、プログラムを書く際のミスを減らすためである。変更しないつもりの変数は積極的に定数として宣言すべきである。
厳密な話、C言語におけるconstは定数ではなく、ただの「書き換えできない変数」である。なので、配列宣言時の要素数指定や、switch文のcase定数式などに必要な、「コンパイル時に既に値が確定している必要がある」という意味の、本当の定数とは別であることに注意。
コンパイラによる最適化を行わない変数。
今のコンパイラは頭がいいので、私たちの書いたなプログラムをできるだけ素晴らしい効率で動くように最適化する。
だが、マルチスレッドなプログラム等、コンパイラの設計の想定を超えるプログラムを書いた場合、逆にその最適化のせいでプログラムが正常に作動しなくなることがある。