パソコン内のデータ保存 - 【C言語】

前回のブログではC言語のデータ型について学習しましたが、まずはC言語のデータ型について復習しておきましょう。


目次

C言語の基本的な組み込み型

タイプの基本的な分類

メモリ内の整数ストレージ

元コード、逆コード、補コード

ストレージ内のエンディアンネス

練習する 

 浮動小数点型のメモリへの保存

 浮動小数点数の格納規則

引用問題の詳細な分析 


C言語の基本的な組み込み型

char //文字データ型

short //短い整数データ型

int //整数    

long // 長整数

long long//long long 整数

float //単精度浮動小数点型

double // 倍精度浮動小数点型

int main(void)
{
	printf("%d\n", sizeof(char));
	printf("%d\n", sizeof(short));
	printf("%d\n", sizeof(int));
	printf("%d\n", sizeof(long));
	printf("%d\n", sizeof(long long));
	printf("%d\n", sizeof(float));
	printf("%d\n", sizeof(double));

	return 0;
}

 組み込み型の最大値と最小値についても一般的に理解できます。

これらの基本的な組み込みデータ型とサイズについてはすでに理解しましたが、次に C 言語でのデータ型を大まかに分類してみます。


タイプの基本的な分類

 整数ファミリー:

文字

        符号付き文字

        符号なし文字

短い

        符号付きショート [int]

        unsigned short [int]

整数

        符号付き整数

        符号なし整数

長さ

        符号付きロング [int]

        符号なしロング [int]

char について詳しく説明します char は、符号付き文字と符号なし文字である signed char と unsigned char に分けられます char のサイズは 8 ビットなので、バイナリ ビットは 8 ビットあります 符号付き char の場合、最上位ビットが Sign bit の場合、unsigned char の場合はすべてデジタル ビットです。したがって、signed char と unsigned char は異なる範囲を表し、結果は次のようになります。

 数値が表現できる範囲を超えた場合、数値は最初に戻りサイクルを継続します。

Int、short、long は上記の内容を参照できます。

浮動小数点ファミリー:

 浮く

ダブル

ポインターファミリー:

int *p;

char *p;

float *p;

ボイド *p;

……

 建設タイプ:

配列型: int arr[10]

構造体の種類: 構造体

ユニオンタイプ: ユニオン

列挙型: enum

空の型: void は空の型 (型なし) を意味します

通常は、関数の戻り値、関数のパラメータ、ポインタの型です。

以上が C 言語全体のデータ型の分類と整理でしたが、次は基礎的な内容を学んで、データについての知識と理解を深めていきましょう。


メモリ内の整数ストレージ

前回のブログで、変数は作成時にメモリ空間を空ける必要があり、その空間のサイズはデータの種類に関係すると書きました。

その整数変数はどのようにしてコンピュータに保存されるのでしょうか?

int a = 10 を作成すると、コンピューターはデータを保存するために 4 バイトのスペースを開き、それを 2 進数 (VS での表示は 16 進数)、つまり 32 と 0 の組み合わせで保存します。または1が格納されます。 

元コード、逆コード、補コード

コンピュータにおける整数のバイナリ表現方法には、原符号、補符号、補符号の 3 種類があります。3 つの表現方法にはいずれも、符号ビットと値ビットの 2 つの部分があり、符号ビットは「正」を表すために 0 を使用し、「負」を表すために 1 を使用します。負の整数を表す方法は 3 つあります。

元のコード: 元のコードは、値を正と負の数の形でバイナリに直接変換することによって取得できます。

逆コード: 元のコードの符号ビットを変更せずに、残りのコードをビットごとに反転します。

補数: 結果の補数に +1 を加算します。 

整数データの場合、コンピューターは通常、それを 2 の補数の形式で保存します。コンピューター システムでは、すべての値が 2 の補数で表され、保存されるためです。その理由は、補数コードを使用すると、符号ビットと値フィールドを均一に処理できると同時に、加算と減算も均一に処理できるためです(CPU には加算器しかありません)。補数コードと元のコードは同じではなく、追加のハードウェア回路が必要です。 

10と-10の元のコード、逆コード、補数コードを見てみましょう

#include<stdio.h>
int main(void)
{
    int a = 10;
   // 00000000000000000000000000001010-原码、反码、补码
    int b = -10;
   // 10000000000000000000000000001010—原码
   // 11111111111111111111111111110101-反码
   // 11111111111111111111111111110110-补码
    return 0;
}

コンピューター内のストレージと比較すると、ストレージの順序が少し間違っていることがわかりますが、これはなぜでしょうか。 

 


ストレージ内のエンディアンネス

ビッグエンディアンとスモールエンディアンとは何ですか?

ビッグエンディアン (ストレージ) モードは、データの下位ビットがメモリの上位アドレスに格納され、データの上位ビットがメモリの下位アドレスに格納されることを意味します。

