高度なC言語:メモリ内のデータストレージの詳細な分析

コンテンツ

1.データ型の概要

1.1タイプの基本的な分類

2.メモリ内の形状ストレージ

2.1元のコード、逆コード、補完コード

2.2ビッグエンディアンとリトルエンディアンの概要

2.3演習

3.メモリ内の浮動小数点ストレージ

3.1浮動小数点ストレージの例: 

3.2浮動小数点ストレージルール


1.データ型の概要

基本的な組み込みタイプと、それらが使用するストレージの量について学習しました

char //文字データ型、1バイトを占める

短い//2バイトを占める短い整数

int // shape、4バイトを占める

long //長整数、4バイトを占める

long long //より長い整数、8バイトを占める

float //単精度浮動小数点数、4バイトを占める

double //倍精度浮動小数点数、8バイトを占める

タイプの意味:

1.このタイプを使用して、メモリスペースのサイズを開きます(サイズによって使用範囲が決まります)。

2.記憶空間の視点を見る方法。(たとえば、intとfloatはどちらも4バイトで、一方は整数で、もう一方は10進数です)

1.1タイプの基本的な分類

整数ファミリ:

char

        unsigned char

        署名された文字

短い

        unsigned short [int]、[int]は省略できます

        符号付きの短い[int]、[int]は省略できます

int

        unsigned int

        署名されたint

長さ

        unsigned long [int]、[int]は省略できます

        signed long [int]、[int]は省略できます

int mian()
{
	char c = 'w'; //char到底是signed char 还是unsigned char是不确定的,取决于编译器的实现
	signed char c2 = 't';

	short int a = 10; //short短整型,int可以省略
	short b = 20; //short是signed short

	signed short c = 30;
	unsigned short d = 40;

	return 0;
}

 浮動小数点ファミリ:

浮く

ダブル

構築型(カスタム型): 

>配列型

>構造型構造体

>列挙型列挙型

>共用体型共用体

ポインタタイプ:

int * pi;

char * pc;

float * pf;

void * pv; 

空のタイプ: 

voidは空のタイプ(タイプなし)を意味します

通常、関数の戻り型(戻り型なし)、関数のパラメーター(パラメーターなし)、ポインター型(null型ポインター)に適用されます。

2.メモリ内の形状ストレージ

変数の作成はメモリ内のスペースを開くことであり、スペースのサイズはさまざまなタイプに応じて決定されます。

例えば:

int a = 3;
int b = -1;
int c = 0x11223344;

a、b、cはメモリ内にあります:

メモリに保存されているデータはバイナリです

VSがメモリを表示する場合、表示の便宜のために16進データを表示します.1桁の16進数は4桁の2進数を表し、2桁の16進数は8桁の2進数、つまり1バイトです(aの場合、03 00 00 00は4バイトを意味します)

2.1元のコード、逆コード、補完コード

コンピュータの整数の表現方法には、元のコード、逆数コード、補数コードの3つがあります

3つの表現方法には、符号ビットと値ビットの2つの部分があります。符号ビットは0を使用して「正」を表し、1を使用して「負」を表します。値ビットの負の整数の3つの表現方法は異なります。 

元のコード

これは、2進数を正と負の数の形式で2進数に直接変換することによって取得できます。

1の補数

元のコードの符号ビットは変更しないでください。他のビットは、他のビットを順番に反転することで取得できます。 

補体

補数+1を取得して補数を取得します。(元のコードを渡すこともできます。補数コードの符号ビットは変更されず、値ビットがビットごとに反転され、+ 1が取得されます)

整数の場合、符号付きの数値と符号なしの数値 に分けることができます

符号付き数値:符号ビット+値ビット

正の数:0+数字

負の数:1+数字

//原码 - 有符号数,直接根据正负数值给出的二进制序列就是原码
//反码 - 原码的符号位不变,其他位按位取反
//补码 - 反码二进制的最低位+1得到

//正数的原码、反码、补码相同
int main()
{
	int a = 3;    //signed int a = 3;
	//00000000000000000000000000000011         - 原码
	//00000000000000000000000000000011         - 反码
	//0000 0000 0000 0000 0000 0000 0000 0011  - 补码
	//0    0    0    0    0    0    0    3     - 16进制(内存中)
	
	int b = -1;    //signed int b = -1;
	//10000000000000000000000000000001         - 原码
	//11111111111111111111111111111110         - 反码
	//1111 1111 1111 1111 1111 1111 1111 1111  - 补码
	//f    f    f    f    f    f    f    f     - 16进制(内存中)
   
	return 0;
}

 16進ビット012 3 4 5 6 7 8 9 abcdefの場合、fはバイナリ1111から取得でき、メモリ内の-1はff ffffffです。

結論は:

1.正の数の元の、逆の、および補数は同じです。

2.整数の場合:メモリに格納されているデータは、実際には補数コードを格納します。

なぜ?

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

例えば:

int c = 1 - 1;
//CPU只有加法器
//1 - 1 => 1 + (-1),此时用原码来进行计算,结果是错误的(-2)
//用补码来计算则可获得正确结果(符号位也参与计算,进位后丢掉即可)

