1. 数据类型的介绍
char//字符型 1字节数
short//短整型 2
int//整型 4
long//长整型 4
long long//更长的整型 8
float//单精度 4
double//双精度 8
整型
一般情况下默认 int是signed int。
正数的原码,反码,补码相同,负数的原码反码补码规则如下:
#include<stdio.h>
int main()
{
int a = 20;//14 00 00 00
int b = -10;//f6 ff ff ff//11110110 11111111 11111111 1111111111111111
//正数的原码,反码,补码相同,负数的原码反码补码规则如下
//原码:10000000 00000000 00000000 00001010
//反码:11111111 11111111 11111111 11110101//其余取反符号位不变
//补码:11111111 11111111 11111111 11110110//取反加1
char c1 = 5;//05 00 00 00
unsigned char c2 = 5;//05 00 00 00
short s1 = 10;// 0a 00 00 00
unsigned s2 = 10;//0a 00 00 00
int i1 = 20;//14 00 00 00
unsigned int i2 = 20;//14 00 00 00
}
对于整型来说:数据存放内存中的是补码。
使用补码可以将符号位和数值位统一处理。(cpu中只有加法)
int q = 1;
int w = 1;
int e = q - w;//看作e=q+(-w)
//q补码:00000001
//-w原码:10000001 反码:11111110 补码:1111111
//计算加法 00000001
/ 11111111
//=======1 00000000 将1截断得 e=0;
符号位不变,原到补为取反加一
符号位不变,补到原为取反加一
减一取反也行
整型提升:
#include<stdio.h>
int main()
{
char a = -1;// -1 的原码1000——0001———反码1111——1110————补码1111——1111;截断a放置为11111111;a再整型提升为1111——1111提升是向前补符号位
signed char b = -1;//与a相同为-1
unsigned char c = -1;// -1 的原码1000——0001———反码1111——1110————补码1111——1111;截断c为11111111;c整型提升高位补0;0000————————1111//最高位是零即为正数//8个1为255
printf("%d %d %d", a, b, c);//-1 -1 255
return 0;
}
原因:有符号整型提升时最前面会补1,为11111111——11111111=-1;无符号整型提升时前面会补0,为00000000——11111111=255;
截断与提升
截断是将整型二进制数高位相应的字节截断
提升是将该变量中二进制数高位补满符号位
例如:
int main()
{
char a = -128;//原10000000 00000000 00000000 10000000
//反11111111 11111111 11111111 01111111
//补11111111 11111111 11111111 10000000
//a 10000000//截断是截断前面的
//a 11111111 11111111 11111111 10000000整型提升
printf("%u\n", a); //%u无符号10进制整数//4294967168
return 0;
}
int main()
{
char a = 128;//a=10000000就是-128
//char类型的取值范围在-128————127 unsigned char的范围是0——255
printf("%d\n%u\n", a, a); //-128 4294967168
//char类型从000000000==0到01111111=127 到100000000=-128 到11111111=-127
return 0;
}
int main()
{
int i = -20; //原10000000 00000000 00000000 00010100
//反11111111 11111111 11111111 11101011
//i //补11111111 11111111 11111111 11101100
unsigned int j = 10;//原00000000 00000000 00000000 00001010
//j //原反补相同
printf("%d", i + j);//补11111111 11111111 11111111 11110110//储存的i+j
printf("\n%u", i + j);//将用无符号方式解二进制得4294967286
return 0;//原10000000 00000000 00000000 00001010 解出-10的原码
}
int main()
{
char a[1000];
int i;
for (i = 0; i < 1000; i++)
{
a[i] = -1 - i;//10000001加任意
}
printf("%d", strlen(a));
//从-1到-128到127到0
return 0;
}
- 大小端存储
1.大端存储模式,是指数据的低位保存在内存的高地址中,而数据的高位,保存在内存的低地址中。例如:x86,ARM,DSP
2.小端存储模式,是指数据的低位保存在内存的低地址中,而数据的高位,保存在内存的高地址中。例如:KEIL,C51
有些ARM处理器还可以由硬件来选择。
#include<stdio.h>
int main()
{
int a = 0x11223344;// 44 33 22 11
//由于11是高位放在高地址处了,所有改编器下为小端存储
return 0;
}
判断大小端:
int main()
{
int a = 3;//大端0——————00000110;小端000000110——————————0;
//将它的前两个字节拿出来——大端:0000000000;小端00000110;
short b = (short)a;
char* p = (char*)&a;
*(char*)&a;//解引用已经被char指针指向的a的地址
printf("%d\n", *p);
if (b != 0)
printf("该编译器为小端存储。\n");
else
printf("该编译器为大端存储。\n");
printf("%d", b);
return 0;
}
当然如果上述例子中a=1时会更方便判断和构造判断函数返回参数,
浮点型
#include<stdio.h>
int main()
{
int n = 9;//n=00 00 00 09
float* pFloat = (float*)&n;//浮点型指针指向浮点型n的地址
printf("n的值为:%d\n", n);
printf("*pFloat的值为:%f\n", *pFloat);
*pFloat = 9.0;//0 10000010 00100000000000000000000
printf("num的值为:%d\n", n);
printf("*pFloat的值为:%f\n", *pFloat);//float n=00 00 10 41
return 0;
}
浮点数V=(-1)s m 2e;
如:
十进制的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。
2E是因为,是因为这个是个二进制数
1<=M<2,所以说m=1.xxxx可以写为.xxxx多保留一位。**
- 对E的分析:
1.存E
(由于E存在负数,但是E的存储是无符号二进制数)需要对E加上一个中间值
八位加127;十一位加1023;
(2的八次方=255;2的十一次方=2048)
2.取E
1)E全为1
表示2的128次方或2的1024次方无穷大(正负取决于S)
2)E全为0
表示2的负127次方或2的负1023次方,表示无穷小,M将不再加上1,而是直接还原为0.xxxx,这样表示无穷小的±0;
3)E不全为1也不全为0:
这是需要根据八位或十一位减去127或1023得到真实值,再在M上加第一位1.
int main()
{
float a = 5.5;//为(-1)**0*5.5*10**0=(-1)**0*101.1*2*2;
//0 10000001 01100000000000000000000
//00 00 b0 40小端存储 40 b0 00 00
return 0;
}
-
单精度
S/E/M=1/8/23 (bit) -
双精度
S/E/M=1/11/52(bit)
自定义类型(构造类型)
数组类型
类型和数组大小改变时,类型也是一种改变。
结构体类型(struct)
枚举类型(enum)
1.枚举顾名思义就是一一列举。
2.把可能的取值一一列举。
列如:
这些可能取值都是有值的,默认从0开始,一次递增1,当然在定义的时候也可以赋初值。
#include<stdio.h>
enum day
{
Mon,
Tues,
Wed=10,
Thur,
Fri,
Sat,
Sun,
};//和#define差不多
int main()
{
printf("%d\n", Mon);//0
printf("%d\n", Tues);//1
printf("%d\n", Wed);//10
printf("%d\n", Thur);//11
printf("%d\n", Fri);//12
return 0;
}
枚举类型的优点:
我们可以使用 #define 定义常量,为什么非要使用枚举?
枚举的优点:
- 增加代码的可读性和可维护性
- 和#define定义的标识符比较枚举有类型检查,更加严谨。
- 防止了命名污染(封装)(如将1可用Mon替代)
- 便于调试
- 使用方便,一次可以定义多个常量
##联合类型(union)
联合的成员是共用同一块内存空间的,这样一个联合变量的大小,至少是最大成员的大小(因为联合至少得有能力保存最大的那个成员)
联合体大小的计算(也满足对齐)
typedef union sz
{
char str[5];//对齐数为1
int a;//对齐数为4
}sz;//1*5+3与4中最大数=8
int main()
{
printf("%d", sizeof(sz));
return 0;
}
也可以用来求大小端存储
typedef union St
{
char a;
int i;
}St;
int main()
{
St s;
s.i = 258;//1 00000010
if (s.a != 0)
printf("小端存储。\n");
else
printf("大端存储。\n");
return 0;
}
空类型
void表示空类型(无类型)
通常适用于函数的返回类型,函数的参数,指针类型。
void f(void)
{;}
void*p;//一种没有具体类型的指针