目录
宏定义(#define)
概念
#define 叫做宏定义命令,它也是 C 语言预处理命令的一种。所谓宏定义,就是用一个标识符来表示一个字符串,如果在后面的代码中出现了该标识符,那么就全部替换成指定的字符串。
无参宏定义
宏定义的一般形式为:
#define 宏名 字符串
#表示这是一条预处理命令,都是预处理器来处理的,所有的预处理命令都以 # 开头。 宏名是标识符的一种,命名规则和变量相同。 字符串可以是数字、表达式、 if 语句、函数等。
这里所说的字符串是一般意义上的字符序列,不要和 C 语言中的字符串等同,它不需要双引号。
举个例子
一:
注意第 6 行代码 int sum = 20 + N, N 被 100 代替了。
#define N 100 就是宏定义, N 为宏名, 100 是宏的内容(宏所表示的字符串)。
原理:在预处理阶段,对程序中所有出现的“宏名”,预处理器都会用宏定义中的字符串去代换,这称为“宏替换”或“宏展开”。宏定义是由源程序中的宏定义命令#define 完成的,宏替换是由预处理程序完成的。
二:
注意在宏定义中表达式(nn+3n)两边的括号不能少,否则在宏展开以后可能会产生歧义
反面例子,
因为你定义的时候是什么样传进去就是什么样,凡是多项尽量都加上括号
#define 用法的几点说明
1 宏定义是用宏名来表示一个字符串,在宏展开时又以该字符串取代宏名,这只是一种简单粗暴的替换。字符串中可以含任何字符,它可以是常数、表达式、 if 语句、函数等,预处理程序对它不作任何检查,如有错误,只能在编 译已被宏展开后的源程序时发现。
2 宏定义不是说明或语句,在行末不必加分号,如加上分号则连分号也一起替换。
3 宏定义必须写在函数之外,其作用域为宏定义命令起到源程序结束。如要终止其作用域可使用#undef 命令。例
如:
4 代码中的宏名如果被引号包围,那么预处理程序不对其作宏代替,例如:
5.宏定义允许嵌套,在宏定义的字符串中可以使用已经定义的宏名,在宏展开时由预处理程序层层代换。例如:
6 习惯上宏名用大写字母表示,以便于与变量区别。但也允许用小写字母。
注意:用宏定义表示数据类型和用 typedef 定义数据说明符的区别:
宏定义只是简单的字符串替换,由预处理器来处理
而 typedef 是在编译阶段由编译器处理的,它并不是简单的字符串替换,而给原有的数据类型起一个新的名字,将它作为一种新的数据类型。
带参数的宏定义
举个例子:
带参宏定义的说明
2 在带参宏定义中,不会为形式参数分配内存,因此不必指明数据类型。
而在宏调用中,实参包含了具体的数据,要用它们去替换形参,因此实参必须要指明数据类型。这一点和函数是不同的:
在函数中,形参和实参是两个不同的变量,都有自己的作用域,调用时要把实参的值传递 给形参;
而在带参数的宏中,只是符号的替换,不存在值传递的问题。
这与函数的调用是不同的,函数调用时要把实参表达式的值求出来再传递给形参,而宏展开中对实参表达式不作计
算,直接按照原样替换。
3 括号问题
括中括
枚举类型(enum 关键字)
概念
在实际编程中,有些数据的取值往往是有限的,只能是非常少量的整数,并且最好为每个值都取一个名字,以方便
在后续代码中使用,比如一个星期只有七天,一年只有十二个月,一个班每周有六门课程等。
还有在学习stm32里面的寄存器,配置它的输出速度,输入输出模式,这些都是有限的取值,所以我们可以把它们所有的取值创建一个枚举,方便以后的编程,在看到相应的枚举成员名称就可以知晓其意思,而不至于看着一大堆的0和1发憷。
过度
在不知道枚举类型时,我们用所学宏定义来实现枚举功能
#define 命令虽然能解决问题,但也带来了不小的副作用,导致宏名过多,代码松散,看起来总有点不舒服。
C 语言提供了一种枚举(Enum)类型,能够列出所有可能的取值,并给它们取一个名字。
类型创建
注意成员之间是用逗号隔开,这点与其他自定义类型不一样
这里假如
类型定义,初始化
定义和其他自定义类型一样
常规定义
简便定义
赋值
枚举赋值不同其他类型,在构建定义的时候就对其赋值操作了,并赋值为常数。
假如这里的web=6,Thurs没有赋值,那默认就是前一数据的基础+1;
举个例子:
需要注意的两点是:
1 枚举列表中的 Mon、 Tues、 Wed 这些标识符的作用范围是全局的(严格来说是 main() 函数内部),不能再定义与它们名字相同的变量。
2 Mon、 Tues、 Wed 等都是常量,不能对它们赋值,只能将它的值赋给其他的变量。
枚举和宏其实非常类似:宏在预处理阶段将名字替换成对应的值,枚举在编译阶段将名字替换成对应的值。我们可以将枚举理解为编译阶段的宏
Mon、 Tues、 Wed 这些名字都被替换成了对应的数字。这意味着, Mon、 Tues、 Wed 等都不是变量,它们不占用数据区(常量区、全局数据区、栈区和堆区)的内存,而是直接被编译到命令里面,放到代码区,所以不能用&取得它们的地址。这就是枚举的本质。
case 关键字后面必须是一个整数,或者是结果为整数的表达式,但不能包含任何变量,正是由于 Mon、 Tues、 Wed 这些名字最终会被替换成一个整数,所以它们才能放在 case 后面
作为函数即返回值
typedef
存在的意义
C 语言允许为一个数据类型起一个新的别名,就像给人起“绰号”一样。最多情况就是原来替换自定义数据类型了。
用法
普通类型用法
数组类型用法
结构体类型
这里其实是把 struct stu{ char name[20]; int age;char sex;} 也就是我们构建的结构体换个名字,就像int类型换名字一样,这里就是把 struct stu{ char name[20]; int age;char sex;}当成int,这里的STU不是结构体定义啊。而是取的新名字
最常用结构体用法还是,匿名结构体用法
像这里 struct 也像当于int 。
原本要定义变量需特殊定义,
指针和函数类型
需要强调的是, typedef 是赋予现有类型一个新的名字,而不是创建新的类型。为了“见名知意”,请尽量使用含义明确的标识符,并且尽量大写。
typedef 和 #define 的区别
typedef 在表现上有时候类似于 #define,但它和宏替换之间存在一个关键性的区别。正确思考这个问题的方法就是把 typedef 看成一种彻底的“封装”类型,声明之后不能再往里面增加别的东西。
2 在连续定义几个变量的时候, typedef 能够保证定义的所有变量均为同一类型,而 #define 则无法保证。这个在#define定义数据类型的时候有提到。