符号なし数値の場合:正の整数と同じです。

char(8bit)の場合:

int c=0x11223344の場合;

メモリに「逆記憶」という現象がありますが、なぜですか?

2.2ビッグエンディアンとリトルエンディアンの概要

ビッグエンディアンリトルエンディアンとは:

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

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

int c = 0x11223344の場合;メモリ内:

表示:現在のコンパイラはリトルエンディアンのバイトオーダーストレージを使用しています

古典的なインタビューの質問:

ビッグエンディアンのバイトオーダーとリトルエンディアンのバイトオーダーの概念を簡単に説明し、現在のマシンのバイトオーダーを決定する小さなプログラムを設計してください。(10点)

//写一个代码,判断当前机器使用的大端还是小端

//方法1
int check_sys()
{
	int a = 1;
	return (*(char*)&a);
}
int main()
{
	int a = 1;
	int ret = check_sys();
	if (ret == 1)
	{
		printf("小端\n");
	}
	else
	{
		printf("大端\n");
	}
	return 0;
}

//方法2
int main()
{
	int a = 1;
	char* p = (char*)&a;//对p解引用则访问一个字节 
	if (*p == 1)
	{
		printf("小端");
	}
	else
	{
		printf("大端");
	}
	//
	//0x 00 00 00 01
	//
	//低       高
	//小端  
	//01 00 00 00
	//大端
	//00 00 00 01

	//只需要看第一个字节是00还是01即可判断
	return 0;
}

2.3練習用の質問

次のコードの出力は何ですか?

//1.
//输出什么?
#include <stdio.h>
int main()
{
	char a = -1;
	//大部分编译器基本上都是signed char
	//11111111 - a
	//11111111111111111111111111111111 - 整形提升后(此时是补码)
	//10000000000000000000000000000001 - 原码  -1
	signed char b = -1;
	//11111111 - b 
	//计算过程和a一样 -1
	unsigned char c = -1;
	//11111111 - c
	//00000000000000000000000011111111 - 正数的原反补码都相同
	//十进制 : 255

	printf("a=%d,b=%d,c=%d", a, b, c);//a = -1, b = -1, c = 255
	//打印%d时会发生整形提升(按照符号位提升)
	return 0;
}
//2.
#include <stdio.h>
int main()
{
	char a = -128;
	//10000000000000000000000010000000 - -128原码
	//11111111111111111111111110000000 - -128补码
	//1000000                          - 存到a中
	//整形提升后:
	//11111111111111111111111110000000 - 4294967168 (2^32 - 127 - 1)
	
    //%u : 打印无符号整形
	printf("a = %u\n", a); //a = 4294967168
	return 0;
}
//3.
#include <stdio.h>
int main()
{
	char a = 128;
	//00000000000000000000000010000000 - 128原码
	//10000000                         - 存到a中
	//整形提升后
	//11111111111111111111111110000000 - 4294967168
	printf("a = %u\n", a); // a = 4294967168
	return 0;
}
//4.
int main()
{
	int i = -20;
	//10000000000000000000000000010100 - -20原码
	//11111111111111111111111111101100 - -20补码
	unsigned  int  j = 10;
	//00000000000000000000000000001010 -  10原(补)码
	
	//11111111111111111111111111110110 - i + j 补码
	//10000000000000000000000000001010 - i + j 原码: -10
	printf("i+j = %d\n", i + j); // i+j = -10
	//按照补码的形式进行运算,最后格式化成为有符号整数

    return 0;
}
//5.
int main()
{
	unsigned int i;//因为i定义的是无符号类型 则始终有:i >= 0; 

	for(i = 9; i >= 0; i--)
	{
		printf("%u\n", i); //死循环
		//当i = -1存入内存中(之后以此类推):
		//11111111111111111111111111111111 - 当做无符号数处理 - 一个巨大的正数
	}
	return 0;
}
//6.
int main()
{
	char a[1000];
	int i;
	for (i = 0; i < 1000; i++)
	{
		a[i] = -1 - i;
	}
	//a[i]从-1 -2 ... -128 共有128个数字
	//对于char来说 负数最多到 : -128
	//当存入-129时,对于char而言是放不下的
	//10000000000000000000000010000001 - -129原码
	//11111111111111111111111101111111 - -129补码
	//01111111                         - -129存入char中 - 127
	//以此类推,存入-130时,内存中实际存储的是126
	//127 126 ... 3 2 1 0,0之前共有127个数字(因为strlen遇到0就终止了)
	//127 + 128 = 255
	printf("%d", strlen(a));//255
	return 0;
}
//7.
#include <stdio.h>
unsigned char i = 0;
//对于无符号的char 取值范围是[0 , 255]
//对于下面代码,i <= 255恒成立,故死循环了
int main()
{
	for (i = 0; i <= 255; i++)
	{
		printf("hello world\n");//死循环
	}
	return 0;
}

 注:signed charの場合は、次の図を覚えておいてください(unsigned char range:[0、255]):

3.メモリ内の浮動小数点ストレージ

一般的な浮動小数点数:

3.14159

