目录
数据类型的意义
大小端介绍
例题1:设计一个小程序输出存储方式:
例题2:下列程序输出什么,为什么
例题3:下列程序输出什么,为什么
例题4:下列程序输出什么,为什么
例题6:下列程序输出什么,为什么
例题7:下列程序输出什么,为什么
例题8:下列程序输出什么,为什么
浮点型在内存中的存储
数据类型的意义
类型的意义
1、使用这个类型开辟内每空间的大小(大小决定了使用范围)。
2、如何看待内存空间的视角。
案例:看a在内存是怎么存放的
int main()
{
int a = -10;
//原码:10000000 00000000 00000000 00001010
//反码:11111111 11111111 11111111 11110101
//补码:11111111 11111111 11111111 11110110
//换成16进制:FF FF FF F6
return 0;
}
//数据在内存中以2进制的形式存储
//对整数来说:
//整数二进制有3种表示形式:原码、反码、补码
//正整数:原码、反码、补码相同
//负整数:原码、反码、补码进行计算
//按照数据的数值直接写出的二进制序列就是原码
//原码的符号位不变,其他位按位取反,得到的就是反码
//反码+1,得到的就是补码
//内存存储的是补码
F10调试,查看内存的信息:
大小端介绍
什么大端小端:
- 大端(存储)模式,是指数据的低位保存在内存的高地址中,而数据的高位,保存在内存的低地址中;
- 小端(存储)模式,是指数据的低位保存在内存的低地址中,而数据的高位,,保存在内存的高地址中。
为什么有大端和小端:
- 这是因为在计算机系统中,我们是以字节为单位的,每个地址单元都对应着一个字节,一个字节为8bit。但是在C语言中除了8bit的char之外,还有16bit的short型,32bit的long型(要看具体的编译器),另外,对于位数大于8位的处理器,例如16位或者32位的处理器,由于寄存器宽度大于一个字节,那么必然存在着一个如果将多个字节安排的问题。因此就导致了大端存储模式和小端存储模式。
- 例如一个16bit的short型x,在内存中的地址为0x0010,的值为0x1122,那么0x11为高字节,0x22为低字节。对于大端模式,就将0x11放在低地址中,即0x0010中,0x22放在高地址中,即0x0011中。小端模式,刚好相反。我们常用的x86 结构是小端模式,而KEIL C51则为大端模式。很多的ARM,DSP都为小端模式。有些ARM处理器还可以由硬件来选择是大端模式还是小端模式。
例题1:设计一个小程序输出存储方式:
int main()
{
int a = 1;
char* p = (char*)&a;
if(*p == 1)
{
printf("小端存储\n");
}
else
printf("大端存储\n");
return 0;
}
或者写一个函数输出存储方式:
int check_sys()
{
int i = 1;
char* p = (char*)&i;
return *p;
}
int main()
{
int ret = check_sys();
if(ret == 1)
{
printf("小端存储\n");
}
else
printf("大端存储\n");
return 0;
}
例题2:下列程序输出什么,为什么
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;
}
原因为下列:
int main()
{
char a =-1;
//补码位:11111111 11111111 111111111 11111111
//因为为char,所以为:11111111
//因为默认为signed,高位第一个1为符号位,又因为要输出%d,所以要整形提升
//整形提升为:11111111 11111111 111111111 11111111(有符号的补符号位)
//所有原码为:-1
signed char b = -1;
//和char一样的,上面的额char是省略了signed
unsigned char c = -1;
//补码位:11111111 11111111 111111111 11111111
//因为为char,所以为:11111111
//因为为unsiged,所有整形提升前面补0
//整形提升为:00000000 00000000 000000000 11111111
//所以原码为:255
printf("a=%d,b=%d,c=%d",a,b,c);
return 0;
}
例题3:下列程序输出什么,为什么
int main()
{
char a =-128;
printf("%u\n",a);
return 0;
}
因为:
int main()
{
char a =-128;
//原码:10000000 00000000 00000000 10000000
//反码:11111111 11111111 11111111 01111111
//补码:11111111 11111111 11111111 10000000
//因为char,所以只能存放8个bit位:10000000
//因为是以%u(无符号整形)的方式打印的,需要整形提升
//又因为整形提升是用之前的char提升的为:11111111 11111111 11111111 10000000
//所以原码为:11111111 11111111 11111111 10000000为:4294967168
printf("%u\n",a);
return 0;
}
例题4:下列程序输出什么,为什么
int main()
{
char a = 128;
printf("%u\n",a);
return 0;
}
因为:
int main()
{
char a = 128;
//原码:00000000 00000000 00000000 10000000
//因为char,所以只能存放8个bit位:10000000
//因为是以%u(无符号整形)的方式打印的,需要整形提升
//又因为整形提升是用之前的char提升的为:11111111 11111111 11111111 10000000
//所以原码为:11111111 11111111 11111111 10000000为:4294967168
printf("%u\n",a);
return 0;
}
//char类型的取值范围:-128到127
例题5:下列程序输出什么,为什么
int main()
{
int i = -20;
unsigned int j = 10;
printf("%d\n",i + j);
return 0;
}
int main()
{
int i = -20;
//原码:10000000 00000000 00000000 00010100
//反码:11111111 11111111 11111111 11101011
//补码:11111111 11111111 11111111 11101100
unsigned int j = 10;
//原反补码:00000000 00000000 00000000 00001010
printf("%d\n",i + j);
//i补码: 11111111 11111111 11111111 11101100
//j补码: 00000000 00000000 00000000 00001010
//相加得:11111111 11111111 11111111 11110110
//因为要%d,所以要以有符号位打印
//得反码:11111111 11111111 11111111 11110101
//得原码:10000000 00000000 00000000 00001010 -> -10
return 0;
}
例题6:下列程序输出什么,为什么
int main()
{
unsigned int i;
for (i = 9; i>= 0;i--)
{
printf("&u\n",i);
}
return 0;
}
//死循环
例题7:下列程序输出什么,为什么
#include<string.h>
int main()
{
char a[1000];
int i;
for (i =0;i<1000;i++)
{
a[i] = -1-i;
}
printf("%d\n", strlen(a));
return 0;
}
#include<string.h>
int main()
{
char a[1000];
int i;
for (i =0;i<1000;i++)
{
a[i] = -1-i;
}
//-1 -2 -3 ... -127 -128 127 126 123 ...3 2 1 0 -1 -2 ...
//128+127=255
//所以输出255
printf("%d\n", strlen(a));
return 0;
}
例题8:下列程序输出什么,为什么
unsigned char i = 0;
int main()
{
for(i = 0;i <= 255; i++)
{
printf("hello world\n");
}
return 0;
}
//输出死循环
浮点型在内存中的存储
根据国际标准IEEE(电气和电子工程协会)754,任意一个二进制浮点数V可以表示成下面的形式:
- (-1)^S*M*2^E
- (-1)^S表示符号位,当s=0,V为正数;当=1,V为负数。
- M表示有效数字,大于等于1,小于2。
- 2^E表示指数位
举例:
- 十进制的 5.0 ,写成二进制是 101.0 ,相当于 1.01×2^2 。
- 那么,按照上面 V 的格式,可以得出 s=0 , M=1.01 , E=2 。
- 十进制的 -5.0 ,写成二进制是 - 101.0 ,相当于 - 1.01×2^2 。
- 那么, s=1 , M=1.01 , E=2 。
对于32位的浮点数,最高的1位是符号位s,接着的8位是指数E,剩下的23位为有效数字M。
对于64位的浮点数,最高的1位是符号位S,接着的11位是指数E,剩下的52位为有效数字M
下面例子:
int main()
{
float f = 5.5f;
//101.1
//1.011 * 2^2
//s=0 M=1.011 E=2
//s=0 M=011 E=2+127
//0100 0000 1011 0000 0000 0000 0000 0000
//40 b0 00 00
return 0;
}
- IEEE 754 对有效数字 M 和指数 E ,还有一些特别规定。
- 前面说过, 1≤M
- IEEE 754 规定,在计算机内部保存 M 时,默认这个数的第一位总是 1 ,因此可以被舍去,只保存后面的xxxxxx部分。比如保存 1.01 的时候,只保存01 ,等到读取的时候,再把第一位的 1 加上去。这样做的目的,是节省 1 位有效数字。以 32 位浮点数为例,留给M 只有 23 位,将第一位的1 舍去以后,等于可以保存 24 位有效数字。
- 因为M都是1.XXX的形式,所以只存XXXX的形式,这样M就可以多存1位有效数字,能过提高精准度
- 至于指数 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 。
解释下下面代码输出什么
int main()
{
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;
}
输出:
n的值为:9
*pFloat的值为:0.000000
n的值为:1091567616
*pFloat的值为:9.000000
请按任意键继续. .
int main()
{
int n = 9;
float* pFloat = (float*)&n;
printf("n的值为:%d\n",n);
printf("*pFloat的值为:%f\n",*pFloat);
//9的二进制:00000000 00000000 00000000 00001001
//上面二进制第一位:S,第二位到第9位:E,余下位:M
//E全0所以打印出来就是0.0000000
*pFloat = 9.0;
printf("n的值为:%d\n",n);
printf("*pFloat的值为:%f\n",*pFloat);
//9.0:1001.0
//1.001*2^3
//E=3
// 0 10000010 001000000000000000
//0100 0001 0001 0000 0000 0000 0000 0000
//81 10 00 00
//