C语言的变量类型(int、short、char、float...)及变量类型转换详解
前言
单片机的基本功能是进行数据处理,而数据在进行处理时需要先存放到单片机的存储器中。所以在编写程序时对变量与常量都要先声明数据类型,以便把不同的数据类型定位到嵌入式处理器的不同存储区中。
具有一定格式的数字或数值叫做数据,数据的不同格式叫做数据类型。数据类型是用来表示数据存储方式及所代表的的数值范围的。
据可变性分类
常量:在程序运行中其值不能改变的量;
变量:与上相反
组成部分:变量名、变量值;
每个变量都有一个变量名,在内存中都占据一定的存储单元(地址),并在该内存单元中存放该变量的值。
变量的概念——所谓变量就是一个用来放东 西的容器
C语言中的变量可以理解成计算机内存的一个空间,它必须有一个名字——标识符,用于决定这片内存可以存放什么样的数据类型、帮助系统决定该分配多大的内存空间给该变量。它是开辟在内存的一般数据区(又称静态数据区-程序段落)也可开辟在内存的堆栈区(又称动态数据区),这由程序中用static或auto(可缺省)来规定。
同时,一个变量有它有效的使用范围,称之为作用域。其次,每个变量都有地址,其地址标号用指针来存放。变量可以在声明的同时赋予初值,在程序运行时可以对其值进行改变,因此称之为变量。
从变量的存储类型来说,外部参照型extern变量不允许初始化。自动型auto 变量、 寄存器型 sbit sfr sfr16 变量和静态型 static 变量可以初始化。其中变量如果不进行初始化,则auto 变量和寄存器型 sbit sfr sfr 16变量的初值不确定,而静态型变量(即全局变量)初值为0。
所有常量在C语言中都有其对应的类型。
关于变量的定义:
在同一个层次里不能定义同名变量,但不同层可以;一个中括号代表一个层次,花括号内的东西整个叫做一个代码块。但不同层的变量作用域不同——故有局部变量和全局变量之分。大层变量在其子层里都可以使用;
局部变量(临时变量):
特点: 1、在函数里定义的变量;
2、只能够在当前函数内使用;
3、函数一旦结束,该变量就会被销毁(释放其所占用的内存)
4、如果变量没有赋予初值,里边的数值将会是乱码(随机数)
全局变量:
特点: 1、在函数体之外定义的变量;
2、能够在整个程序里使用;一旦声明,直到程序运行结束才会被销毁;
3、若没赋予初值,其值为0(对应变量类型的0);
4、不仅同一个 .c 文件可以使用,只要是同属于一个工程的.c文件都可 以共用,只
要添加声明即可,即添加外部声明。(extern)
若在函数内(临时)和函数外(全局)都定义里一个同名变量,则优先使用局部变量(就近原则)。
同等类型的变量声明要放在一起,因为C虽然是面向过程的,但在申请变量时它它是会把同种类型的变量放到一起,按变量的大小从小到大一起编排的,故放到一起申明不会造成歧义(也就是在代码里我们就已经告诉阅读者,我们是这么编排的了)。
因为不同系统位数不同,如此替换可提高代码的兼容性
定义变量的思想:
你要确切的的知道你所定义的每一个变量是用来干嘛的,且清晰的知道该变量所代表的实际量的变化规律,这样逻辑会更加清晰明了!——即使得代码具有自注性!
变量类型
不同类型的变量,决定了该变量向系统升旗的内存大小不同和系统对该边内存分析方式的不同。
Int 整型 —— 大小一般32位,四字节
MAX—MIN:1111 1111 1111 1111 1111 1111 1111 111 (31个1),换成十进制数就是2147483647。(2^31-1)—(-2^31+1);其无符号型为0—(2^32-1)
分类:有十进制八进制十六进制二进制;无论是几进制都是整型,每个进制最终都是以二进制的形式存放到内存当中。
十进制:人的习惯,数学的计算都是用该进制
二进制:计算机的习惯
八进制:每一位八进制数代表三位二进制数;一般用于代表权限(读r 写w 执行x),在系统中使用
(PS:权限的三个个体分别是:文件拥有者、文件所在组的其他用户、不在同一个组的其他用户)
十六进制:每一位十六进制代表四位二进制数,在计算机编程中常用该进制,尤其是操作底层CPU时,即后期会用十六进制取操作芯片的每一个位!——所以要掌握16进制转2进制!
PS:在十六进制和八进制里,是不会去考虑负数的情况的——即不会先把一个这两个进制的数转换为二进制后去看第一位是否为负数,这样的话就会丢失掉这个两个进制本身的原数据了,也没有在八进制:032和十六进制:0X23F的表达格式里,没有符号位的位置,仅代表数值。
关于定义的详细的正确解读方式:
int i; //申请一块内存,这块内存的别名叫做i,以后可以通过i这个别名访问这块 内存,这块内
存有4个字节(int大小),分析方式按照int型的方式分析
i=100; //通过i这个别名找到对应的内存给与赋值为100
Short 短整型 —— 大小一般16位,2B
MIN—MAX:0~65535;-32767~32767/-(2^31-1)~(2^31)
Long 长整型 —— windows:32位,4B; ubunt:64b, 8B
表示格式:0L、34L、65L
MAX—MIN:同int
Long long——复合型——长长整型
32位系统位64位,64位系统位128位,不同编译器其大小会不同;
PS:
- C语言中,short大小一定是2B,long一定是4B,而int和long long会因所处不同的系统而不同,int可能是2B (32 位系统)也可能是4B(64位系统),long long 可能是64(32位系统),也可能是128(64位系统)。即基本数据类型大小由编译系统来决定!
- 关于long与int:标准只规定long不小于int的长度,int不小于short的长度。而非long一定要大于int。
- 所输入的值不要大于所定义数据的范围,否则会得不到自己想要的值,数据会从头开始写入
Char 字符类型 —— 又称超级短整型—— 8位
分析方式:按照ascii的方式去分析内存
ascii最大值是127(用到7位数据),该类型说到底其实是整型,应为它是通过ascii码表进行对应输出字符的(也就是说中间有个对照的过程)
即:char ch='a'; char ch = 97;(a)
char ch = '101';(A)//将101这个八进制数转换为A 这个字符放到这个字符 内存里边
Char char = ‘x41’;(A) //这两个叫转义字符,因为它们代表字符
对于输出格式 %c,当你将一个数字转换为其形式输出时,就是输出ASCII码表里对应数值的字符,EP:printf(“%c”, 65);//打印出来的就是字符 A
:这是一个格式符,意思是 转义;其后边添加的是动作叫转义序列,否则为转义字符
ddd: ‘’ ‘34’ ‘412’ //最多只能写三位八进制数
xhh: ‘xa’ ‘x1f’ ‘x1a’ //能写的十六进制数不限
十进制整数转为char型数整数的方法:
该数对256取余,若余数小于127那就是这个余数,若大于127那就(256 -余数)取负;转为无符号型就直接等于对256取余数
PS:(其实应该不止该类型是如此轮回)
1、char型数的轮回是这样的 -128—(递增到)—127—(跳到)—-128
2、无符号char型数的轮回则是 0—(递增到)—255—(跳到)—0 —— 所能存的最大数为255,但所能存的数总共为256个:0~255
Unsigned 无符号的整型
没有符号位,全部都是数据位,全部都按照原码的方式去分析;它不是指不带+ 或 -号的整数,而是指内存表示这类数时没有符号位
有符号型第一位为符号位1为负,0为正
Float 单精度浮点型—4B—其32位中,1位符号位,8位指数位,23位尾数 位
float 类型又称为“浮点” 类型,把实数的小数点都看成在第一位,而用指数位“浮动”小数点。
表示方法:有 3.14 、 3.14e3、 3.14E-2(3.14*10^-2)
PS:浮点数与整型数的编码方式不一样,其编码方式如下
如 123.45 用 32 位二进制(四个字节)表示为 0.12345×10^3
所以计算机拿到浮点数并不可以直接拿去使用(从底层的二进制存储那里拿),而是还需要经过上边的公示进行计算后才能拿去使用,这会浪费CPU时间去计算,降低CPU的整体计算速度; 所以高端芯片一般都会配置 浮点运算器 这个独立的子硬件专门用于进行该运算。
PS:在嵌入式当中能不用浮点数就不要用。
Double 双精度浮点 — 8B — 64位中,1位符号位,11位指数位,52位尾数 位
long double 多精度浮点 — 大小128位 — 保证有效位18位
关于浮点型的说明:
1、不同精度决定了小数个数的多少
2、注意:判断两个浮点数是否相等却不能用 = =
方法:一般float型只能精确到小数后六位(即1e-6),将float型数据的绝对值与1e-6 比较,来判断是否相等(为零)。
PS:float的精度误差在1e-6;double精度误差在1e-15
所以要判断一个float型数:if(fabs(f) < 1e-6);
要判断一个double型数:if(fabs(f) < 1e-15);若满足,为零,则相等。
例:判断float a,b是否相等
if(fabs(a-b) < 1e-6)
printf("a等于bn");
else
printf("a不等于b");
- 浮点数在内存中的状态和输出值未必是一致的,printf可能会四舍五入,大多数十进 制小数无法转换为精确的二进制小数,对于0.1来说,float值大概是: 0.10000000149011612
- 浮点型也称实型
Void 通用类型,可以用来定义通配型指针,但不可以用来定义常量
bool型 其值有两个true和false,用于逻辑判断真假——主要用于高级语言里
备注: 1、bool类型在上层语言他是判断的标准,在C语言当中被当成透明(即一般不使用该类
型)了,因C里判断的条件是:判断一个条件是真还是假,用非0或者0来判断的
2、在C语言中,用bool类型需要导入头文件<stdbool.h>
3、面向底层的c我们不用这个类型,但面向软件级的c会用
数据类型转换
当程序中出现表达式或变量赋值运算时,若运算对象的数据类型不一致,数据类型可以自动进行转换,转换按以下优先级自动进行:
bit ->char->int->long->float->
Unsigned->signed
关于字符串——以首字符地址开始,’’结 束
系统会用一块独立的内存来存放字符串,且这块内存不属于其他任何基础变量的类型,因为其他的大小都已经定死了,而字符串大小不确定,故不能用。
其返回给用户的地址类型是 char * 型,故可这样写:
char *str = “adasda”;//用这个字符型指针来存放这个字符,更根本一点说是记录地址而 已;
真正的字符串并不在这里
char str[7] = “adasda”;//而用数组来存是真的拿这么一个char型的内存来存这么多个字 符,相
对于这字符串的复印件,但不等价于这么一个字符串
PS:
1、在同一个代码中,你在不同的地方用不同指针指向同一个字符串,则编译器会优化它们到同一片内存,共享同一地址。而非肉眼所见的区分开来。
2、字符串的潜规则:所有的字符串即字符串的判断都必定是以''结尾,如果字符串中间夹杂'',遇到什么函数的分析都会一遇到''马上停止分析
常量修饰符
例子:printf(“%”, 234U)//这里边的U限制了我们要打印的这个东西一定为无符号型即一个
常量后边我们可添加一个符号,以定性其为什么型;例如加UL
则为无符号长整型等
变量类型转换
隐式类型转换
在运算当中,如果存在两种不同类型的数据进行运算,系统将会把低精度的类型转化为高精度的类型进行运算。
低精度类型可以默认为高精度类型进行转换(小数点后的数越多精度越高,即float型比整型精度高);
情景1——赋值时转换
Int i = 100; float f;
F = i;//这时因为f精度更高,所以i会把自己转变为浮点型100.000000后再赋值给f
情景2——计算的过程当中的转换
10*3.14;//同样低精度的10会先转换为高精度的浮点型(且系统都会默认转换为 double型,
因为系统认为float型精度不够高)再进行运算,最终相除结 果为一个浮点型(而且
结果是一个更高精度的double型)
情景3——情景2的细化——ps:同类型运算结果不会变为其他类型
Float f;
若如此赋值:f = 1/2;//结果会是0.000000
再例如f = 3/2;//结果会是1.000000
也就是这两个整型数除出来得到的还是整型数,即先得到这个整数0和1再进行进制转换。
情景4——字符型遇到整型会自动转换为整型
当高精度往低精度变量赋值时,自己会阉了自己的高精度部分
强制类型转化——将数据转化成为指定类型的数据
格式: (类型) 数据或者语句
int i;
(float) i;//将整型i强制转换为float型
注意事项:
高精度往低精度转化的时候会丢失高精度数据
等号右边的东西可以强制类型转换,但左值不可以!
常见作用场景:
1,需要阉割高精度部分的时候
2,为了消除警告,可能会用到强制类型转换
但是,温馨提示一下,除非你有信心这里不会有问题,不然不要这样消除警告,例如:强制类型转化一块只读内存变成可读,之后一旦不小心更改这块内存的数据,你的代码就会崩溃
一些变量定义的例子
int a = 100;
int *b = &a;
int **c = &b;
int d[10];
d[0] = 100;
d[1] = a;
d[2] = *b;
d[3] = **c;
int *e[10];//指针数组 ,一个有十个指针的数组,且该指针指向一个整型数
e[0] = &a;
e[1] = b;
e[2] = *c;
e[3] = d;
int (*f)[10];//数组指针,一个指向有10个整型数组的指针
f = &d;
long (*array[2])[10];//数组指针数组,每个数组元素存放long [10]型的元素的地址
long a[10];
array[1] = &a;
(以上所有内容仅为个人学习所做笔记,如有疏漏、错误,欢迎你的指正!感谢你的阅读!)