6.2 C99标准
C 语言标准C99
提供了_Bool 型,_Bool 仍是整数类型,但与一般整型不同的是,_Bool 变量只能赋值为 0 或 1,非 0 的值都会被存储为 1,C99 还提供了一个头文件 <stdbool.h> 定义了 bool 代表_Bool,true 代表 1,false 代表 0。只要导入 stdbool.h ,就能方便的操作布尔类型了。
📝 下述代码说明 bool类型 用法:
#include<stdio.h> #include<stdbool.h> int main() { //bool类型所占字节大小 printf("bool类型所占字节大小:%lld\n\n", sizeof(bool)); // false对应整数 bool a = false; printf("false对应整数:%d\n\n", a); //输出说明 false 对应 0 //true对应整数 bool b = true; printf("true对应整数:%d\n\n", b); //输出说明 true 对应 1 if (true) printf("我曾经很爱很爱你,但再也不会去找你了,夜莺\n\n"); if (!false)//false取反为true printf("爱一个人,九分喜欢,一分尊严\n\n"); return 0; }
7.变量与常量
7.1 变量
7.1.1 基本概念
变量相当于内存中一个数据存储空间的表示,可以把变量看做是一个房间的门牌号,通过门牌号可以找到房间,而通过变量名就可以访问到变量值。
7.1.2 三个基本要素:类型 变量名 变量值
不论是使用哪种高级程序语言编写程序,变量都是其程序的基本组成单位。
/* 类型:int 变量名:a 变量值:100 */ int a = 100;
7.1.3 变量的声明与定义
声明
是用来告诉编译器变量的名称和类型,而不分配内存。定义
是为了给变量分配内存,可以为变量赋初值。
#include<stdio.h> int main() { int a; //变量声明,未分配内存空间 a = 520; //变量定义,分配内存空间 int b = 1314; //变量声明并定义(也叫 初始化) return 0; }
7.1.4 变量使用注意事项
- 变量表示内存中的一个存储区域。不同的变量,类型不同,占用的空间大小也可能不同。
- 该区域有自己的名称(变量名)和类型(数据类型)
- 变量必须先声明,再定义使用。
- 该区域的数据值可以在
同一类型
取值范围内不断变化。 - 变量在同一作用域内不能重名。
- 变量 = 数据类型 + 变量名 + 变量值
- 数据类型只在定义变量时指明,而且必须指明;使用变量时无需再指明,因为此时的数据类型已经确定了。
7.2 常量
7.2.1 基本概念
- 常量是固定值,在程序执行期间不能改变。这些固定的值,又叫做字面量。
- 常量可以是任何的基本数据类型,比如整数常量、浮点常量、字符常量,或字符串字面值,也有枚举常量。
- 常量的值在定义后不能进行修改
7.2.2 常量举例
//100就是常量 int a = 100; // 13.14就是常量 double b = 13.14; // A就是常量 char c = 'A';
7.2.3 define定义常量
凡是以 # 开头的均为预处理指令,预处理又叫预编译。预编译不是编译,而是编译前的处理。这个操作是在正式编译之前由系统自动完成的。
📍 C 语言中,可以用 #define
定义一个标识符来表示一个常量,用 #define 定义标识符的一般形式为:
#define 标识符 常量值 //注意define最后没有分号 //例如: #define MAX_VALUE 100 //定义整型变量MAX_VALUE值为100 #define USER_NAME "huge" //定义字符串变量USER_NAME值为"huge" #define PI 3.1415926 //定义浮点数变量PI值为3.1415926
📝 举例说明:计算半径为2的圆的面积
#include<stdio.h> //定义一个常量PI,值为3.14 #define PI 3.14 int main() { int r = 2; printf("S = %lf", PI * r * r); return 0; }
7.2.4 const定义常量
关键字const用来定义常量,如果一个变量被const修饰,那么它的值就不能再被改变,我想一定有人有这样的疑问,C语言中不是有#define吗,干嘛还要用const呢,我想事物的存在一定有它自己的道理,所以说const的存在一定有它的合理性,与预编译指令相比,const修饰符有以下的优点:
- 预编译指令只是对值进行简单的替换,不能进行类型检查
- 可以保护被修饰的东西,防止意外修改,增强程序的健壮性
- 编译器通常不为普通const常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有了存储与读内存的操作,使得它的效率也很高。
📍 基本语法:
const 数据类型 常量名 = 常量值;
📝 举例说明:计算半径为2的圆的周长
#include<stdio.h> //定义全局常量 PI,值为3.14 const double PI = 3.14; int main() { //定义局部常量 r ,值为2 const int r = 2; printf("C = %lf", 2 * PI * r); return 0; }
7.2.5 补充:#define与const区别
- const定义的常量时带类型,#define不带类型。
- const是在编译、运行的时候起作用,而define是在编译的预处理阶段起作用。
- #define 只是简单的替换,没有类型检查。简单的字符串替换会导致边界效应。
- const常量可以进行调试的,#define是不能进行调试的,主要是预编译阶段就已经替换掉了,调试的时候就没它了。
- const不能重定义, 不可以定义两个一样的,而#define通 过
undef
取消某个符号的定义,再重新定义。 - #define 可以配合
#ifdef
、#ifndef
、#endif
来使用,可 以让代码更加灵活,比如我们可以通过#define来启动或者关闭调试信息。
7.2.6 注意事项和细节说明
- 常量不能作为左值。例如:
// 常量100作为右值,是可以的 int a = 100; // 常量100作为左值,是不可以的,会报错 100 = 20; //将20赋值给100,很明显是一个荒谬的行为
- 常量一旦确定,就无法更改。
const int a = 520; a = 1314; //由于有关键字const修饰,a是常量,因此直接把1314赋值给a会报错。
学到了指针小伙伴可能会提出不同的意见,即使定义为常量,也可以通过指针修改值。
但你必须思考这样一件事情:为什么要定义常量?不就是告诉自己与其他程序员不要去修改这个值吗?只需要将它作为一个常量看待即可。这才是常量存在的意义,是const存在的意义,因此,如果你将一个变量用const修饰,又用指针去修改它,这本身就是一个荒谬的事情!
- 常量必须一开始就赋值,即声明时完成初始化。
//声明并初始化是可以的 const int a = 520; //先声明,后复制是不可以的,如下代码是错误的 const int b; b = 1314;
8.数据类型转换
数据类型转换就是将数据(变量、数值、表达式的结果等)从一种类型转换为另一种类型。
8.1 自动类型转换
自动类型转换就是编译器默默地、隐式地、偷偷地进行的数据类型转换,这种转换不需要程序员干预,会自动发生。
在赋值运算中,赋值号两边的数据类型不同时,需要把右边表达式的类型转换为左边变量的类型,这可能会导致数据失真,或者精度降低;所以说,自动类型转换并不一定是安全的。对于不安全的类型转换,编译器一般会给出警告。
// 520是int类型常数,因此这里进行了自动类型转换,将int类型的520变成了float类型的520.000000 float a =520;
📍 自动类型转换规则如下:
- 运算结果的类型是由参与运算的精度最高的那个数据类型所决定。
比如说 1/2 ,这两个数都是整型,因此参与运算的最高精度 的数据类型也是整型,因此运算结果也是整型,即会去除小数位,0.5去除小数位就是0,即 1/2 的结果为0。 - 参与运算的类型不同,则先转换为同一类型。
int r = 2; double pi = 3.14; /* 1.在计算 pi * r * r时,会先将这三个变量值转换为数据类型一致的值 2.pi为double,r为int,根据上图可以得到double的精度更高 3.因此 r 的变量值2也会先转成double类型的2.000000,再进行计算 4.所以 3.140000 * 2.000000 * 2.000000 得到12.560000 5.但接收类型为int,因此会舍去小数部分,则s为12 */ int s = pi * r * r;
- 两种类型字节数不同,转换为高字节数的类型
- 转换按数据长度增加的方向进行,以保证精度不降低。如int型和double型运算时,先把int量转成double型后再进行运算。
/* 1. 在运算9.4 / 2前 2. 发现9.4是double类型(默认),2是int类型 3. 根据自动转换规则,将int类型的2转成double类型的2.000000 4. 因此运算的结果为 4.700000 5. 又发现了接收类型为Int,因此舍去小数部分得到 a 为 4 */ int a = 9.4 / 2;
- 若两种类型的字节数相同,且一种有符号,一种无符号,则转换成无符号类型。因此,从这个意义上讲,无符号数的运算优先级要高于有符号数,这一点对于应当频繁用到无符号数据类型的嵌入式系统来说是丰常重要的。
- 所有的浮点运算都是以双精度进行的,即使仅含float单精度量运算的表达式,也要先转换成double型,再作运算。
- char型和short型参与运算时,必须先转换成int型
char a = 'A'; char b = 'B'; /* 1.运算 a+b 前,发现a和b是字符型,因此先转为int类型 2.'A' --> 65 'B' --> 66 3.所以 a+b 的结果为 131 4.发现 c 是 char类型,因此又会自动转为131对应的ASCII字符为【问号?】 */ char c = a + b; short d = 1; /* 1.运算 d+a 前,发现d是short,a是char 2.因此将这两个都转成int类型 3.short类型的1转成int类型的1,char类型的a转成int类型的65 4.所以 d+a 即计算 1+65 --> 为int类型的66 5.发现接收类型是e,所以e为66,不需要类型转换了 */ int e = d + a;
8.2 强制类型转换
自动类型转换是编译器根据代码的上下文环境自行判断的结果,有时候并不是那么“智能”,不能满足所有的需求。如果需要,程序员也可以自己在代码中明确地提出要进行类型转换,这称为强制类型转换。
- 自动类型转换是编译器默默地、隐式地进行的一种类型转换,不需要在代码中体现出来;
- 强制类型转换是程序员明确提出的、需要通过特定格式的代码来指明的一种类型转换。
换句话说,自动类型转换不需要程序员干预,强制类型转换必须有程序员干预。
📍 强制类型转换的格式为:(type_name) expression
。type_name为新类型名称,expression为表达式。
(float) a; //将变量 a 转换为 float 类型 (int)(x+y); //把表达式 x+y 的结果转换为 int 整型 (float) 100; //将数值 100(默认为int类型)转换为 float 类型
📝 再举一个例子:
#include<stdio.h> int main() { double forever = 520.1314; //强制将520.1314转成int类型,就需要舍去小数部分,即变为520 int nowForMe = (int)forever; printf("其实我很想 %.4lf,但是现在我却只能做到 %d", forever, nowForMe); return 0; }
8.3 类型转换只是临时性的
无论是自动类型转换还是强制类型转换,都只是为了本次运算而进行的临时性转换,转换的结果也会保存到临时的内存空间,不会改变数据本来的类型或者值。
float a = 13.14; /* 这里进行了强制类型转换,首先由于()的优先级别高于 * ,因此先进行(int)a 得到该值为13 然后 13 * 10 得到 130,所以 b 为 130 但需要注意:虽然a在运算中转成了int类型,但实际上a依旧为 float 类型 */ int b = (int)a * 10;
8.4 自动类型转换 VS 强制类型转换
在C语言中,有些类型既可以自动转换,也可以强制转换,例如 int 到 double,float 到 int 等;而有些类型只能强制转换,不能自动转换,例如以后将要学到的 void * 到 int *,int 到 char * 等。
可以自动转换的类型一定能够强制转换,但是,需要强制转换的类型不一定能够自动转换。现在我们学到的数据类型,既可以自动转换,又可以强制转换,以后我们还会学到一些只能强制转换而不能自动转换的类型。
可以自动进行的类型转换一般风险较低,不会对程序带来严重的后果,例如,int 到 double 没有什么缺点,float 到 int 顶多是数值失真。只能强制进行的类型转换一般风险较高,或者行为匪夷所思,例如,char * 到 int * 就是很奇怪的一种转换,这会导致取得的值也很奇怪,再如,int 到 char * 就是风险极高的一种转换,一般会导致程序崩溃。
使用强制类型转换时,程序员自己要意识到潜在的风险。
🔖 小结
💬 最后再啰嗦一句:当格式控制符(占位符)
和数据类型
不匹配时,编译器会给出警告,提示程序员可能会存在风险。编译器的警告是分等级的,不同程度的风险被划分成了不同的警告等级,而使用%d输出 short 和 long 类型的风险较低,如果你的编译器设置只对较高风险的操作发出警告,那么此处你就看不到警告信息。
💬 遇到任何疑惑请联系软件协会。
🏠 了解更多关注软协官网:https://www.csuftsap.cn/
💚 来自软件协会编辑,注册会员即可获取全部开源.md资源,请勿转载,归软件协会所有。