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


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

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

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

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ビット(仮数を1.xxxという形にして、その小数点以下の部分)

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]までです。

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

宣言

変数を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

複数の変数の値を出力することもできます(プログラム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型の変数に上限より[math]1[/math]だけ大きい数を代入する次のプログラムについて考えます(プログラム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の補数」という形で表現されています。

[math]2,147,483,648[/math]という数は、2進数で表すと、最初のビットだけが1で、その他のビットはすべて0の、32ビットの数になります。

10000000 00000000 00000000 00000000

これを32ビットint型として解釈すると、符号ビットが1で、残りが0となり、負の数の中で最も小さい数となります。 そのため、画面には次のように出力されます。

-2147483648

また、[math]4,294,967,296[/math]という数は、2進数で表すと、最初のビットだけが1で、その他のビットはすべての0の33ビットの数になります。

1 00000000 00000000 00000000 00000000

これを無理やり32ビットint型の変数に押し込むと、下位32ビットだけになってしまい、値がゼロになってしまいます。

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

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

課題・練習問題

2A-1(難易度♠♠)

変数 x と y を入れ替える命令文を、次のプログラムの空欄部分に追加せよ。

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



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