リトル エンディアン (ストレージ) モードは、データの下位ビットがメモリの下位アドレスに格納され、データの上位ビットがメモリの上位アドレスに格納されることを意味します。 

なぜビッグエンディアンストレージとスモールエンディアンストレージを区別する必要があるのでしょうか?

これは、コンピュータ システムではバイトという単位が使用され、各アドレス単位が 1 バイトに対応し、1 バイトが 8 ビットであるためです。ただし、C 言語には 8 ビット char のほかに、16 ビットの short 型と 32 ビットの long 型があります (コンパイラによって異なります)。あるいは、32 ビットプロセッサの場合、レジスタ幅が 1 バイトよりも大きいため、複数のバイトをどのように配置するかという問題が発生するはずです。したがって、ビッグエンディアンストレージモードとリトルエンディアンストレージモードにつながります。たとえば、16 ビットの short 型 x の場合、メモリ内のアドレスは 0x0010、x の値は 0x1122、0x11 が上位バイト、0x22 が下位バイトになります。ビッグエンディアン モードの場合、下位アドレス (0x0010) に 0x11 を入力し、上位アドレス (0x0011) に 0x22 を入力します。リトル エンディアン モード、その逆です。一般的に使用される X86 構造はリトルエンディアン モードですが、KEIL C51 はビッグエンディアン モードです。多くの ARM と DSP はリトルエンディアン モードです。一部の ARM プロセッサでは、ハードウェアによってビッグ エンディアン モードまたはリトル エンディアン モードを選択することもできます。

コンピューターがビッグエンディアンであるかリトルエンディアンであるかをプログラムで区別するにはどうすればよいでしょうか?

ビッグ エンディアンとスモール エンディアンの違いを利用して問題を開始できます。ビッグ エンディアンとリトル エンディアンの格納方法は正反対です。固定値 1 を設定し、ポインタを使用してその位置にアクセスします。数値が 1 の場合はリトルエンディアン ストレージであり、それ以外の場合はビッグエンディアン ストレージです。(作成された変数は int です。現時点では、1 バイトの内容にアクセスし、必須の型変換を使用して変数のアドレスを (char *) に変更するだけで済みます)。理論が形成され、実践が始まります!

#include<stdio.h>
int main(void)

int check_sys(int a)
{
	return (*(char*)&a);
}

int main(void)
{
	int i = 1;
	if (check_sys(i))
	{
		printf("小端\n");
	}
	else
	{
		printf("大端\n");
	}
	return 0;
}

練習する 

 上記は整数ストレージの基本的な内容ですが、いくつかの演習を通じてこれを統合できます。

#include <stdio.h>
unsigned char i = 0;
int main()
{
    for(i = 0;i<=255;i++)
   {
        printf("hello world\n");
   }
    return 0;
}

まずこの質問を分析しましょう: unsigned char が表現できる範囲は (0~255) です。for ループでは、i の値が 255 を超えるとループから抜け出すことができますが、i が表現できる最大値は次のとおりです。 255. あと1つ足すと i の値が0に戻ってループを続けるので、このプログラムは無限ループになります。

#include <stdio.h>
int main()
{
    char a= -1;
    signed char b=-1;
    unsigned char c=-1;
    printf("a=%d,b=%d,c=%d",a,b,c);
    return 0;
}
//-1的原反补码
//10000000000000000000000000000001-原码
//11111111111111111111111111111110-反码
//11111111111111111111111111111111-补码

この問題は、char、signed char、unsigned char で表される数値の範囲を区別するものです。一般に、char は signed char を意味するため、a と b の出力値は同じである必要があり、どちらも -1 です。unsigned char の場合、最初に -1 の補数を 11111111 に切り捨てます。これは、出力形式が %d であるため、11111111 の符号なし整数は 00000000000000000000000011111111 に昇格され、出力結果は 255 になるため、最終的な出力結果は - になるはずです。 1、-1、255。

整数の格納については読み終えましたが、浮動小数点型をどのように格納すればよいでしょうか?


 浮動小数点型のメモリへの保存

浮動小数点数も C 言語で頻繁に使用されており、私たちの周りには 3.14、13.14 など、多くの浮動小数点数があります。上で述べたように、浮動小数点数ファミリーには float と double が含まれます。

浮動小数点数の範囲は #include<float.h> でクエリできます。

例を使用して、メモリ内の浮動小数点型の格納を紹介します。

#include<stdio.h>
int main(void)
{
	int n = 9;
	float* pFloat = (float*) & n;
	printf("n的值为:%d\n", n);
	printf("*pFloat的值为:%f\n", *pFloat);

	*pFloat = 9.0;
	printf("n的值为:%d\n", n);
	printf("*pFloat的值为:%f\n", *pFloat);

	return 0;
}

 出力結果は次のとおりです。

