一、预处理指令#define
cs
#define叫做宏定义,语法格式:
#define 名字 值
#define PI 3.14159
- 结尾没有分号;
- 和#include一样,在预处理阶段执行,文本替换
- 值可以是数字、表达式、代码语句等
- 宏定义的好处,便于程序的阅读和维护
cs
#define MAX 50
#define M 3+2
#define N (3+2)
#define NULL 0
#define EOF -1
#define ERROR -1
- 定义一个宏名字之后,可以在其他宏定义中使用,例如
cs
#define ONE 1
#define TWO ONE+ONE
#define THREE ONE+TWO
二、没有值的宏定义
- #include是一个预处理指令,预处理这个动作发生在编译之前:
- #include 的作用是,在预处理时,将文件中的全部文本内容复制粘贴到#include所在的位置
1、#define 名字
这种宏定义,是用于条件编译的,配合其它的预处理指令来检测这个宏是否被定义过
cs
#define DEBUG
#ifdef DEBUG
printf("%s:This error is in \"%s\" on line %d.\n", __FUNCTION__, ___FILE___, ___LINE___);
#endif
当运行时,这三个宏分别能返回所在的函数,所在的文件名和所在的行号。
2、#include预处理指令
1)<>和""有什么区别
cs
#include <stdio.h>
#include "head.h"
- 使用尖括号<>,编译器会到标准库路径下查找头文件/usr/include
- 使用双引号"",编译器首先在当前目录下查找头文件,如果没有找到,再到标准库路径下查找
2)规范用法
- 标准库的头文件使用尖括号<>
- 自定义的头文件使用双引号""
3)宏定义指令
cs
#define、#undef
三、宏定义和 const 常量区别
1、 定义的区别
- 宏用 #define 声明,const 常量用 const + 数据类型 声明。
- 宏最后没用分号,const 常量声明需要用分号表示语句结束。
- 宏不需要用等号赋值,cosnt 常量需要用等号赋值。
2 、处理阶段阶段的不同
- 宏定义在预处理阶段进行文本替换。
- const 常量在程序运行时使用。
3 、存储方式不同
- 宏定义是直接替换,不会分配内存,存储于程序的代码段中。
- const 常量需要进行内存分配。
4 、是否进行类型检查
- 宏定义是字符替换,不进行类型检查。
- const 常量定义时需要声明数据类型,使用时会进行类型检测。
5 、宏定义可以声明函数
6、定义后能否取消
-
宏定义可以通过#undef来使之前的宏定义失效
-
const常量定义后将在定义域内永久有效
csvoid f1() { #define N 12 const int n = 12; #undef N //取消宏定义后,即使在f1函数中,N也无效了 #define N 21//取消后可以重新定义 }
四、typedef的用法
1、 什么是typedef
- typedef是在C和C++编程语言中的一个关键字。作用是为现有的数据类型(int、float、char......)创建一个新的名字
- 目的是为了使代码方便阅读和理解。
2、 typedef用法
1)对于数据类型使用
cs
typedef int Integer;
- 以上就是给int起了一个新的名字Integer,注意要加分号。当要定义int类型数据时就可以:
cs
Integer num;
- 此时Integer num 等同于 int num。
2)对于指针的使用
cs
typedef int * PTRINT;
- 以上就是给int *起了一个新的名字NEW_INT。可定义int类型指针变量如:
cs
PTRINT x;
- 此时PTRINT x等同于int *x。
3)对于结构体的使用
- 在声明结构体时可为结构体和结构体指针起别名,如:
cs
typedef struct NUM
{
int a;
int b;
}DATA,*PTRDATA;
- 此时DATA等同于struct NUM,PTRDATA等同于struct NUM *。
3、进阶typedef
1)数组指针
cs
int (*ptr)[3];
使用Typedef:
typedef int (*PTR_TO_ARRAY)[3];
举个栗子:
cs
#include <stdio.h>
typedef int (*ptr_to_arr)[3];
int main(int argc, const char *argv[]) {
int i;
int temp[3] = {1, 5, 8};
ptr_to_arr p;
p = &temp;
for (i = 0; i < 3; i++) {
printf("%d\n", (*p)[i]);
}
return 0;
}
2)函数指针
cs
int (*fun)(int);
使用Typedef:
typedef int (*PTR_TO_FUN)(int);
举个栗子:
cs
#include <stdio.h>
typedef int (*ptr_to_fun)(int);
int fun(int n) {
int i, sum = 0;
for (i = 1; i <= n; i++) {
sum += i;
}
return sum;
}
int main(int argc, const char *argv[]) {
ptr_to_fun p;
p = fun;
printf("%d\n", (*p)(100));
return 0;
}
五、#define和typedef的比较
1、语法形式不同
- typedef 是 C语言的关键字,用于创建类型别名,它需要使用标识符和现有的类型进行配合;
- #define 是预处理指令,用于创建宏定义,它可以定义任意的标识符和文本替换。
- 【注意】typedef 定义是语句,因为句尾要加上分号,而 #define不是语句,千万不能在句尾加分号。
2、执行时间不同
- 关键字typedef在编译阶段有效,有类型检查的功能。
- #define则是宏定义,发生在预处理阶段,也就是编译之前,它只进行简单而机械的文本替换,而不进行任何检查。
3、#define和typedef的比较
cs
typedef int *PTR;
PTR a,b;
此时a,b都是指针变量。
#define PTR int*
PTR a,b;
此时等同于
int *a,b;
只有a为指针变量,而b为整型变量。
4、作用域不同
1)typedef 有作用域限定
- 如果放在所有函数之外,它的作用域就是从它定义开始直到文件尾;
- 如果放在某个函数内,定义域就是从定义开始直到该函数结尾;
2)#define 不受作用域约束,只要是在 #define声明后的引用都是正确的
- 不管是在某个函数内,还是在所有函数之外,作用域都是从定义开始直到整个文件结尾。
cs
#include <stdio.h>
//typedef int (*ptr_to_fun)();
int fun(int n) {
#define uchar unsigned char
typedef int (*ptr_to_fun)();
int i, sum = 0;
for (i = 1; i <= n; i++) {
sum += i;
}
return sum;
}
int main(int argc, const char *argv[])
{
ptr_to_fun p;
p = fun;
uchar ch = 'a';
putchar(ch);
putchar('\n');
printf("%d\n", (*p)(100));
return 0;
}
- 总结:
- 不管是typedef还是define,都不能在定义之前使用;
- typedef受函数范围影响,而define不受;
六、宏函数的使用
1、无参宏
cs
#define debug printf("hello world")
int main() {
debug;
return 0;
}
2、带参宏
1)语法
- #define 宏名(形参列表) 字符串
- 不是进行简单的字符串替换,还要进行参数替换
cs
#define debug(s) printf("%s\n", s)
int main() {
debug("helloworld"); //debug(1)保持
return 0;
}
2)举个栗子
cs
#include<stdio.h>
#define SQUARE(n) (n)*(n)
int main()
{
int n = 0;
scanf("%d", &n);
printf("%d\n", SQUARE(n));
return 0;
}
cs
#define PI 3.1415926
//#define S(r) PI*r*r
//如果传两个参数就会变成,s=3.1415926*a+b*a+b;这样运算的优先级会出问题
//如果要使运算的优先级没有问题则写成下面的这种形式
#define S(r) PI*(r)*(r)
int main(int argc, const char *argv[])
{
float a = 2;
float b = 3;
float s = 0;
s=S(a+b);
printf("sum=%f\n ",s);
return 0;
}
3、宏函数的作用
cs
#define MAX(a,b) ((a) > (b) ? (a) : (b))
- 和函数不同,宏的参数没有数据类型,因为是文本展开
- 因为是文本展开,相比函数没有执行调度的开销,效率要高
- 使用有参的宏函数时,参数再替换文字中要用括号包围,以免受到运算符优先级的影响
4、 函数实现-两个整数相加
cs
#include <stdio.h>
int ADD(int x,int y){
return x + y;
}
int main(){
int a = 10;
int b = 20;
int sum = ADD(a,b);
printf("%d\n",sum);
return 0;
}
cs
#include<stdio.h>
// ADD是宏名,(x,y)是宏的参数但无类型,((x)+(y))是宏体
#define ADD(x,y) ((x)+(y))
int main() {
int a = 10;
int b = 20;
int sum = ADD(a, b);//int sum = ((a)+(b))
//宏是替换的,有替换作用
printf("%d\n", sum);//打印结果:30
return 0;
}
七、#define定义的宏和函数的区别
- 宏在调用时的效率是比函数高很多的;
- 函数的参数是有类型的,也存在类型检查。但宏的参数是没有类型与类型检查的;
- 函数可以递归,而宏不可以递归;
- 函数方便调试,而宏是不方便调试的;
- 对于参数而言,宏的参数是直接替换的,所以会有一些参数具有副作用。而函数的参数是临时拷贝的,没有副作用的情况;