C语言预处理

目录

〇,预处理

一,宏定义

1,宏定义

2,无参宏定义

3,带参宏定义

4,#和##

二,条件编译

三,#include


〇,预处理

预处理是指在进行编译的第一遍扫描(词法扫描和语法分析)之前所作的工作。预处理是C语言的一个重要功能,它由预处理程序负责完成。当对一个源文件进行编译时,系统将自动引用预处理程序对源程序中的预处理部分作处理,处理完毕自动进入对源程序的编译。
C语言提供了多种预处理功能,如宏定义、文件包含、条件编译等。

一,宏定义

1,宏定义

在C语言源程序中允许用一个标识符来表示一个字符串,称为“宏”

在C语言中,“宏”分为有参数和无参数两种。

2,无参宏定义

格式:  #define 标识符 字符串
说明:
(1)宏定义是用宏名来表示一个字符串,在宏展开时又以该字符串取代宏名,这只是一种简单的代换,字符串中可以含任何字符,可以是常数,也可以是表达式,预处理程序对它不作任何检查。如有错误,只能在编译已被宏展开后的源程序时发现。
(2)宏定义不是说明或语句,在行末不必加分号,如加上分号则连分号也一起置换。
(3) 宏定义必须写在函数之外,其作用域为宏定义命令起到源程序结束(类似全局变量的作用域规则)。
(4)宏名在源程序中若用双引号括起来,则预处理程序不对其作宏代换。

示例:

#define OK 100

int main()
{
    printf("OK");
    printf("\n");
    printf("OK123");
}

输出:

OK
OK123

(5)宏定义允许嵌套,在宏定义的字符串中可以使用已经定义的宏名。在宏展开时由预处理程序层层代换。
(6)可以用#undef 宏名来结束宏的作用域

3,带参宏定义

C语言允许宏带有参数。在宏定义中的参数称为形式参数,在宏调用中的参数称为实际参数。
对带参数的宏,在调用中,不仅要宏展开,而且要用实参去代换形参。
带参宏定义的一般形式为:#define 宏名(形参表) 字符串  在字符串中含有各个形参。
带参宏调用的一般形式为:宏名(实参表);

示例:

#define f(x,y) if(x>y)x^=y^=x^=y

int main()
{
    int a=1,b=3,c=2;
    f(a,b);
    f(a,c);
    f(b,c);
    printf("%d %d %d",a,b,c);
}

输出:1 2 3

说明:

(1)带参宏定义中,宏名和形参表之间不能有空格出现。
(2)在带参宏定义中,形式参数不分配内存单元,因此不必作类型定义。
(3)在宏定义中的形参是标识符,而宏调用中的实参可以是表达式。

4,#和##

#是字符串化,##是拼接

示例:

#include<iostream>
using namespace std;

#define v(x) #x
#define g(x,y) x##y

int main()
{
    int a=5,a2=6;
    cout<<v(a)<<endl<<g(a,2);
    return 0;
}

输出:

a
6

假如有这么一段代码:

enum E {
    case1,
    case2,
    case3
};

int main()
{
    int ca = 1;
    switch (ca) {
        case case1:
            cout << "ca = case1 \n";
            break;
        case case2:
            cout << "ca = case2 \n";
            break;
        case case3:
            cout << "ca = case3 \n";
            break;
        default:
            ;
    }
    return 0;
}

我们可以利用#来进行重构:

#define myout(x) cout << "ca = "#x" \n"

enum E {
    case1,
    case2,
    case3
};

int main()
{
    int ca = 1;
    switch (ca) {
        case case1:
            myout(case1);
            break;
        case case2:
            myout(case2);
            break;
        case case3:
            myout(case3);
        default:
            ;
    }
    return 0;
}

这样,看起来就形式比较统一了,那么此时能不能用表驱动来继续重构呢?

试一下:

#define myout(x) cout << "ca = "#x" \n"

enum E {
    case1,
    case2,
    case3
};

int main()
{
    int ca = 1;
    int a[]={ case1, case2, case3 };
    int length = sizeof(a) / sizeof(a[0]);
    for (int i = 0; i < length; i++) {
        if (ca == a[i]) {
            myout(a[i]);
            break;
        }
    }
    return 0;
}

果然,直接改是不行的,因为宏定义属于预处理,在运行之前就已经完成替换了。

这是用了#的宏定义,给代码留下的坑

如果要用表驱动来重构,就需要建立 case1——“case1”这样的映射,用字符串取代此处的#x

对于形式比较统一的情况,也可以用##来进行重构,得到这样的代码:

#define myout(x) cout << "ca = "#x" \n"
#define f(x) if(ca==case##x)myout(case##x);

enum E {
    case1,
    case2,
    case3
};

int main()
{
    int ca = 1;
    f(1);
    f(2);
    f(3);
    return 0;
}

这样,我们就成功的让代码就变得更加复杂了。

二,条件编译

条件编译:预处理程序提供了条件编译的功能。可以按不同的条件去编译不同的程序部分,因而产生不同的目标代码文件。这对于程序的移植和调试是很有用的。
条件编译有三种形式:#ifdef 、#ifndef 、#if

(1)#ifdef 

#ifdef 标识符
程序段1 
#else 程序段2
#endif
或者
#ifdef 标识符
程序段
#endif

(2)#ifndef 

#ifndef 标识符
程序段 1
#else 程序段2
#endif
或者
#ifndef 标识符
程序段
#endif

(3)#if

#if 常量表达式
程序段 1
#else 程序段2
#endif
或者
#if 常量表达式
程序段
#endif

三,#include

1,包含命令中的文件名可以用双引号括起来,也可以用尖括号括起来。

#include"文件名"
#include<文件名>
#include 尖括号和引号的区别:
在编译时,尖括号代表优先查找include的目录,引号代表优先源代码。
如果查找失败才查找另外一个。

2,文件包含允许嵌套,即在一个被包含的文件中又可以包含另一个文件。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值