- 数据类型:
char(字符型)
short(短整型)
int (整型)
long (长整型)
long long(更长的整型)
float(单精度浮点型)
double(双精度浮点型)
- 类型的基本归类:
整型家族:
char (字符在存储时存储的是ASCII码值)
unsigned char signed char
(signed char 和char 并不一定相等,取决于编译器)
short
unsigned short signed short
int
unsigned int
signed int
long
unsigned long
signed long
浮点型家族:
float
double
构造类型:
数组类型
结构体类型
枚举类型
联合类型
指针类型:
int*p
char*p
float*p
void*p
空类型:
void表示空类型(无类型)
(函数的返回类型,函数参数,指针类型)
- 整型在内存中的存储:
整数有3种二进制表示方法,即原码、反码、补码。
三种表示方法都分为符号位和数值位两部分,第一位为符号位,剩余为数值位
符号位中,0表示正,1表示负
正数的原码、反码、补码都相同。
负数的原码、反码、补码则不同:
原码:直接将数值按照正负数形式转换为二进制
反码:原码的符号位不变,其他位依次按位取反
补码:反码+1
(将补码的符号位不变,其他位依次按位取反,并+1后得到原码)
对于整型来说,数据存放内存中其实存放的是补码(16进制存放)
然而在观察内存时,a和b在内存中的存储又与我们想象的不太一样
- 大小端
大端存储模式:数据的低位保存在内存的高地址中,而数据的高位保存在低地址中
小端存储模式:数据的低位保存在内存的低地址中,而数据的高位保存在高地址中
(上内存中的存储模式为小端存储,而我们所分析的是大端存储)
- 判断大小端:
设计一个函数用于判断大小端
int a=1;若为大端模式,则在内存中的存储应为0x 00 00 00 01
若为小端模式,则在内存中的存储应为0x 01 00 00 00
所以只需取出第一个字节的内容,看是否等于1,若为1则是小端存储,否则即为大端存储
#include<stdio.h>
//判断大小端
int check()
{
int a = 1;
//小端:0x 01 00 00 00
//大端:0x 00 00 00 01
return *((char*)&a);
}
int main()
{
int ret=check();
if (ret == 1)
printf("小端\n");
else
printf("大端\n");
}
- 练习1
以下程序输出什么
#include <stdio.h>
int main()
{
char a= -1;
//10000000000000000000000000000001-原码
//11111111111111111111111111111111-补码
//由于a为char类型,所以在内存中存储时会发生截断,故存储内容为
//11111111
//11111111111111111111111111111111-提升后的补码
//10000000000000000000000000000001-提升后的原码
//输出-1
signed char b=-1;
//10000000000000000000000000000001-原码
//11111111111111111111111111111111-补码
//由于b为signed char类型,所以在内存中存储时会发生截断,故存储内容为
//11111111
//11111111111111111111111111111111-提升后的补码
//10000000000000000000000000000001-提升后的原码
//输出-1
unsigned char c=-1;
//10000000000000000000000000000001-原码
//11111111111111111111111111111111-补码
//由于c为unsigned char类型,所以在内存中存储时会发生截断,故存储内容为
//11111111
//由于c为unsigned char类型,所以提升时直接在前面补0
//00000000000000000000000011111111-提升后的补码
//00000000000000000000000011111111-提升后的原码
//输出255
printf("a=%d,b=%d,c=%d",a,b,c);
//%d为打印有符号的整数,而a,b,c都为char类型,所以需要提升
//%u为打印有符号的整数
return 0;
}
- 练习2
以下程序输出什么
#include<stdio.h>
int main()
{
char a[1000];
int i;
for(i=0; i<1000; i++)
{
a[i] = -1-i;
//-1 -2 -3...-128 127 126...3 2 1 0 -1..
}
printf("%d",strlen(a));
//strlen遇'\0'停止,即计算0之前的字符串长度
//128+127=255
return 0;
}
- 浮点型在内存中的存储
根据国际标准IEEE754,任意一个二进制浮点数V都可以表示成:
(-1)^S*M*2^E
(-1)^S表示符号位,当S=0,V为整数;当S=1,V为负数。
M表示有效数字,大于等于1,小于2。
2^E表示指数位
例如:
5.0=101.0=1.01*2^2
S=0,M=1.01,E=2
IEEE 754规定:
对于32位的浮点数,最高的1位是符号位S,接着的8位是指数E,剩下的23位为有效数字M
对于64位的浮点数,最高的1位是符号位S,接着的11位是指数E,剩下的52位为有效数字M
对于有效数字M:
在计算机内部保存M时,默认这个数的第一位总是1,因此可以被舍去,只保存后面的数字
等到读取的时候再加上1,这样可以节省1位有效数字
对于有效数字E:
E为一个无符号整数,如果E为8位,取值范围是0~255,如果E为11位,取值范围是0~2047,而科学计数法中,指数可以为负数,所以规定一个中间数来区分正负,对于8位的E,中间数为127,对于11位,中间数为1023。比如2^10的E为10,所以保存成32位浮点数时保存成10+127=137,即10001001。
当E不全为0或不全为1时:
指数E的计算值减去127或1023,得到真实值,再将有效数字M前加上第一位的1
当E全为0时:
浮点数的指数E等于1-127(或1-1023)即为真实值,有效数字M不再加上第一位的1,而是还原为0.xxxxxx的小数。
当E全为1时:
如果有效数字M全为0,则表示正负无穷大(正负去决于符号位S);
举例:
#include <stdio.h>
int main()
{
int n = 9;
//在内存中以补码的形式存放
//00000000000000000000000000001001
float* pFloat = (float*)&n;
//以浮点数的角度,n的存储内容为
//0 00000000 00000000000000000001001
//E全为0,有效数字M不再加上第一位,E为-126
//(-1)^0*0.00000000000000000001001*2^-126(该数无限接近于0)
printf("n的值为:%d\n", n);//9
printf("*pFloat的值为:%f\n", *pFloat);//0.000000000
*pFloat = 9.0;
//9.0=1001.0=1.001*2^3
//在内存中的存储形式为
//0 10000010 001000000000000000000000
//以整型的角度,存储的内容则为补码
//010000010001000000000000000000000=1091567616
printf("num的值为:%d\n", n);//1091567616
printf("*pFloat的值为:%f\n", *pFloat);//9.0
return 0;
}