文章目录
数据类型
基本的内置类型
char //字符数据类型
short //短整型
int //整形
long //长整型
long long //更长的整形
float //单精度浮点数
double //双精度浮点数
//C语言有没有字符串类型?
long long是C99标准才引入的,某些旧的编译器无法支持的
int在16位机器下是2个字节
c语言只规定了sizeof(long)>=sizeof(int)
#include <stdio.h>
int main()
{
printf("%d\n", sizeof(int)); 4
printf("%d\n", sizeof(long)); 4
return 0;
}
布尔类型(C99引入)
_Bool 表示真假
需要引入对应头文件#include <stdbool.h>
//0表示假
//非0表示真
int main()
{
int flag = 0;
if (flag)
{
}
return 0;
}
#include <stdbool.h>
int main()
{
_Bool flag = true;
if (flag)
{
printf("hehe\n");
}
return 0;
}
c语言中可以完全没有_Bool
转到定义可以发现
#define false 0
#define true 1
类型基本归类
整型家族
char
unsigned char
signed char
short
unsigned short [int]
signed short [int]
int
unsigned int
signed int
long
unsigned long [int]
signed long [int]
int a = 10;默认是signed int
unsigned int age = 20;
short ==> signed short (int)
long ==> signed long (int)
int可以省略不写
而char到底是 signed char 还是 unsigned char 取决于编译器
常见的编译器下:char ==> signed char
%d打印有符号数
%u打印无符号数
unsigned 要配合%u来使用
int num = 10;
printf("%d\n", num);
num = -10;
printf("%d\n", num);
unsigned int num = 10;
printf("%u\n", num);
num = -10;
printf("%u\n", num);//很大的一个数
浮点数家族
float
double
构造类型
也叫自定义类型
数组类型
结构体类型 struct
枚举类型 enum
联合类型 union
int a[10];类型为int [10]
int b[5]; 类型为int [5]
指针类型
int* pi;
char* pc;
float* pf;
void* pv;
空类型
void 表示空类型
通常应用于函数的返回类型、函数的参数、指针类型
整型在内存中的存储
数据在内存中以2进制的形式存储
内存中存的是2进制,但在调试器的内存窗口中是以16进制的形式进行展示,以便节省空间
最右边的是将内存中的数据解析成文本内容展示,参考意义不大
整数:
整数二进制有3种表示形式:原码 反码 补码
正整数三码相同
原码:按照一个数的正负,直接写出来的二进制就是原码
反码:符号位不变,其他位按位取反
补码:反码+1
最高位是符号位,后面的是有效位
符号位为1表示负数,0表示正数
-10
10000000000000000000000000001010
11111111111111111111111111110101
11111111111111111111111111110110
ff ff ff f6
因此内存中展示的就是ff ff ff f6,但需要倒着存放
f6 ff ff ff
为什么存的是补码呢
cpu只能做加法 不能做减法
减法是由加法模拟,
乘法就是多个数相加
1-1 =1+(-1)
如果用原码去计算 算出来的是-2 结果有误
00000000000000000000000000000001
10000000000000000000000000000001
10000000000000000000000000000010
用补码计算
00000000000000000000000000000001
11111111111111111111111111111111
100000000000000000000000000000000只有32位,高位舍去
00000000000000000000000000000000
结果才正确
使用补码,可以将符号位和数值域统一处理
此外,"补码与原码相互转换",运算过程是相同的,
不需要额外的硬件电路
对补码再次取反加1,又能得到原来的原码
11111111111111111111111111111111
10000000000000000000000000000000
10000000000000000000000000000001
大小端
为什么是倒着存的呢?
大端字节序:
把数据的低位字节序的内容存放在高地址处,高位字节序的内容存放在低地址处
小端字节序:
把数据的低位字节序的内容存放在低地址处,高位字节序的内容存放在高地址处
地址从左到右是由低到高的
0x11223344 44是低字节序(类比个位十位百位千位)
地址从左到右(从上到下)增大
0x11223344 放到内存中是44332211就是小端存储
为什么出现大小端存储?
存在如何安排多个字节存储的问题,32位,64位
练习
1.大小端
请简述大端字节序和小端字节序的概念,设计一个小程序来判断当前机器的字节序。
#include <stdio.h>
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;
}
&i访问的int* 要强制转换成char* 来访问第一个字节
char类型数据没有大小端问题
大小端是字节序,2个及以上字节才有
char类型数据只有1个字节
2.截断
//输出什么?
#include <stdio.h>
int main()
{
char a= -1;
signed char b=-1;
unsigned char c=-1;
printf("a=%d,b=%d,c=%d",a,b,c);//-1 -1 255
return 0;
}
char a = -1;
10000000000000000000000000000001 原码
11111111111111111111111111111110 反码
11111111111111111111111111111111 补码
补码存到a里面去,发生截断,只截断最低的8个bit位
a里面放的是 11111111
char a 与signed char b等价(VS编译器)
c里面放的也是11111111
-1是整数,a是char类型,会发生截断
以%d格式打印,a 是char类型,也就是signed char
要"发生整型提升",高位补符号位
11111111111111111111111111111111 提升之后的补码
打印打的是原码
10000000000000000000000000000000
10000000000000000000000000000001 -1
11111111
c是unsigned char
c也要发生整型提升,"无符号高位补符号位"
00000000000000000000000011111111 补码/反码/原码
正数3码相同
以%d格式打印,认为内存中存的是有符号数
2^8 - 1 = 255
3.%u打印负数
#include <stdio.h>
int main()
{
char a = -128;
printf("%u\n",a);//4294967168
return 0;
}
-128
10000000000000000000000010000000 -128原码
11111111111111111111111101111111
11111111111111111111111110000000
存到char a中会发生截断 10000000
整型提升后
11111111111111111111111110000000
以%u打印,认为内存中存的是无符号数
三码相同
转换为10进制:4294967168
4.%u打印正数
char a = 128;
以%u打印
00000000000000000000000010000000
截断后10000000放到a里面
整型提升
11111111111111111111111110000000
%u无符号数打印
转换为10进制:4294967168
5.正负相加
int i= -20;
unsigned int j = 10;
printf("%d\n", i+j);
//按照补码的形式进行运算,最后格式化成为有符号整数
(不用再去考虑把 int 转换为 unsigned int 再计算,内存中肯定是会转换的)
10000000000000000000000000010100
11111111111111111111111111101011
11111111111111111111111111101100 -20补码
00000000000000000000000000001010 10补码
11111111111111111111111111110110 加完之后的补码
11111111111111111111111111110101
10000000000000000000000000001010 -10 原码
6.unsigned int i循环变量
#include <stdio.h>
#include<Windows.h>
int main()
{
unsigned int i;
for (i = 9; i >= 0; i--)
{
printf("%u\n", i);
Sleep(1000);//为了看得更清楚
}
return 0;
}
死循环了
无符号整数i恒大于0的
一旦变成-1,而-1放到unsigned int里面
11111111111111111111111111111111
每一位都是有效位
再以%u打印
对应十进制4294967295
2 ^ 32 - 1
7.char取值范围
int main()
{
char a[1000];
int i;
for(i=0; i<1000; i++)
{
a[i] = -1-i;
}
printf("%d",strlen(a));
return 0;
}
char数组最多储存 2 ^ 8 - 1个元素
signed char 1byte = 8bit
unsigned char 1byte = 8bit
内存中放的是补码
如果是无符号char取值范围是0 ~ 255
有符号的char -128 ~ 127
11111111 补码
11111110
10000001 -1(原码)
10000001 补码
10000000
11111111 -127(原码)
10000000直接解析成-128
110000000 -128
101111111
110000000
取完8位也还是10000000
又strlen遇到\0才停止故刚好转一圈停下来255个数
\0的ASCII值也就是0
巧记:超出范围的数据如果是正数则减去256,如果是负数则加上256
128减去256 = -128
signed short 16 bit
00000000 00000000 0
00000000 00000001
00000000 00000010
01111111 11111111 32767
10000000 00000000 -32768
...
11111111 11111110 -2
11111111 11111111 -1
取值范围就是
#include<limits.h>
中有数据类型的取值范围
8.无符号变量
#include <stdio.h>
unsigned char i = 0;
int main()
{
for(i = 0;i<=255;i++)
{
printf("hello world\n");
}
return 0;
}
死循环
unsigned char取值范围0 ~ 255
i<=255恒成立
使用无符号变量要小心!!!一不注意就死循环了
浮点型在内存中的存储
常见浮点数
3.14159
1E10 科学计数法1.0 * 10^10
浮点数表示的范围:float.h中定义
存储规则
根据国际标准IEEE(电气和电子工程协会) 754,任意一个二进制浮点数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。
十进制的-5.0,写成二进制是 -101.0 ,相当于 -1.01×2^2 。s=1,M=1.01,E=2。
十进制5.5 101.1 1.011 x 2 ^ 2(二进制)
-1 ^ 0 x 1.011 x 2 ^ 2
123.4 = 1.234 x 10 ^ 2
* 2 ^ 2 等价于小数点右移2位
float32bit位
最高位当符号位 1/0
double 64个bit位
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是可以出现负数的,所以IEEE 754规定,存入内存时E的真实值必须"再加上一个中间数",
对于8位的E,这个中间数是127;
对于11位的E,这个中间数是1023。
2^10的E是10,所以保存成32位浮点数时,必须保存成10+127=137,对应二进制为10001001。
从内存中取出指数E
1.E不全为0或不全为1
指数E的值减去127/1023得到真实值
再将有效数字M前加上第一位的1
2.E全为0
±1.xxx * 2^(-)127 无限接近于0的数
浮点数的指数E等于1-127(或者1-1023)即为真实值
有效数字M不再加上第一位的1,而是还原为0.xxxxxx的小数
表示±0,以及接近于0的很小的数字
3.E全为1
±1.xxxx * 2^(128)
表示±∞
例子
int main()
{
int n = 9;
float *pFloat = (float *)&n;
printf("n的值为:%d\n",n);//9
printf("*pFloat的值为:%f\n",*pFloat);//0.000000
*pFloat = 9.0;
printf("num的值为:%d\n",n);//1091567616
printf("*pFloat的值为:%f\n",*pFloat);//9.000000
return 0;
}
n的值为:9
*pFloat的值为:0.000000
num的值为:1091567616
*pFloat的值为:9.000000
以%f或%lf格式打印,默认小数点后有6位
整型的9放进去,%d打印出来还是9
整型的形式放进去,浮点型方式拿出来不一样
浮点数形式放进去,%d拿出来不一样
浮点数形式放进去,浮点数形式拿出来一样
9的三码相同
00000000000000000000000000001001
9放到n里面去
pFloat指针变量,4个字节
整型的9放进去,%d打印出来还是9
整型的形式放进去,浮点型方式拿出来
0 00000000 00000000000000000001001
S E M
E为全0 E的真实值1-127 = -126
2^(-126)
0.00000000000000000001001
(-1)^0 * 0.00000000000000000001001 * 2^(-126)
所以打印出来是0
*pFloat = 9.0;
printf("num的值为:%d\n",n);
1001.0 --二进制
1.001 * 2 ^ 3
9.0是正数 S=0
E=3 要存3+127进去 130 也就是 10000010
有效数字M等于001后面再加20个0,凑满23位
0 10000010 00100000000000000000000
S E M
0x 41 10 00 00在内存中也是小端存储
00 00 10 41
%d打印出来,最高位为0 三码相同
01000001000100000000000000000000
1091567616