C语言预处理(预定义符号,#define,宏,#和##,宏与函数的对比,条件编译,文件包含)

发布于:2022-12-07 ⋅ 阅读:(967) ⋅ 点赞:(0)

#define _CRT_SECURE_NO_WARNINGS 
#include<stdio.h>
1、预定义符号

int main()
{
	//这些预定义符号都是C语言内置的
	printf("%s\n", __FILE__);//打印当前文件所处的路径
	printf("%d\n", __LINE__);//打印当前行的行数
	printf("%s\n", __DATE__);//打印当前的日期
	printf("%s\n", __TIME__);//打印现在的时间
	return 0;
}

2、#define 定义的标识符(#define不止可以定义整形,还可以定义字符串等类型)
注:如果定义的stuff过长,可以分成几行写,除了最后一行外,每行的后面都可以加一个反斜杠(续航符)

#define DEBUG_PRINT printf("file:%s\t line:%d\t date:%s\t time:%s\n,  \
                             __FILE__,__LINE__,  \
                              __DATE__,__TIME__")

注意:#define后面定义的值后面尽量不加;否则可能会形成语法错误 eg:
#define MAX 100;//表达的意思为MAX这个符号等价于100;

#define 定义宏
宏/定义宏:#define机制包括了一个规定,允许把参数替换到文本中
 #define name(parament-list) stuff 其中的parament-list是一个由','隔开的符号表,它们可能出现在stuff中
注意参数列表的左括号必须与前面的name紧邻。如果两者之间有任何空白存在,参数列表就会被解释为stuff的一部分

#define SQUARE(x) x*x
#define SQUARE1(x) (x)+(x)
#define SQUARE2(x) ((x)+(x))
int main()
{
    int ret = 10*SQUARE(4);//160
    int ret1 = 10*SQUARE(3 + 1);//34
    int ret2 = 10*SQUARE1(3+1);//44
    int ret3 = 10 * SQUARE2(3 + 1);//80
    //为保证在进行宏的替换时达到我们想要的理想值,应在定义时加上必要的括号
    return 0;
}

注意:宏并不是函数的传参,而是一种简单的替换

#define的替换规则:

1、在调用宏时,首先对参数进行检查,看看是否包含热河由#define定义的符号,如果是,首先将其替换

#define MAX 10
#define ADD(x) x+x
int main()
{
    printf("%d\n", ADD(MAX));
    return 0;
}

2、替换文本随后被插入到程序中原本文本的位置。对于宏,参数名被它们的值替换
3、最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,重复上述步骤
 

注意:
1、宏参数和#define定义中可以出现其他#define定义的变量。但是对于宏,不能出现递归
2、当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索

#和##
首先需要了解字符串是由自动连接的特点的

int main()
{
    printf("hello bit\n");
    printf("hel""lo"" bit\n");
    return 0;
}

#define PRINT(x) printf("the result of "#x" is %d\n",x)
int main()
{
    int a = 10;
    PRINT(a);
    int b = 20;
    PRINT(b);
    return 0;

#的作用是在宏定义中将x所对应的值的字符替换到字符串中

##可以把位于它两边的符号(字符/字符串)合并成一个符号 

#define RENAME(X,Y) X##Y
int main()
{
    int ret = 10;
    printf("%d\n", RENAME(re, t));
    return 0;
}

 带副作用的宏参数

#define MAX(a,b) ((a)>(b)?(a):(b))
int main()
{
    int a = 10;
    int b = 20;
    printf("%d\n", MAX(a++, b++));//21
    printf("%d\n", a);//11
    printf("%d\n", b);//22
    return 0;
}

可以看出正常思维的值和计算出的值会出现较大的偏差,这是由于++会改变原来的值并且宏只会简单替换而不会计算参数产生的信息差。因此在定义宏时尽量不写这种带副作用的宏参数。

宏和函数的对比

 

命名的约定:

一般来讲,函数和宏的语法使用很相似,使用语言本身没有办法帮我们区分二者。为了便于区分,我们自己定了一个约定:把宏名全部大写,而函数名不要全部大写。
 

#undef  应用于移除一个宏定义

#define DEL(x,y) x-y
int main()
{
    int a=10;
    int b = 20;
    printf("%d\n", DEL(a, b));
#undef DEL
    printf("%d\n", DEL(a, b));//err
    return 0;
}

命令行定义-

简单的来收就是在编译时才对变量进行赋值,一般可以用于linux系统的gcc编译中 

int main()
{
    int arr[MAX] = { 0 };//MAX的赋值在编译时进行。编译指令为:gcc -D 文件名 MAX=10; 
    for (int i = 0; i < 10; i++)
    {
        arr[i] = i;
        printf("%d\n", arr[i]);
    }
    return 0;
}

条件编译-此操作在预处理中被执行 

在编译一个系统时,我们如果要将一条\一组语句编译或者放弃,可以使用条件编译指令
常见的条件编译指令:

1、#ifdef  #endif

int main()
{
#ifdef DEBUG//表示:如果预定义了DEBUG这个量,则条件为真,执行下列语句,否则不执行
    printf("hehe\n");
#endif
#define DEBUG
#ifdef DEBUG
    printf("hehe\n");
#endif
    return 0;
}

#endif和#ifdef是相互对应的关系。用了#ifdef之后必须得用#endif结束 

2、#if + 常量表达式

int main()
{
#if 1//非0,条件为真,执行下列代码
    printf("hehe\n");
#endif
#if 0//0,条件为假,不执行下列代码
    printf("haha\n");
#endif
    return 0;
}

3、多个分支的条件编译

int main()
{
#if 0
    printf("hehe\n");
#elif 1==1
    printf("haha\n");
#else
    printf("heihei\n");
#endif
    return 0;

4、判断是否被定义

int main()
{
#ifdef SUN//判断SUN是否被定义,为真则执行,否则在预编译时删除下列代码
    printf("haha\n");
#endif
#if defined(SUN)//与#ifdef SUN表达的意思相同,仅书写方式不同
    printf("hehe\n");
#endif
#ifndef SUM//如果SUM未被定义,则条件为真,输出以下语句
    printf("houhou\n");
#endif
#if !defined(SUM)//与#ifndef意思相同,仅写法不同
    printf("xixi\n");
#endif
    return 0;
}

 注:以上都是# ...开头,以#endif结尾,两个#endif之间没有任何关联

5、嵌套指令-类似于if语句嵌套

文件包含

  #include-所包含的头文件在预编译时先被删除,然后用包含文件的内容进行替换
""与<>的区别:
""是专门用来引用用户自己定义的头文件的。当引用""时,系统先在当前文件路径底下找,如果找不到再到标准存放c语言
头文件库中查找,若仍然找不到,则报错。
<>是专门用来引用库里面的头文件的,当在库中找不到所输入的头文件时,系统就会进行报错

嵌套文件包含-

一个头文件中包含了两个其他不同的头文件,而这两个头文件又同时包含了同一个另外的头文件的情况
如果头文件被多次包含,则会出现被多次编译的情况,当头文件内容较多时,则会严重拖慢系统运行时间,因此需要进行解决而解决的方法则是条件编译或者在头文件前加一个#pragma once每个头文件开头可以这样写(条件编译)

#ifndef __TEST_H__
#define __TEST_H__
//头文件的内容
#endif

这样就可以避免头文件的重复引入或者也可以使用#pragma once

其他预处理指令-自行了解

 

本文含有隐藏内容,请 开通VIP 后查看