1E10(科学的記数法、1.0 * 10 ^ 10)

浮動小数点ファミリには、float、double、longdouble型が含まれます。

浮動小数点数で表される範囲:float.hで定義(整数ファミリー:値の範囲の定義-> Limits.h)

3.1浮動小数点ストレージの例: 

int main()
{
	int n = 9;
	float* pFloat = (float*)&n; //把int*类型指针强制转换成float*类型
	printf("n的值为:%d\n", n); //9
	printf("*pFloat的值为:%f\n", *pFloat);//0.000000
	*pFloat = 9.0;
	printf("n的值为:%d\n", n); //1091567616
	printf("*pFloat的值为:%f\n", *pFloat); //9.000000
	
	return 0;
}

説明:浮動小数点数の格納は整数の格納とは異なります。では、浮動小数点数はどのように格納されますか?

3.2浮動小数点ストレージルール

国際規格IEEE(Institute of Electrical and Electronics Engineering)754によると、任意の2進浮動小数点数Vは次の形式で表すことができます。

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

(-1)^ sは符号ビットを表し、s = 0の場合、Vは正であり、s = 1の場合、Vは負です。

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

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

5.5の場合:

IEEE 754では、次のように規定されています。32ビット浮動小数点数の場合、最上位1ビットは符号ビットs、次の8ビットは指数E、残りの23ビットは仮数Mです。

例えば:

int main()
{
	float f = 5.5f;//如果不写f,则默认是double类型
	//101.1
	//1.011 * 2^2
	//(-1)^0 * 1.011 * 2^2
	//S = 0,E =10000001 (2 + 127),M = 011(后面再补20个0)
	//0100 0000 1011 0000 0000 0000 0000 0000 - 内存中存储的二进制
	//4    0    11   0    0    0    0    0    
	//40 B0 00 00 - 十六进制

	return 0;
}

メモリ内のf=5.5f:

 64ビット浮動小数点数の場合、最上位1ビットは符号ビットS、次の11ビットは指数E、残りの52ビットは仮数Mです。

 

IEEE 754には、仮数Mと指数Eに関する特別な規定もあります。

前述のように、1≤M<2、つまりMは1.xxxxxxの形式で記述できます。ここで、xxxxxxは小数部分を表します。IEEE 754では、Mがコンピュータに格納されている場合、この番号の最初の桁はデフォルトで常に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は負の数になる可能性があるため、IEEE754では、メモリに格納するときに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.5(1/2)の2進形式は0.1です。正の部分は1でなければならない、つまり小数点が1だけ右にシフトされると規定されているため、1.0 * 2 ^( -1)であり、その順序コードは-1 + 127 = 126は01111110として表され、仮数1.0は整数部分を0として削除し、0〜23ビット00000000000000000000000を入力すると、そのバイナリ表現は次のようになります。

0 01111110 00000000000000000000000

Eはすべて0です:

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

有効数字Mは最初の1追加されなくなりましたが、0.xxxxxxの10進数に削減されます。これは、±0、および0に近い非常に小さい数値を表すために行われます

例えば:

    //0 00000000 01000100101000000000000
	//E+127存入数据后是00000000
	//真实的E = -127
	//(-1)^0 * 1.01000100101 * 2^(-127) - 无限接近于0的数字
	
	//所以对于接近0的数字:
	//M拿出来不+1,E = -126(32位)
	//(-1)^0 * 0.01000100101 * 2^(-126) - 真实取出时的数字,也无限接近于0

Eはすべて1です

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

	//E为全1
	//E + 128 = 255
	//E = 127
	//(+ -) * 1.xxxxxx * 2 ^ 128 - 趋近于正负无限大

浮動小数点数の表現規則については、ここで説明します。

上記の基礎を使用して:最初の質問に戻りましょう:0x00000009が浮動小数点数に復元されるのはなぜですか?0.000000になりますか?

int main()
{
	int n = 9;
	//00000000000000000000000000001001 - 二进制

	float* pFloat = (float*)&n; //把int*类型指针强制转换成float*类型
	printf("n的值为:%d\n", n); //9
	
	printf("*pFloat的值为:%f\n", *pFloat);//0.000000
	//*pFloat - 以浮点数的视角去访问n的四个字节,就会认为n的4个字节中放的是浮点数
	//0 00000000 00000000000000000001001 (E全0的情况)
	//(-1)^0 * 2 ^ (-126) * 0.00000000000000000001001
	//0.000000
	
	*pFloat = 9.0;
	//*pFloat - 以浮点数的视角观察n的4个字节
	//以浮点数的形式存储9.0
	//1001.0 - 二进制
	//1.001 * 2^3 - 科学计数法
	//(-1)^0 * 1.001 * 2^3
	//S = 0,E = 130(3 + 127). M = 00100000000000000000000 
	//0 10000010 00100000000000000000000 - 内存中存储形式
	//1091567616 - 二进制
	printf("n的值为:%d\n", n); //1091567616
	printf("*pFloat的值为:%f\n", *pFloat); //9.000000

	return 0;
}

おすすめ

転載: blog.csdn.net/m0_62934529/article/details/123702995