授業/C言語基礎/変数 のバックアップ(No.14)


コンピューターが記憶しているデータの一つ一つに名前をつけたものを変数といいます。

記憶するデータの種類によって、コンピューターが用意しなければならない記憶領域の大きさが異なります。

このデータの種類のことを変数のといいます。

int型

int型は、整数を記憶するためのデータ型です。

16ビットCPUでは16ビット(2バイト)、32ビットCPUでは32ビット(4バイト)というように、CPUが扱う基本サイズの大きさになります。 ただし、64ビットCPUでは、32ビットCPUとの互換性を保つために、64ビット(8バイト)でなく32ビット(4バイト)にするものもあります。

32ビットの2進数では、[math]2^{32}[/math]通り([math]4,294,967,296[/math]通り)のパターンを作ることができます。

整数には負の数も含まれますので、先頭のビットを符号ビットとし、符号ビットが1のときは負の数と考えます。 つまり、先頭の符号ビットが1である[math]2^{16}[/math]乗個のパターンは負の数を表すのに使われます。

残り[math]2^{16}[/math]個のパターンがありますが、「すべてのビットがゼロのパターン」は「ゼロの数」を表すのに使いますので、正の数を表すのに使うことができるのは[math]2^{16}-1[/math]個のパターンになります。

したがって、32ビットint型で表すことができる数は、[math]-2^{16}[/math]から[math]2^{16}-1[/math]まで、すなわち、[math]-2,147,483,648[/math]から[math]+2,147,483,647[/math]までです。

double型

double型は、小数を記憶するためのデータ型です。

コンピューターで小数を表すのには、浮動小数点数という表現が用いられています。 double型は、倍精度浮動小数点数といい、64ビットで表されます。

浮動小数点数は、[math](-1)^{S} \times M \times 2^E[/math]という形で表されます。 ここで、[math]S[/math]は符号、[math]M[/math]は仮数、[math]E[/math]は指数です。

わかりやすいように10進数で考えると、10進数の[math]0.00123[/math]は[math]1.23 \times 10^{-3}[/math]と表すことができ、このときの仮数は[math]1.23[/math]、指数は[math]-3[/math]です。

倍精度浮動小数点数では、64ビットが以下のように使われます。

  • 符号部1ビット(正のとき[math]0[/math]、負のとき[math]1[/math])
  • 指数部11ビット(実際の指数から[math]1,023[/math]を引いたもの)
  • 仮数部52ビット(仮数を2進数1.xxxという形にして、その小数点以下の部分)

double型で表すことができる数は、負の数は[math]-1.797693 \times 10^{308}[/math]から[math]-2.225074 \times 10^{-308}[/math]まで、正の数は[math]2.225074 \times 10^{-308}[/math]から[math]1.797693 \times 10^{308}[/math]までです。

浮動小数点数についての詳しいことは、計算機アーキテクチャーの授業や、数値解析の授業で勉強してください。

宣言

変数をCプログラムで使うには、最初に、どの型の変数をどのような名前で使うのかを宣言しなければなりません。

C言語では、以下のように変数を宣言します。

型 変数名;

たとえば、int型の変数をiという名前で使用することを宣言するときは、次のようにします。

#include <stdio.h>

int main(void) {
  int i;
  return 0;
}

また、double型の変数をdという名前で使用することを宣言するときは、次のようにします。

#include <stdio.h>

int main(void) {
  double d;
  return 0;
}

変数名をコンマ , で区切ることで、同じ型の複数の変数を一つの文で宣言できます。

 型 変数名1, 変数名2, ...;

たとえば、int型の変数x, y, zを宣言するときは、次のようになります。

#include <stdio.h>

int main(void) {
  int x, y, z;
  return 0;
}

しかし、型が異なるときは、一つの文では宣言できません。

たとえば、int型の変数iとdouble型の変数dを宣言するときは、次のようになります。

#include <stdio.h>

int main(void) {
  int i;
  double d;
  return 0;
}

代入

変数に具体的な値を入れることを、代入といいます。

C言語では、以下のようにイコール = を使って値を変数に代入します。

変数名 = 代入する値;

たとえば、int型の変数iに3を代入するときは、次のようになります。

#include <stdio.h>

int main(void) {
  int i;
  i = 3;
  return 0;
}

また、double型の変数に[math]-12.3[/math]を代入するときは、次のようになります。

#include <stdio.h>

int main(void) {
  double d;
  d = -12.3;
  return 0;
}

初期化

