生产类营销型网站,苏州网站制作搭建,网站报价表对比表怎么做,免费空间和域名C语言#xff1a;预处理 预定义符号#define定义常量定义宏宏与函数对比 #操作符##操作符条件编译头文件包含库文件包含本地文件包含嵌套文件包含 预定义符号
C语⾔设置了⼀些预定义符号#xff0c;可以直接使⽤#xff0c;预定义符号也是在预处理期间处理的。
__FILE__ //… C语言预处理 预定义符号#define定义常量定义宏宏与函数对比 #操作符##操作符条件编译头文件包含库文件包含本地文件包含嵌套文件包含 预定义符号
C语⾔设置了⼀些预定义符号可以直接使⽤预定义符号也是在预处理期间处理的。
__FILE__ //进⾏编译的源⽂件
__LINE__ //⽂件当前的⾏号
__DATE__ //⽂件被编译的⽇期
__TIME__ //⽂件被编译的时间
__STDC__ //如果编译器遵循ANSI C其值为1否则未定义示例
printf(file:%s line:%d\n, __FILE__, __LINE__);输出结果
file:test.c line:1以上预定义符号都是一些常量可以自己一一尝试。 #define
在C语言中#define是一个预处理器指令用于定义宏。
宏是一个被预处理器替换的标识符或一个标识符和参数的组合。宏定义可以用来简化代码、提高代码的可读性和可维护性。
使用#define可以定义常量、函数宏等。
定义常量
定义常量 #define PI 3.14这样就可以在代码中使用PI来代表3.14。
定义关键字
#define reg register将 reg 定义为关键字 register可以将reg这个简写代替register关键字使用。
定义代码段
#define CASE break;case正常的switch语句每一个case都要加上break通过这个写法我们可以在写CASE时自动补齐break。 定义宏
#define 机制允许把参数替换到文本中这种功能叫做 宏(macro) / 定义宏(define macro)
语法
#define name(parament-list) stuff其中的 parament-list 是⼀个由逗号隔开的符号表它们可能出现在stuff中。
示例
#define SQUARE(x) x * x当我们在代码中输入以下代码
int main()
{int a 5;int b SQUARE(a);return 0;
}在编译后就会转化为
int main()
{int a 5;int b a * a;return 0;
}也就是直接发生了文本替换这种宏的形式非常像函数因此也可以称为宏函数。但是其也有很多需要注意的地方。
比如以下代码
int a 5;
int b SQUARE(a 1);我们希望先执行a 1然后再传入SQUARE中但是其不会这样做因为其会将上述代码直接替换为
int a 5;
int b a 1 * a 1;由于操作符优先级的问题我们不会得到想要的结果。为了处理这个情况我们需要把参数用小括号括起来
#define SQUARE(x) (x) * (x)代码就变成
int a 5;
int b (a 1) * (a 1);这样我们就可以行使预期的功能了。
那么我们再看到一串代码
#define DOUBLE(x) (x) (x)int a 5;
int b 10 * DOUBLE(a);代码编译后为
int a 5;
int b 10 * 5 5; 又出现了一样的问题我们的 10 * DOUBLE(a)并没有先执行DOUBLE(a)而展开后又出现了操作符优先级问题所以我们的宏还要再优化
#define DOUBLE(x) ((x) (x))在宏的最外侧再加一层括号就可以独立运行不受外界操作符影响了。
通过以上推断我们可以发现宏虽然可以很好的替换代码但是会受到外界操作符的影响此时就要注意很多细节。 宏与函数对比 函数在调用时是会开辟内存创建栈帧的而宏则直接执行所以速度更快。但是由于宏是在编译阶段就已经处理好了所以宏不能通过调试观察现象还要操作符优先级带来的种种问题。因此宏不适合处理复杂的函数但是很适合短小简单的函数。 #操作符
功能 #可以将宏的参数转化为字符串 比如以下代码
#define PRINT(n) printf(the value of #n is %d, n);我们尝试调用
int a 5;
PRINT(a);代码就会被转化为
int a 5;
printf(the value of a is %d, a);可以看到两个n的替换效果是不同的对于n其会直接被替换为变量a而对于#n其不是简单的替换而是把参数名转化为了字符串”a“。 ##操作符 ##操纵符可以将两个符号合并为一个符号 示例
#define GENERIC_MAX(type) \
type type##_max(type x, type y)\
{ \
return (xy?x:y); \
}该宏用于创建不同类型的比大小函数由于不同函数需要不同的函数名于是利用##来连接函数名也就是type##_max部分。type是一个宏参数当传入floattype##_max整体就被连接为float_max当传入int整体就被连接为int_max。也就是##起到一个连接作用。 条件编译
条件编译是C语言中的一种编译指令用于在编译过程中根据指定的条件选择性地包含或排除某些代码。它主要是为了满足不同平台、不同编译选项或不同场景下的需求。
条件编译使用预处理指令实现预处理指令以#开头。下面是一些常用的条件编译指令及其用法
#if / #elif / #else / #endif #if用于基于预处理器常量的值进行条件判断。 #elif用于在多个条件之间进行选择。 #else用于在没有匹配的#if或#elif时执行。 #endif用于结束条件编译块。 示例 #define NUM 5#if NUM 10printf(NUM is greater than 10\n);#elif NUM 0printf(NUM is greater than 0\n);#elseprintf(NUM is less than or equal to 0\n);#endif这个代码和C语言的if代码很像不过多讲解了。
#ifdef / #ifndef #ifdef用于检查一个标识符是否已经定义如果已定义则编译后面的代码否则跳过。 #ifndef与#ifdef相反用于检查一个标识符是否未定义。 示例
#ifdef DEBUGprintf(Debug mode enabled\n);
#endif以上代码中只要我们定义了DEBUG这个变量就会输出Debug mode enabled\n语句。 #define #define用于定义宏。宏是一种将一组指令作为一个整体进行替换的方式。 示例 #define MAX(a, b) ((a) (b) ? (a) : (b))int x 10;
int y 20;
int max MAX(x, y);#include #include用于将指定的头文件包含到当前文件中。 示例 #include stdio.hint main() {printf(Hello, World!\n);return 0;
}#pragma #pragma用于向编译器发出特定的指令如优化选项、警告控制等。它的语法和功能因编译器而异。 示例 #pragma warning(disable: 4996)这些是C语言中常用的条件编译指令和代码用法。通过合理使用条件编译我们可以根据不同的需求自由地控制代码的编译过程。 头文件包含
头文件包含分两种形式本地头文件与库文件。
库文件包含
语法
#include filename.h查找头⽂件会直接去标准路径下去查找如果找不到就提⽰编译错误 我们平常使用的库文件都通过尖括号来包含其会直接到存放库文件的路径中查找。
本地文件包含
#include filename先在源⽂件所在⽬录下查找如果该头⽂件未找到编译器就像查找库函数头⽂件⼀样在标准位置查找头⽂件如果找不到就提⽰编译错误 如果是用户自己编写的头文件我们要用双引号包含如果通过这种方式包含头文件那么会先在当前源文件的目录下查找如果没有找到再去库文件中查找。
也就是说库文件也可以通过双引号包含但是会多出额外的查找步骤所以库文件还是用尖括号包含更好而自己编写的头文件必须双引号包含。
嵌套文件包含
假设我们现在有以下文件结构 头文件test.h
void test();
struct Stu
{int id;char name[20];
};源文件test.c
#include test.h
#include test.h
#include test.h
#include test.h
#include test.h
int main()
{return 0;
}我们在test.c中多次包含了头文件这就会导致test.h反复被展开产生大量重复代码。这就是嵌套文件包含的问题那么我们要如何处理这个问题让其只能被包含一次呢
条件编译方法 在头文件test.h中加入以下代码
#ifndef __TEST_H__
#define __TEST_H__
//头⽂件的内容
#endif //__TEST_H__第一次包含头文件 先执行#ifndef __TEST_H__我们此时没有定义__TEST_H__这个变量if成立此时头文件会被展开同时执行#define __TEST_H__此时__TEST_H__就已经被定义了 第二次包含头文件 第二次站时由于上一次展开已经定义了__TEST_H__这个变量导致#ifndef __TEST_H__判断为假此时整个头文件都不会再被编译直接舍弃 后续再展开头文件都会因为 __TEST_H__被定义而不会编译解决了嵌套编译的问题。
一般而言我们这个用于判断头文件有没有被展开过的变量是头文件名通过一定规则转化来的 在头文件前后加上两个下划线__头文件.h__把头文件中的点.也改为下划线__头文件_h__ 因此test.h的常量就是__TEST_H__。
pragma 通过条件编译其实是比较传统的写法我们还有一种更加简洁方便的写法
#pragma once只要在任何头文件前面加上这句话就只会被编译一次了。