目录
1. 数据类型介绍
char //字符数据类型
short //短整型
int //整形
long //长整型
long long //更长的整形
float //单精度浮点数
double //双精度浮点数
值得一提的是,C中是没有字符串类型存在的 。
为什么C中要设置各种各样的数据类型呢,个人认为,它的意义有以下几点。
数据类型的意义
①避免浪费内存防止内存不够用。
②解决“存”的问题:决定使用此类型需要开辟内存空间大小(大小决定了使用范围)。
③解决“取”的问题:改变看内存视角,可以一个类型一个类型的看,而非一个比特位一个比特位的看,即如何看待内存空间的视角。
1.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]
char类型也输入整形家族,因为char类型在存储的时候是以ACISS码进行存储的,也就是说,实际上存储的是数组,所以char类型属于整形家族
浮点数家族
float
double
构造类型
> 数组类型
> 结构体类型 struct
> 枚举类型 enum
> 联合类型 union
关于此类构造类型以后我会写博客进行详细的讲解
指针类型
int *pi;
char *pc;
float* pf;
void* pv;
值得注意的是,不同的指针类型的大小在同一个平台下是相同的,具体为32位平台4个字节,64位平台8个字节,而关于指针类型的意义啥的,我会在之后写一篇关于指针的博客进行讲解
空类型
void 表示空类型(无类型)
通常应用于函数的返回类型、函数的参数、指针类型
2. 整形在内存中的存储
一个变量的创建是要在内存中开辟空间的,而具体开辟空间的大小是由变量的类型决定的。
比如我们在创建一些整形变量的时候,
int a = 100;
int b = -100;
我们知道的是,在创建这两个变量的时候会为其分配4个字节的空间,那么,数据在所开辟的内存中是如何储存的呢?在了解这个之前,我们先来学习一下原反补的概念。
2.1 原码、反码、补码
首先,我们需要了解,在计算机中的整数总共存在三种二进制的表达方式,分别是原码,反码和补码。
三种表达方式都具有符号位和数值位两部分,对于符号位而言“0”表示的是正数,“1”表示的是负数。
现在,我们开始了解一下原反补码的概念。
原码
直接将数值按照正负数的形式翻译成二进制就可以得到原码。
栗子:
在此,我们以32位机器为例(32位机器的int类型大小为4个字节,即32个比特位)
例如,20的原码是
0000 0000 0000 0000 0000 0000 0001 0100(第一个0是符号位,表示正数)
-20的原码是
1000 0000 0000 0000 0000 0000 0001 0100(第一个1是符号位,表示负数)
反码
将原码的符号位不变,其他位按位取反就得到了反码。
栗子:
-20的原码是
1000 0000 0000 0000 0000 0000 0001 0100(第一个1是符号位,表示负数)
由于反码是原码的除符号位的按位取反,所以-20的补码是
1111 1111 1111 1111 1111 1111 1110 1011(第一个1是符号位,表示负数)
补码
反码+1,得到的就是补码。
栗子:
-20的原码是
1000 0000 0000 0000 0000 0000 0001 0100(第一个1是符号位,表示负数)
由于反码是原码的除符号位的按位取反,所以-20的补码是
1111 1111 1111 1111 1111 1111 1110 1011(第一个1是符号位,表示负数)
补码是反码+1
所以-20的补码就是
1111 1111 1111 1111 1111 1111 1110 1100(第一个1是符号位,表示负数)
然后再来注意正负数。
正数
正数的原反补都相同。
负数
负数在原反补上的存储与正数截然不同。
值得注意的是,对于整形来说,在内存中存的是补码
为啥呢?
现在我们不妨在VS里调试一下看看
通过这里,我们不难发现内存中存储的确实是补码,但是,他的存储顺序好像有点问题,这又是为什么呢?
2.2 大小端介绍
首先,什么是大小端
那为什么会出现大小端这种东西呢?
Ok,下面我们进行一个小插曲,写一个可以判别当前机器是大端还是小端的代码
#include <stdio.h>
int check_sys()
{
int i = 1;
return (*(char *)&i);//char类型的指针会取低地址的第一个字节
//所以只需要判断取出的字节是0还是1就好了
}
int main()
{
int ret = check_sys();
if(ret == 1)
{
printf("小端\n");
}
else
{
printf("大端\n");
}
return 0;
}
2.3整形提升
首先,什么是整型提升?
整型提升是C程序设计语言中的一项规定:在表达式计算时,各种整形首先要提升为int类型。表达式的整型运算要在CPU的相应运算器件内执行,CPU内的整型运算器的操作数的字节长度一般是int 类型的字节长度,即四个字节。
所以说,如果是两个char类型的数据相加的话,需要先将其整形提升为整形之后在进行计算。
下面,我们对整形提升进行详细的讲解。
首先,要了解一下整形提升的规则。
整型提升的规则:
整型提升分为有符号和无符号两种。
1.有符号的:整型提升时是按照变量的补码被截断时的最高位是什么进行补位的,如果截断后最高位即最左面的一位数为 1 则在最高位前补 1 ,如果最高位是 0 则在前面补 0 ,补够32位即int类型即可。
2.无符号的: 直接在被截断的前面补 0 即可。
举几个栗子:
char a = -1;
printf("%d",a);
//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 截断
//整形提升
//1111 1111 1111 1111 1111 1111 1111 1111 补码
//1111 1111 1111 1111 1111 1111 1111 1110 反码
//1000 0000 0000 0000 0000 0000 0000 0001 补码
所以上述代码最终打印的是 -1。
char a = 130;
printf("%d",a);
//0000 0000 0000 0000 0000 0000 1000 0010 原码
//0000 0000 0000 0000 0000 0000 1000 0010 原码
//0000 0000 0000 0000 0000 0000 1000 0010 原码
//1000 0010 截断
//整形提升
//1111 1111 1111 1111 1111 1111 1000 0010 补码
//1111 1111 1111 1111 1111 1111 1000 0001 反码
//1000 0000 0000 0000 0000 0000 0111 1110 补码 -126
由上述过程可知,最终打印的值为-126。
unsigned char c = 130;
//0000 0000 0000 0000 0000 0000 1000 0010原
//0000 0000 0000 0000 0000 0000 1000 0010反
//0000 0000 0000 0000 0000 0000 1000 0010补
//1000 0010截断
//整形提升
//0000 0000 0000 0000 0000 0000 1000 0010原
//0000 0000 0000 0000 0000 0000 1000 0010反
//0000 0000 0000 0000 0000 0000 1000 0010补 130
unsigned char d = -10;
//1000 0000 0000 0000 0000 0000 0000 0010原
//1111 1111 1111 1111 1111 1111 1111 1101反
//1111 1111 1111 1111 1111 1111 1111 1110补
//1111 1110截断
//整形提升
//0000 0000 0000 0000 0000 0000 1111 1110原
//0000 0000 0000 0000 0000 0000 1111 1110反
//0000 0000 0000 0000 0000 0000 1111 1110补 246
printf("%d\n%d\n", c, d);
所以最终输出的答案是 130 和 246。
小练习(判断以下代码输出的结果)答案在文章末尾
// 1.
#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);
return 0;
}
//2.
#include <stdio.h>
int main()
{
char a = -128;
printf("%u\n",a);
return 0;
}
//3.
#include <stdio.h>
int main()
{
char a = 128;
printf("%u\n",a);
return 0;
}
//4.
int i= -20;
unsigned int j = 10;
printf("%d\n", i+j);
//5.
unsigned int i;
for(i = 9; i >= 0; i--)
{
printf("%u\n",i);
}
//6.
int main()
{
char a[1000];
int i;
for(i=0; i<1000; i++)
{
a[i] = -1-i;
}
printf("%d",strlen(a));
return 0;
}
//7.
#include <stdio.h>
unsigned char i = 0;
int main()
{
for(i = 0;i<=255;i++)
{
printf("hello world\n");
}
return 0;
}
3. 浮点型在内存中的存储
对于浮点数的存储,我们以一个程序为栗子展开讲解。
int main()
{
int n = 9;
float *pFloat = (float *)&n;
printf("n的值为:%d\n",n);
printf("*pFloat的值为:%f\n",*pFloat);
*pFloat = 9.0;
printf("num的值为:%d\n",n);
printf("*pFloat的值为:%f\n",*pFloat);
return 0;
}
这是为啥呢,让我们先来看一下浮点数的存储规则 。
3.1 浮点数存储规则
![](https://i-blog.csdnimg.cn/blog_migrate/e221752bbe5dd181080b1b3268afd3e2.png)
![](https://i-blog.csdnimg.cn/blog_migrate/1c1e1cdc5a25489da02ed1f7e7c3ba10.png)
![](https://i-blog.csdnimg.cn/blog_migrate/59d788eb6abebe6728d95c64c5b5c3f6.png)
![](https://i-blog.csdnimg.cn/blog_migrate/93ce3614458ea16b793875f595f45ce5.png)
9 -> 0000 0000 0000 0000 0000 0000 0000 1001
9.0 -> 1001.0 ->(-1)^01.0012^3 -> s=0, M=1.001,E=3+127=130
0 10000010 001 0000 0000 0000 0000 0000