目录
数据类型
整形家族
浮点型家族
构造类型
指针类型
空类型
整形在内存中的存储(原反补)
NO1.
NO2.
NO3.
NO4.
NO5.
NO6.
大端小端字节序
NO.1
NO.2
NO.3
NO.4
练习题
NO1.
NO2.
NO3.
NO4.
NO5.
NO6.
总结
数据类型
在初C语言中,我们已经学习过基本内置类型以及它们所占存储空间的大小。我们再回顾一下。
每一个类型所占空间的大小都不一样,这么多丰富的类型,那它们存在的意义是什么呢?
类型的意义:
- 使用这个类型开辟内存空间的大小(大小决定了使用范围)。
- 如何看待内存空间的视角。
这样我们就会在合适的场景选择合适的数据类型。
整形家族
char
unsigned char
signed char
short
unsigned short [int]
signed short [int]
int
unsigned int
signed int
long
unsigned long [int]
signed long [int]
long long
//关于long long我们酌情使用。
signed 有符号的
unsigned 无符号的
为什么char字符会归为整形家族?
字符在内存中的存储的是字符的ASCII码值,ASCII码值是整形。
signed和unsigned 修饰的整形有什么区别吗?
signed是有符号的;unsigned int 是无符号的。
int a;//== sined int a signed int a; unsigned int a;
我们平时在编译器上创建一个变量,相当于创建一个有符号的变量。
当然short long long long 均是创建变量相当于创建一个有符号signed 的变量。(除了char)
那char呢?
char是否是相当于signed char?C语言标准并没有规定,取决于编译器。
大部分编译器是,不排除小部分编译器是unsigned int
ASCII码表中规定的ASCII码值的范围是0~127(字符均是正数)。
只有小部分负数是可以存储在 signed char
浮点型家族
float
double
long double
//建议酌情使用
构造类型
//自定义类型
> 数组类型
> 结构体类型 struct
> 枚举类型 enum
> 联合类型 union
指针类型
int *pi;
char *pc;
float* pf;
void* pv;//无具体类型的指针
空类型
void test(void)
//第一个void 表示test函数不会返回任何值
//第二个void 表示test函数没有参数
{
//
}
int main(void)//
int main(int argc,char* argv[],char* envp[])
整形在内存中的存储(原反补)
一个变量的创建是要在内存中开辟空间的。空间的大小是根据不同的类型而决定的。
NO1.
那接下来我们来谈谈数据在所开辟内存中到底是如何存储的呢?
计算机能够处理的是二进制的数据。
整型和浮点型数据在内存中也是以二进制的形式进行存储的。
整型在内存中存储的是补码的二进制序列。
NO2.
整型的二进制表示形式是怎样的呢?
计算机中的整型有三种二进制表示:原码,反码,补码。
- 分为有 有符号整数 和 无符号整数。
- 无符号整数:原码,反码,补码相同。
- 有符号整数:三种表示方法均有 符号位 和 数值位 两部分。
- 正数:原码,反码,补码相同。
- 负数:原码,反码,补码要进行计算的。
- 符号位:0 表示正
- 符号位:1 表示负
- 数值位:正数的原,反,补码相同。
- 数值位:负数的原,反,补表示方法各不同。
NO3.
signed int 和 unsigned int?
在内存中,对于存储一个整数需要4个字节,也就是32个比特位。
一个整数写出二进制序列的时候,也就是32个比特位。
有符号整数:最高位就是符号位。符号位是1,则表示是负数。符号位是0,则表示是正数。
无符号整数:没有符号位,所有位都是数值位。
同一个数无论是有符号整数,还是无符号整数的 数值均相同。
有符号整数的最高位符号位还是会参与运算。
如下:
#include<stdio.h> int main() { int a = 10;//== signed int //0 0000000 00000000 00000000 00001010 unsigned int b =10; //00000000 00000000 00000000 00001010 return 0; }
站在a的角度,第一个0就是符号位。
站在b的角度,第一个0就是数值位。
NO4.
负整数的原反补怎样转化呢?
NO5.
进制间的转化?这里我们不专门去讲解,可以自己学一学。
对于整形来说,数据存放内存中其实存放的是补码。为什么呢?
- 在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码,可以将符号和数值域统一处理。
- 同时,加法和减法也可以统一处理(CPU只有加法器)此外,补码和原码相互转换,其运算过程是相同的,不需要额外的硬件电路。
NO6.
#include<stdio.h>
int main()
{
int a = -10;//== signed int
//1 0000000 00000000 00000000 00001010
//1 1111111 11111111 11111111 11110101
//1111 1111 1111 1111 1111 1111 1111 0110——补码
//十六进制
//0x ff ff ff f6
unsigned int b =-10;
//10000000 00000000 00000000 00001010
return 0;
}
整形在内存中是以二进制的补码存储的,在编译器中为了观瞻,是以十六进制的方式去展示给我们,请问为什么是以十六进制倒叙放置的呢?那接下来我们就要介绍我们的大小端字节序存储。
大端小端字节序
关于内存中二进制/十六进制,单位 比特位和字节的转换,如下图。
NO.1
大端小端产生?
int a=0x11223344
//1 2 3
//百位 十位 各位
//高位 低位
根据字节存储来解释。
我们可以有无数中存储方式,只要按照存进去的方式,在需要使用时拿出来按照原来的顺序放置还原。怎么存都可以!
但是为了简便我们只采用了两种存储方式:
- 大端字节序存储:把一个数据的低位字节序的数据存放在内存的高地址处,高位字节处的数据存放在内存的低地址处。
- 小端字节序存储:把一个数据的高位字节序的数据存放在内存的低地址处,高位字节处的数据,存放在内存的高地址处。
除了字符整型,都是这两种存储方式,char1个字节不需要顺序。
NO.2
什么是大端小端?
NO.3
为什么有大端小端?
NO.4
笔试题:请简述大端字节序和小端字节序的概念,设计一个小程序来判断当前机器的字节序?
根据上图就有如下代码:
#include<stdio.h>
int main()
{
int a = 1;//
char* p = (char*)&a;//强制转化
if (*p == 0)
printf("大端\n");
if(*p == 1)
printf("小端\n");
return 0;
}
//用函数包装呢?
#include<stdio.h>
int check_sys(void)
{
int a = 1;
char* p =(char*) &a;//强制类型转化
if (*p == 0)
return 0;
if (*p == 1)
return 1;
}
int main()
{
int ret = check_sys();
if (ret == 0)
printf("大端\n");
if (ret == 1)
printf("小端\n");
return 0;
}
//简化
#include<stdio.h>
int check_sys()
{
int a = 1;
return *(char*)&a;
}
int main()
{
int ret = check_sys();
if (ret == 0)
printf("大端\n");
if (ret == 1)
printf("小端\n");
return 0;
}
练习题
//以下主要讲解的是char类型的signed 和unsigned
//可以推广到short/long等等
%d 是十进制的形式打印有符号整数
%u 是十进制的形式打印无符号整数
NO1.
请问下面程序a,b,c分别是多少?
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;
}
#include <stdio.h>
int main()
{
char a = -1;
//有符号的 负数 char类型
// 10000000 00000000 00000000 00000001原码
// 11111111 11111111 11111111 11111110反码
// 11111111 11111111 11111111 11111111补码
//存储char 11111111补码
signed char b = -1;
//有符号的 负数 char类型同上
//存储char 11111111补码
unsigned char c = -1;
//无符号的 负数 char类型
//无符号——没有符号位的概念
// 无符号整数一般放置正数,如果放置负数还是按照负数的原反补来计算
// 10000000 00000000 00000000 00000001原码
// 01111111 11111111 11111111 11111110反码
// 01111111 11111111 11111111 11111111补码
// 存储char 11111111补码
//存储char 11111111
printf("a=%d,b=%d,c=%d", a, b, c);
//%d 是十进制的形式打印有符号整数
// 即便没有符号,当作有符号去打印
//整型提升
// 有符号位提升符号位
// 无符号位补0,提升0
// 有符号ab
//11111111 11111111 11111111 11111111补码
//10000000 00000000 00000000 00000001原码
//打印-1
//b同理-1
//无符号c
//1111111
//00000000 00000000 00000000 11111111补码原码反码
//正数的原码反码补码相同
//打印-c
//225
return 0;
}
NO2.
请问下面三端程序分别输出什么?
2.
#include <stdio.h>
int main()
{
char a = -128;
printf("%u\n",a);
return 0;
}
//
#include <stdio.h>
int main()
{
char a = 128;
printf("%u\n",a);
return 0;
}
//
#include <stdio.h>
int main()
{
char a = 384;//128+256
printf("%u\n",a);
return 0;
}
//2.
#include <stdio.h>
int main()
{
char a = -128;
//有符号整型 char 负数
//10000000 00000000 00000000 10000000原码
//11111111 11111111 11111111 01111111反码
//11111111 11111111 11111111 10000000补码
//存储a char 10000000
printf("%u\n", a);
//%u 是十进制打印无符号整型
//整型提升——变量的类型(有符号/无符号)
//1111111 111111111 11111111 10000000补码
// 打印——看(u/d)——(u_原反补相同/d_正原反补相同_负的计算)
//%u把a看成无符号整数
// 1就不是a的符号位了
//打印
//无符号整型原反补相同
//4,294,967,168
return 0;
}
//
#include <stdio.h>
int main()
{
char a = 128;
//原反补相同
//00000000 00000000 00000000 10000000补码
//存储 10000000
printf("%u\n", a);
//提升 11111111 11111111 11111111 10000000补码
//打印——看成无符号的——原反补相同
//所以还是同上
return 0;
}
经过我们计算和分析,发现上面三段 代码输出的结果是一样的。why?
- 有符号的char类型(signed char)取值范围:-128~127
- 无符号的char类型(unsigned char)取值范围:0~225
- 截断:超过以上的范围,有一部分数据会被截断,只要存储在内存中的数据必须是在以上范围内。
NO3.
请问下面这段代码输出什么?
3.
int i= -20;
unsigned int j = 10;
printf("%d\n", i+j);
//按照补码的形式进行运算,最后格式化成为有符号整数
#include<stdio.h>
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);
//11111111 11111111 11111111 11110110补码
//10000000 00000000 00000000 00001010原码
//-10
return 0;
}
NO4.
下面这段代码输出什么? unsigned int的范围
4.
unsigned int i;
for(i = 9; i >= 0; i--)
{
printf("%u\n",i);
}
NO5.
下面这段代码输出什么?signed char的范围
5.
int main()
{
char a[1000];
int i;
for(i=0; i<1000; i++)
{
a[i] = -1-i;
}
printf("%d",strlen(a));
return 0;
}
NO6.
下面这段代码输出什么?unsigned char的范围
6.
#include <stdio.h>
unsigned char i = 0;
int main()
{
for(i = 0;i<=255;i++)
{
printf("hello world\n");
}
return 0;
}
总结
有符号整型signed char
无符号整型unsigned int
- 有符号整型signed char的取值范围:127 ~ -128
- 无符号整型unsigned char的取值范围:0~255
同理我们可以将以上代码应用于其他整型short/int/long等等。总结出它们的取值范围。特别注意千万别掉进无符号整型的陷阱了!!🆗🆗🆗
例如:short两个字节 16个比特位
- 有符号整型signed short的取值范围:-32768~32767
- 无符号整型unsigned short的取值范围:0~65635
同理大家自己动手总结一下整型类型的取值范围。
关于浮点数的存储会在下篇博文讲解,同时也会去总结二进制中的知识和易错混点。
✔✔✔✔最后,感谢大家的阅读,若有错误和不足,欢迎指正。
代码-----------------→【gitee:https://gitee.com/TSQXG】
联系-----------------→【邮箱:2784139418@qq.com】