赣州大余做网站建设,新浪云 安装wordpress,高校门户网站建设问题,有了域名和主机怎么做网站☃️内容专栏#xff1a;【C语言】进阶部分 ☃️本文概括#xff1a; C语言中的数据类型及其存储方式。 ☃️本文作者#xff1a;花香碟自来_ ☃️发布时间#xff1a;2023.2.24 目录 一、数据类型详细介绍
1.1 基本的数据类型
1.2 整型家族
1.3 构造类型
1.4 指针类型… ☃️内容专栏【C语言】进阶部分 ☃️本文概括 C语言中的数据类型及其存储方式。 ☃️本文作者花香碟自来_ ☃️发布时间2023.2.24 目录 一、数据类型详细介绍
1.1 基本的数据类型
1.2 整型家族
1.3 构造类型
1.4 指针类型
1.5 空类型
二、整型在内存中的存储
2.1 原码、反码、补码
三、大小端字节序
3.1概念
3.2 笔试题
四、 练习——内存存储
4.1.
4.2.补充char类型存放数值的范围
1. signed char 范围划分 2. unsigned char 范围划分 4.3
五、浮点型在内存中的存储
5.1浮点数存储的规则
5.2浮点数存储的例子 一、数据类型详细介绍
1.1 基本的数据类型
以下为C语言的几种常见类型
char 字符数据类型short短整型int整型long长整型long long更长的整型float 单精度浮点数 double双精度浮点数
1.2 整型家族
char存储的是字符但本质存储和表示的是其字符所对应的ASCII码值ASCII码值属于整数所以char也属于整数。
数据类型有符号无符号charunsigned charsigned charshortunsigned shortintsigned shortintintunsigned intsigned intlongunsigned longint signed long int 我们知道对于数值如“10”、“-10”数值有正负数值是有符号的比如说温度是有正数也是有负数的我们可以用unsigned int 来存储数值比如说年龄没有负数的概念我们就可以用signed int来存储。 我们平时默认写的short 、int 、long前面其实省略了signed这个signed可添加可不添加表示的都是有符号。需要注意的是对于char类型来说不一定省略的都是signed需要根据编译器确定一般情况下属于signed的。 1.3 构造类型
也称自定义类型
数组类型int [],char [],float []……结构体类型由struct关键字构造的结构体枚举类型enum联合类型union
1.4 指针类型
整型指针int *字符指针char *浮点数指针float * / double *空类型指针void *
1.5 空类型
void 表示空类型无类型通常应用于函数的返回类型、函数的参数、指针类型。 二、整型在内存中的存储
我们在初阶部分就已经知道变量的创建是需要在内存中开辟空间的。空间的大小是根据不同的类型来决定的。内存中存放的数据是以二进制的形式表示。
那么整数在内存中是如何存储的我们就需要引入原码、反码、补码的概念
2.1 原码、反码、补码 计算机中的整数有三种2进制表示方法即原码、反码和补码。 三种表示方法均有符号位和数值位两部分符号位最高位都是用0表示“正”用1表示“负”。其余位数均为数值位。 正数的原、反、补码都相同。负整数的三种表示方法各不相同。 原码直接将数值按照正负数的形式翻译成二进制就可以得到原码。反码将原码的符号位不变其他位依次按位取反就可以得到反码补码反码1就得到补码
#includestdio.h
int main()
{int a 10;// 0000 0000 0000 0000 0000 0000 0000 1010 原码// 0000 0000 0000 0000 0000 0000 0000 1010 反码// 0000 0000 0000 0000 0000 0000 0000 1010 补码int b -10;// 1000 0000 0000 0000 0000 0000 0000 1010 原码// 1111 1111 1111 1111 1111 1111 1111 0101 反码// 1111 1111 1111 1111 1111 1111 1111 0110 补码return 0;
}
那么整型存放在内存中是以什么码的形式进行存储的呢
正数并不好观察所以我们考虑观察一个负数我们取出变量b的地址观察是以小端字节序的形式显示什么是小端后面会讲到这里暂且理解为倒着存放即将补码化为十六进制后的数据ff ff ff f6与内存中的数据吻合所以我们有结论整数数据在内存中存放的是补码。 为什么呢 在计算机系统中数值一律用补码来表示和存储。原因在于使用补码可以将符号位和数值域统一处理 同时加法和减法也可以统一处理CPU只有加法器此外补码与原码相互转换其运算过程是相同的不需要额外的硬件电路。 三、大小端字节序 为何叫字节序是因为将内存的数据以字节为单位进行存储。 大端字节序Big-Endian将数据的低位字节存放在内存的高位地址高位字节存放在低位地址。这种排列方式与数据用字节表示时的书写顺序一致符合人类的阅读习惯。小端字节序Little-Endian将一个多位数的低位放在较小的地址处高位放在较大的地址处。小端字节序与人类的阅读习惯相反但更符合计算机读取内存的方式因为CPU读取内存中的数据时是从低地址向高地址方向进行读取的。3.1概念
简单理解
大端字节序存储模式是指数据的低位保存在内存的高地址处而数据的高位保存在内存的低地址处 小端字节序存储模式是指数据的低位保存在内存的低地址处而数据的高位保存在内存的高地址处。 为什么会有大端小端呢
为什么会有大小端模式之分呢这是因为在计算机系统中我们是以字节为单位的每个地址单元 都对应着一个字节一个字节为8 bit。但是在C语言中除了8 bit的char之外还有16 bit的short型32 bit的long型要看具体的编译器另外对于位数大于8位的处理器例如16位或者32位的处理器由于寄存器宽度大于一个字节那么必然存在着一个如何将多个字节安排的问题。因此就导致了大端存储模式和小端存储模式。 例如一个 16bit 的 short 型 x 在内存中的地址为 0x0010 x 的值为 0x1122 那么 0x11 为 高字节 0x22 为低字节。对于大端模式就将 0x11 放在低地址中即 0x0010 中 0x22 放在高 地址中即 0x0011 中。小端模式刚好相反。我们常用的 X86 结构是小端模式而 KEIL C51 则 为大端模式。很多的ARMDSP都为小端模式。有些ARM处理器还可以由硬件来选择是大端模式 还是小端模式。
3.2 笔试题
百度2015年系统工程师笔试题请简述大端字节序和小端字节序的概念设计一个小程序来判断当前机器的字节序。10分 如何设计一段代码来判断机器的大小端呢? 思路我们拿最简单的数字1进行判断数字1存放在内存中转换为十六进制位0x000001若在小端模式中则以“01 00 00 00”的形式进行存储若在大端模式中则以“00 00 00 01”的形式进行存储。通过观察我们会发现其区别是第一个字节为1还是0我们就可以确定机器是大端存储还是小端存储。于是我们可以想到通过指针来访问取地址访问的是四个字节那么什么类型的指针可以访问1个字节的数据呢当然是char*类型的指针将拿到的地址强制转换成char*。再通过指针解引用结果是1那么断定是小端结果是0就是大端了。 代码1
#includestdio.h
int main()
{int a 1;char* p (char*)a;if (*p 1){printf(小端\n);}else{printf(大端\n);}return 0;
}
优化代码封装成函数
#include stdio.h
//大端返回0
//小端返回1
int check_sys()
{int i 1;return (*(char*)i);//强制类型转换后解引用
}
int main()
{int ret check_sys();if (ret 1){printf(小端\n);}else{printf(大端\n);}return 0;
}
四、 练习——内存存储
4.1.
思考以下代码输出什么 #include stdio.h
int main()
{char a -1;//写出-1的二进制形式// 1000 0000 0000 0000 0000 0000 0000 0001 原码// 1111 1111 1111 1111 1111 1111 1111 1110 反码// 1111 1111 1111 1111 1111 1111 1111 1111 补码// 1111 1111 (截断)signed char b -1;// -1unsigned char c -1;// 1000 0000 0000 0000 0000 0000 0000 0001 原码// 1111 1111 1111 1111 1111 1111 1111 1110 反码// 1111 1111 1111 1111 1111 1111 1111 1111 补码// 1111 1111 截断因为char里面只能存放8个bit位printf(a%d,b%d,c%d, a, b, c);// -1 -1 255//打印%d的a%d为有符号整数编译器默认写char 是有符号的char// 按符号位填充// 1111 1111 整型提升为// 1111 1111 1111 1111 1111 1111 1111 1111补码// %d为有符号的数所以按内存的角度高位为1为负数// 所以需要转换为原码// 1111 1111 1111 1111 1111 1111 1111 1110 反码// 1000 0000 0000 0000 0000 0000 0000 0001 (原码)// a -1//打印b同理 分析过程和a一样//打印%d的c//将无符号的char c打印首先进行整型提升//无符号的char高位补充0// 1111 1111 整型提升为// 0000 0000 0000 0000 0000 0000 1111 1111补码// %d为有符号的数所以按内存的角度高位为0为正数//正数的原码反码补码相同// c 255return 0;
}
打印代码观察 4.2.补充char类型存放数值的范围 char类型数值的存储范围是按照有符号和无符号区分的。char类型的大小为1个字节1个字节为8个bit位。 1. signed char 范围划分
推断如下 需要注意的是内存中 10000000 这个补码表示的是原码十进制-128这个值 我们不妨先将 -128用二进制形式表示为110000000原码101111111反码110000000补码但是存放在char类型空间里只有8bit的空间也就是蓝色底纹的部分剩余的部分将会被丢弃。故以上的 10000000 会被直接翻译成128 2. unsigned char 范围划分
推断如下 对于unsigned来说就没有符号位的概念了存在内存中的补码也是原码数值均为正数所以unsigned的取值范围是0~255 4.3
下列程序输出什么 //2.
#include stdio.h
int main()
{char a -128;//将-128转换成二进制 // 1000 0000 0000 0000 0000 0000 1000 0000 (-128的原码)// 1111 1111 1111 1111 1111 1111 0111 1111 (反码)// 1111 1111 1111 1111 1111 1111 1000 0000 补码// 1000 0000截断printf(%u\n, a);//整型提升// char为signed 高位按符号位补充// 1111 1111 1111 1111 1111 1111 1000 0000//%u打印无符号的数 站在内存的角度会视为无符号的数//所以为正数直接打印//计算得到4,294,967,168return 0;
} //3.
#include stdio.h
int main()
{char a 128;//将128转换成二进制 // 0000 0000 0000 0000 0000 0000 1000 0000 (128的原码)// 1000 0000截断printf(%u\n, a);//整型提升// char为signed 高位按符号位补充// 1111 1111 1111 1111 1111 1111 1000 0000//%u打印无符号的数 站在内存的角度会视为无符号的数//所以为正数直接打印//计算还是得到4,294,967,168return 0;
} //4.
#includestdio.h
int main()
{int i -20;// -20转换为二进制// 1000 0000 0000 0000 0000 0000 0001 0100 (原码)// 1111 1111 1111 1111 1111 1111 1110 1011 (反码)// 1111 1111 1111 1111 1111 1111 1110 1100 (补码)unsigned int j 10;// 1000 0000 0000 0000 0000 0000 0000 1010 补码//ij// 1111 1111 1111 1111 1111 1111 1111 0110(补码)//按照补码的形式进行运算最后格式化成为有符号整数// 1111 1111 1111 1111 1111 1111 1111 0101反码// 1000 0000 0000 0000 0000 0000 0000 1010 (原码) - -10printf(%d\n, i j); //发生了算术转换但并不影响值的计算return 0;
} 该代码的结果是个死循环因为i为unsigned的int类型循环条件大于等于0是恒成立的。 解释当i -1的时候将-1转换为二进制1000 0000 0000 0000 0000 0000 0000 0001 化为反码为1111 1111 1111 1111 1111 1111 1111 1110 化为补码为1111 1111 1111 1111 1111 1111 1111 1111 以%u的形式打印直接将补码打印结果就为4,294,967,295 //6.
#includestdio.h
#includestring.h
int main()
{char a[1000];int i;for (i 0; i 1000; i){a[i] -1 - i;}//char a[1000]里面的值存放的是 -1 -2 -3 …… -999 -1000 //我们知道signed char类型的取值范围是 -128~127//具体怎么运算的呢我们可以观察代码段以下图形////其实是一个轮回的过程// -129不是真正意义上的-129了而是站在char内存的角度截断计算为127了//我们就知道 -1 -2 -3 … -127 -128 127 126 … 2 1 0 -1printf(%d, strlen(a));// 128 127 255//求字符串长度 找到\0(0)即为字符串的结束标志return 0;
} 该代码的运行结果是一个死循环unsigned char 的范围是 0 ~ 255当i得到i 256的时候化为二进制序列为1000 0000 0000 0000 0000 0001 0000 0000但是存放在char里面的时候存储的是0000 0000高位丢掉了。所以循环条件恒小于等于255。 五、浮点型在内存中的存储
常见的浮点数 3.14159 1E10科学计数法 浮点数家族包括 float、double、long double 类型。 浮点数表示的范围float.h中定义
5.1浮点数存储的规则
将一个十进制的浮点数转换为一个二进制浮点数的规则如下
根据国际标准IEEE 754 电气和电子工程协会规定任意一个二进制浮点数V可以表示成下面的形式
V (-1)^S * M * 2^E
(-1)^S表示符号位当S0V为正数当S1V为负数。
M表示有效数字 1≤M2。
2^E表示指数位。 举例来说 十进制的5.0写成二进制是 101.0 相当于 1.01×2^2 。 那么按照上面V的格式可以得出S0M1.01E2。 十进制的-5.0写成二进制是 -101.0 相当于 -1.01×2^2 。那么S1M1.01E2。 IEEE 754规定
对于32位的浮点数来说最高的1位是符号位s接着的8位是指数E剩下的23位为有效数字M。 对于64位的浮点数来说最高的1位是符号位S接着的11位是指数E剩下的52位为有效数字M。
对于有效数字M前面说过 1≤M2 也就是说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是可以出现负数的比如说0.5所以IEEE 754规定存入内存时E的真实值必须再加上一个中间数对于8位的E这个中间数是127对于11位的E这个中间 数是1023。比如2^10的E是10所以保存成32位浮点数时必须保存成10127137即 10001001。
然后指数E从内存中取出还可以分成三种情况
第一种E不全为0或不全为1 这时浮点数就采用下面的规则表示即指数E的计算值减去127或1023得到真实值再将有效数字M前加上第一位的1。 比如 0.51/2的二进制形式为0.1由于规定正数部分必须为1即将小数点右移1位则为 1.0*2^(-1)其阶码为-1127126表示为01111110而尾数1.0去掉整数部分为0补齐0到23位00000000000000000000000则其二进制表示形式为:0 01111110 00000000000000000000000 第二种E为全0 这时浮点数的指数E等于1-127或者1-1023即为真实值 有效数字M不再加上第一位的1而是还原为0.xxxxxx的小数。这样做是为了表示±0以及接近于 0 的很小的数字。 第三种E为全1 这时如果有效数字M全为0表示±无穷大正负取决于符号位s 5.2浮点数存储的例子
观察以下代码
int main()
{int n 9;float *pFloat (float *)n;printf(n的值为%d\n,n); //code1printf(*pFloat的值为%f\n,*pFloat); //code2*pFloat 9.0;printf(num的值为%d\n,n); //code3printf(*pFloat的值为%f\n,*pFloat); //code4return 0;
}
将代码打印得到以下结果 n的值为9 //code1 *pFloat的值为0.000000 //code2 num的值为1091567616 //code3 *pFloat的值为9.000000 //code4 code1与code2我们能够表面地看出来但是对于code2 和code3就需要对浮点数进行内存存储的计算了。
对于code2为什么 0x00000009 还原成浮点数就成了 0.000000 首先将 0x00000009 拆分得到第一位符号位s0后面8位的指数 E00000000 最后23位的有效数字M000 0000 0000 0000 0000 1001。 9 - 0000 0000 0000 0000 0000 0000 0000 1001 由于指数E全为0所以符合上一节的第二种情况。因此浮点数V就写成 V(-1)^0 × 0.00000000000000000001001×2^(-126)1.001×2^(-146)
显然V是一个很小的接近于0的正数所以用十进制小数表示就是0.000000。 再次观察code3
问浮点数9.0如何用二进制表示还原成十进制又是多少 首先浮点数9.0等于二进制的1001.0即1.001×2^3。 9.0 - 1001.0 -(-1)^0*1.001*2^3 - s0, M1.001,E3127130 那么第一位的符号位s0有效数字M等于001后面再加20个0凑满23位指数E等于3127130 即10000010。 所以写成二进制形式应该是SEM即 0 10000010 001 0000 0000 0000 0000 0000 将其二进制换算成十进制打印就是 1091567616 ❤ 好啦数据的存储内容到此为止创作不易还请多多三连支持我哦~