预处理指令
宏定义
-
宏定义会在预处理的时候, 用宏定义的值来替换宏的名称
-
格式: #define 宏名称 宏值
-
应用场景:
-
企业开发分为开发阶段和部署阶段
-
例如在程序开发中会用到很多的地址
-
-
注意点:
-
1.宏定义的后面不要写分号
-
因为宏定义是单纯的替换, 会用宏的值替换宏名
-
2.宏定义分为两种, 一种是不带参数的宏定义, 一种是带参数的宏定义
-
不带参数的宏定义
#define COUNT 5
int main()
{
int ages[COUNT] = {1, 3, 5, 7, 9};
// 除了可以动态计算数组的长度以外, 还可以利用宏定义
// int len = sizeof(ages) / sizeof(ages[1]);
// for(int i = 0; i < len; i++){
for(int i = 0; i < COUNT; i++){
printf("ages[%i] = %i\n", i, ages[i]);
}
return 0;
}
带参数的宏定义
格式: #define 宏名称(参数) 参数
#define SUM(a, b) a+b
int main()
{
int num1 = 10;
int num2 = 20;
int res = SUM(num1, num2);
printf("res = %i\n", res);
return 0;
}
-
选择:
-
如果函数的业务逻辑非常简单, 建议使用宏定义,因为宏定义的效率更高(原因:1.宏定义是在预处理的时候就执行了, 而函数是在运行的时候才会执行.2.宏定义只会做简单的替换, 而函数还需要分配内存空间)
-
如果业务逻辑比较复杂, 那么还是建议使用函数
-
-
注意点:
-
1.宏的值的每个参数都需要加上()
-
2.宏的值的结果也需要加上()
-
#define JF(a) ((a)+(a))
int main()
{
int num = 10;
int res = JF(5 + 5) / JF(2 + 2);
printf("res = %i\n", res);
return 0;
}
宏定义的作用域
-
宏定义的作用域和全局变量很像, 但是可以通过#undef宏名提前结束
-
作用域是从定义的那一行开始, 直到文件末尾
条件编译
- 格式:
#if
#else
#endif
-
条件编译和if else区别
1.if else语句是在程序运行的时候执行的而#if #else #endif是在预处理的时候执行的
2.if else语句所有的代码都会编译到程序中而#if #else #endif中只有满足条件的语句才会编译到程序中 -
注意点:
1.---> #if 和 #endif 可以组成一对 #if #else #endif 可以组成一对 #if #elif #else #endif 可以组成一对, 其中#elif可以有一个或对个
2.--->无论怎么组合#endif都不可以省略
3.--->条件编译中不能获取变量的值
4.--->条件编译一般是配合宏定义来使用,因为宏定义和条件编译都是在预处理的时候执行的
#include <stdio.h>
/*
#define DEBUG 0
#if DEBUG == 1
#define URL "127.0.0.1"
#else
#define URL "www.it666.com"
#endif
*/
#define DEBUG 1
#if DEBUG == 1
#define NJLOG(format, ...) printf(format, ##__VA_ARGS__)
#else
#define NJLOG(format, ...)
#endif
int main()
{
/*
* 在开发阶段, 我们经常使用打印的形式来调试程序
* 但是打印其实是非常消耗性能的, 所以在部署阶段都需要去除打印
*/
// printf("%s\n", URL);
// printf("%s\n", URL);
// printf("%s\n", URL);
// printf("%s\n", URL);
// printf("%s\n", URL);
for(int i = 0; i < 100; i++){
NJLOG("i = %i\n", i);
}
return 0;
}
- 第二种格式:
* #ifdef #else #endif
* #ifdef作用: 判断是否定义某一个宏
*
* #ifndef #else #endif
* #ifndef作用: 判断是否没有定义某一个宏
文件包含
1.--->#include <>
<> 会先从编译器的环境中查找对应的文件, 如果没有再从系统的环境中查找对应的文件
2.--->#include ""
"" 会先从当前项目环境中查找对应的文件, 如果没有再从编译器的环境中查找对应的文件, 如果还没有再从系统的环境中查找对应的文件
3.---> #include作用:
将指定文件中的代码原方不动的拷贝到#include的位置
- 注意点:
1.已知函数的定义不可以重复, 但是函数的声明可以重复
2.但是如果重复声明函数, 或者重复导入.h文件会影响编译器的编译效率
3.所以在C语言中引入了头文件卫士的概念, 专门用于解决重复导入
4.不要出现循环包含(A文件包含B文件, B文件又包含A文件)
typedef
-
作用:给数据类型起别名
-
格式:typedef 原来的类型名称 新的类型名称;
-
注意点:
-
typedef相当于给人起了一个外号, 人还是那个人, 只不过多了一个名称而已
-
所以typedef不是定义一个新的数据类型, 而是定义一个新的名称而已
-
typedef int ZHENGSHU;
// 2.给别名再起别名
typedef ZHENGSHU DOUBI;
int num = 9;
printf("num = %i\n", num);
ZHENGSHU value = 666;
printf("value = %i\n", value);
DOUBI i = 888;
printf("i = %i\n", i);
ZS j = 123;
printf("j = %i\n", j);
-
应用场景
-
typedef一般用于给复杂的数据类型起别名, 方便将来使用
- 结构体
// 2.给结构体起别名
struct Person{
String name;
int age;
};
// 2.1先定义结构体类型, 再给结构体类型起别名
typedef struct Person ps;
struct Person p;
// 2.2定义结构体类型的同时, 给结构体类型起别名
typedef struct Person{
String name;
int age;
} ps;
ps p = {"lnj", 58};
// 2.3定义结构体类型的同时, 给结构体类型起别名, 并且省略原有类型的名称
// 企业开发的写法
typedef struct{
String name;
int age;
} Person;
// struct Person p = {"lnj", 58};
Person p = {"lnj", 58};
printf("name = %s\n", p.name);
- 指针
typedef char* String;
String name = "it666";
printf("name = %s\n", name);
- 指向函数的指针
// 注意点: 如果给指向函数的指针起别名, 那么指针名称就是别名
// 企业开发经常使用, 随处可见
typedef int (*funP)(int, int);
funP p1 = ∑
funP p2 = −
typdef和宏定义的区别
如果是要给数据类型起别名, 那么一律用typedef即可
注意点:
#define ZIFUCHUAN char*
// ZIFUCHUAN n3, n4;
// char* n3, n4;
// 只有n3是指针, 而n4是字符
ZIFUCHUAN n3, n4;
//而n1,n2都是指针
typedef char* String;
// String name2 = "zs";
String n1, n2;
n1 = "zs";
n2 = "ww";
printf("n1 = %s\n", n1);
printf("n2 = %s\n", n2);
const
-
const 格式: const 数据类型 变量名称;
-
注意点:
- const可以写在数据类型的前面, 也可以写在数据类型的后面
- 对于基本数据类型来说, 写在数据类型的前面和后面没有任何区别
- 但是对于指针来说, 写在数据类型的前面和后面就有区别了
-
const和指针
- const如果写在指针数据类型左边, 那么代表指针指向的内存空间不能修改, 但是指针的指向可以修改
- const如果写在指针数据类型右边, 那么代表指针指向的内存空间不能修改, 但是指针的指向可以修改
- const如果写在指针星号右边, 那么代表指针指向的内存空间可以修改, 但是指针的指向不可以修改
网友评论