変数を宣言するときに値を代入することができ、これを初期化といいます。

C言語では、以下のように変数を初期化します。

 型 変数名 = 代入する値;

たとえば、int型の変数iを宣言し、同時に[math]3[/math]を代入するときは、次のようになります。

#include <stdio.h>

int main(void) {
  int i = 3;
  return 0;
}

また、double型の変数dを宣言し、同時に[math]-12.3[/math]を代入するときは、次のようになります。

#include <stdio.h>

int main(void) {
  double d = -12.3;
  return 0;
}

同じ型であれば、複数の変数を一度に宣言して初期化することもできます。

 型 変数名1 = 代入する値1, 変数名2 = 代入する値2, ...; 

たとえば、int型の変数x, y, zを宣言し、同時にそれぞれに[math]1[/math], [math]2[/math], [math]3[/math]を代入するときは、次のようにします。

#include <stdio.h>

int main(void) {
  int x = 1, y = 2, z = 3;
  return 0;
}

初期化しないときの変数の値

変数を宣言だけして初期化しないとき、変数の値がどうなるかは決まっていません。

総合情報センターのLinuxサーバーでは、int型とdouble型の変数を宣言すると、値が 0 に初期化されているように見えますが、これは保証されていません。

その変数を記憶するための場所(メモリー)がこのプログラムに割り当てられる前に、その場所を使っていた別のプログラムが残した値がそのまま残っていることもあり得ます。

printf命令による表示

printf命令を使って変数の値を画面に出力(表示)するときは、出力する文字列の中に % と変数の種類(int型のときは d、double型のときは f または e)を入れて、文字列の後にコンマ , をつけて、変数名を指定します。

例えば、int型の変数の値を表示するときは %d を使い、次のようになります(プログラム1)。

#include <stdio.h>

int main(void) {
  int i = 3;
  printf("i = %d\n", i);
  return 0;
}

このプログラムを実行すると、次のようになります。

luna% a.out
i = 3

double型の変数の値を小数形式で出力するときは %f を使い、次のようになります(プログラム2)。

#include <stdio.h>

int main(void) {
  double d = -12.3;
  printf("d = %f\n", d);
  return 0;
}

このプログラムを実行すると、次のようになります。

luna% a.out
d = -12.300000

double型の変数の値を指数形式で出力するときは %e を使い、次のようになります(プログラム3)。

#include <stdio.h>

int main(void) {
  double d = -12.3;
  printf("d = %e\n", d);
  return 0;
}

このプログラムを実行すると、次のようになります。

luna% a.out
d = -1.230000e+01

eの後に続く数が指数を表していて、[math]-1.23 \times 10^{+1}[/math]となります。

複数の変数の値を出力することもできます(プログラム4)。

#include <stdio.h>

int main(void) {
  int i = 3;
  double d = -12.3;
  printf("i = %d, d = %f\n", i, d);
  return 0;
}

このプログラムを実行すると、次のようになります。

luna% a.out
i = 3, d = -12.3

printf命令の名前のfはフォーマット (format) を表し、printf命令の中で変数のフォーマット(書式)を指定するための %d や %i のことを変換指定子といいます。

演習1

上のプログラム1, 2, 3, 4を作成し、実行せよ。

暗黙的型変換

ある型の変数を別の型の変数に代入すると、変数の型が暗黙的に変換されます。

たとえば、小数の値をint型の変数に代入すると、double型からint型に暗黙的に変換され、小数点以下が無視されて整数部分だけが残ります。 つまり、小数点以下が切り捨てられます。 (切り捨てではなく四捨五入するプログラミング言語もあります。)

次のプログラムについて考えてみましょう(プログラム5)。

#include <stdio.h>

int main(void) {
  int i = 1.23, j = 9.87;
  printf("i = %d, j = %d\n", i, j);
  return 0;
}

これを実行すると、次のようになります。

luna% a.out
i = 1, j = 9

整数の値をdouble型の変数に代入すると、int型からdoule型に暗黙的に変換されますが、値は変わりません。

演習2

プログラム5を作成し、実行結果を確認せよ。 また、整数の値をdouble型の変数に代入して出力するプログラムを作成し、実行結果を確認せよ 。

オーバーフロー(おまけ)

32ビットint型で表現できる数は、[math]-2,147,483,648[/math]から[math]+2,147,483,647[/math]までなので、[math]-2,147,483,648[/math]より小さい数や[math]+2,147,483,647[/math]より大きい数を表現することはできません。