浮動小数点ポインタを使用して整数を逆参照すると、予想とは異なる値が読み取られます。逆に、浮動小数点数を使用してメモリからフェッチすると、期待した値が返されません。このことから、浮動小数点数と整数の格納と読み取りのルールは異なると結論付けることができます。浮動小数点数はどのようにメモリに格納すべきでしょうか?


 浮動小数点数の格納規則

国際標準 IEEE (電気電子工学研究所) 754 によれば、任意の 2 進浮動小数点数 V は次の形式で表現できます。

(-1)^S * M * 2^E

(-1)^S は符号ビットを表し、S=0 の場合、V は正の数、S=1 の場合、V は負の数になります。

M は、1 以上 2 未満の有効な数値を表します。

2^E は指数ビットを意味します。

例えば:

10 進数の 5.0 は 2 進数では 101.0 であり、1.01×2^2 に相当します。次に、上記の V の形式に従って、S=0、M=1.01、および E=2 と結論付けることができます。10 進数の -5.0 を 2 進数で書くと -101.0 となり、-1.01×2^2 に相当します。すると、S=1、M=1.01、E=2となります。

IEEE 754 では、 32 ビット浮動小数点数の場合、最上位ビットは符号ビット S、次の 8 ビットは指数 E、残りの 23 ビットは有効数 M と規定されています。 

64 ビット浮動小数点数の場合、最上位ビットは符号ビット S、次の 11 ビットは指数 E、残りの 52 ビットは仮数 M です。 IEEE 754 には、実効数 M と指数 E に関していくつかの特別な規則があります。

 前述したように、1≤M<2、つまり M は 1.xxxxxx の形式で記述できます。xxxxxx は小数部を表します。IEEE 754 では、M をコンピュータ内に保存する場合、デフォルトではこの数値の最初の 1 桁が常に 1 であるため、これを破棄して、次の xxxxxx 部分のみを保存することが規定されています。たとえば、1.01 を保存する場合は、01 だけを保存し、読み込むときに最初の 1 を追加します。これを行う目的は、有効数字 1 桁を節約することです。32 ビット浮動小数点数を例にとると、M には 23 ビットしか残っておらず、最初の 1 が破棄されると、有効数字 24 桁を節約することになります。

インデックス E に関しては、状況はさらに複雑です。

 まず、E は符号なし整数 (unsigned int) であり、E が 8 ビットの場合、値の範囲は 0 ~ 255、E が 11 ビットの場合、値の範囲は 0 ~ 2047 であることを意味します。ただし、科学表記法の E には負の数が含まれる可能性があることがわかっているため、IEEE 754 では、メモリに格納するときに E の実数値に中間数を加算する必要があると規定しています。8 桁の E の場合、中間数は 127 です。 11 ビット E、この中間数は 1023 です。たとえば、2^10 の E は 10 であるため、32 ビット浮動小数点数として保存する場合は、10+127=137 (つまり 10001001) として保存する必要があります。 

E がメモリから取り出される状況は 3 つあります。

E はすべて 0 または 1 ではありません。

このとき、浮動小数点数は次の規則で表されます。つまり、指数 E の計算値から 127 (または 1023) を引いて実数値を求め、その前に最初の桁の 1 を追加します。有効数M。

例: 0.25 (1/4) の 2 進形式は 0.01 です。正の部分は 1 でなければならないため、つまり小数点が 2 ビット右に移動されるため、1.0*2^(-2) となります。その順序コードは -2+127= 125 で、01111101 と表現されます。仮数 1.0 は整数部分を削除して 0 にし、0 ~ 23 桁を埋めます 00000000000000000000000、バイナリ表現は次のようになります: 0011111010000000000000000000 000

E はすべて 0:

このとき、浮動小数点数の指数 E は実数値である 1 ~ 127 (または 1 ~ 1023) となります。

有効数 M は最初の 1 を加算せず、10 進数の 0.xxxxxx に戻します。これは、±0、および 0 に近い非常に小さな数値を表すために行われます。

E はすべて 1 です。 

このとき、有効数Mがすべて0であれば±無限大を意味します(正負は符号ビットsにより異なります)


引用問題の詳細な分析 

ここで、前の例に戻って分析してみましょう。

2 進数値 9 は 0000000000000000000000000001001 ですが、なぜこれが 9 尾部の浮動小数点数 0.000000 に変換されるのでしょうか?  指数 E はすべて 0 であるため、前のセクションの 2 番目のケースに当てはまります。したがって、浮動小数点数 V は次のように書かれます: V=(-1)^0 × 0.00000000000000000001001×2^(-126)=1.001×2^(-146) 明らかに、V は 0 に近い小さな正の数です。 10 進数表記では 0.000000 です。

浮動小数点数を整数に変換するときに発生する問題を分析してみましょう。 

 結果として得られる 10 進整数がプログラムの結果です。

以上がパソコン内のデータの全内容となりますが、この記事を通じて少しでも何かを得られれば幸いです。

おすすめ

転載: blog.csdn.net/m0_74755811/article/details/131613077