32ビットint型の変数に[math]-2,147,483,648[/math]より小さい数や[math]+2,147,483,647[/math]より大きい数を代入すると、32ビットに収まりません。 これをオーバーフローといいます。

たとえば、32ビットint型の変数に上限より 1 だけ大きい数を代入する次のプログラムについて考えます(プログラム5)。

#include <stdio.h>

int main(void) {
  int i = 2147483648;
  printf("i = %d\n", i);
  return 0;
}

これをコンパイルすると、次のようなメッセージが出ます。

luna% gcc int.c
int.c: In function 'main':
int.c:4: 警告: 式の整数がオーバーフローしました

これは警告であってエラーではないので、実行可能ファイルa.outが作られていて、実行することができます。 そこで、このプログラムを実行すると、次のようになります。

luna% a.out
i = -2147483648

このように、プログラム実行時にオーバーフローが発生しても、プログラムは停止しません。

負の数は「2の補数」という形で表現され、先頭ビットが 1 のときは負、0 のときはゼロまたは正の数を表します。

32ビットのint型で表現できる最も大きな数を2進数で表すと次のようになり、これを10進数で表すと [math]2,147,483,647[/math] になります。

01111111 11111111 11111111 11111111

10進数 [math]2,147,483,648[/math] は、これに 1 を加えた数なので、2進数で表すと次のようになります。

10000000 00000000 00000000 00000000

これの値は先頭ビットが 1 なので、コンピューターはこれを負の値で、2の補数で表現されている数だと解釈します。

このため、画面には次のように出力されます。

-2147483648

2の補数についての詳しいことは、計算機アーキテクチャーについての授業で勉強してください。

演習3

オーバーフローが生じるプログラムを作成し、実行結果を確認せよ。

アンダーフロー(おまけ)

double型で表現できる数は、負の数は[math]-1.79769313486231570 \times 10^{308}[/math]から[math]-4.94065645841246544\times 10^{-324}[/math]まで、正の数は[math]4.94065645841246544 \times 10^{-324}[/math]から[math]1.79769313486231570 \times 10^{308}[/math]までなので、ものすごくゼロに近い数を表現することはできません。

double型の変数に[math]-1.79769313486231570 \times 10^{308}[/math]よりもゼロに近い負の数や、[math]4.94065645841246544 \times 10^{-324}[/math]よりもゼロに近い正の数を代入すると、指数部が11ビットに収まりません。 これをアンダーフローといいます。

たとえば、double型の変数に正の数の下限の10分の1の数を代入する次のプログラムについて考えてみます(プログラム6)。

#include <stdio.h>

int main(void) {
  double d = 0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000494065645841246544;
  printf("d = %e\n", d);
  return 0;
}

画面に収まりませんが、プログラムを選択してコピーすれば実際の値を取得できます。

これをコンパイルしてもオーバーフローのときのような警告は出ませんが、実行すると次のようになります。

luna% a.out
d = 0.000000e+00

オーバーフローと同じように、プログラム実行時にアンダーフローが発生しても、プログラムは停止しません。

演習4

アンダーフローが生じるプログラムを作成し、実行結果を確認せよ。

まとめ

変数は、コンピューターに記憶されるデータに名前をつけたものです。

変数にはがあり、整数を記憶する変数はint型、小数を記憶する変数はdouble型です。

変数は、使う前に宣言しなければなりません。 変数に値を記憶させることを代入といいます。 また、変数を宣言するときに値を代入することを初期化といいます。

変数の値を出力するには、printf命令の文字列の中に変換指定子を埋め込み、出力する変数を指定します。 int型の値を出力するときの変換指定子は %d、double型の値を出力するときの変換指定子は %f です。

変数に型が異なる値を代入すると、自動的に型が変換されます。 int型の値をdouble型に代入しても値は変わりませんが、double型の値をint型に代入すると小数点以下が切り捨てられます。 これを暗黙的型変換といいます。

課題・練習問題

2A-1 変数宣言、代入、出力(難易度♠)

int型の変数 x を宣言し、3 を代入し、その値を出力するプログラムを作成せよ。

2A-2 変数宣言、代入、出力(難易度♠)

double型の変数 x を宣言し、3.14 を代入し、その値を出力するプログラムを作成せよ。

2A-3 スワップ(難易度♠♠)

次の空欄部分を補って、変数 x と変数 y の値を入れ替えるプログラムを作成せよ。

int main(void) {
  int x = 3, y = 5;



  printf("x = %d, y = %d\n", x, y);
  return 0;
}
トップ   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS