C语言超全面define预处理指令的使用说明

 更新时间:2022年04月22日 10:35:55   作者:耀 星  
C语言里可以用#define定义一个标识符来表示一个常量。特点是:定义的标识符不占内存,只是一个临时的符号,预编译后这个符号就不存在了,也不做类型定义。预编译又叫预处理

前言

C语言中源代码到可执行文件的第一阶段,也就是预处理阶段,会检查源文件中的预处理指令语句和宏定义,并对源代码进行相应的替换,预处理过程还会删除程序中的注释和多余的空白符号。

预处理指令是以#开头的代码行,#必须是该行除了空白符外的第一个字符,#后是指令关键字,在#和指令关键字之间允许存在若干个空白字符,define是宏定义命令。在C语言程序中允许用一个标识符来表示一个字符串,称为“宏”,“宏”又分为有参和无参,有参又称为“宏函数”,被定义为“宏”的标识符称为“宏名”。

#define 定义宏(无参)

语法规定:

#define name stuff
name:标识符名\宏名
stuff:可以是关键字、常量、关键字、标识符、标点符号、运算符,表达式

在预处理阶段,编译器会在程序中使用#define定义的标识符替换成stuff,可以通过预处理生成的.i文件查看效果。

//stuff是数值常量
#define NUM 10

//stuff是关键字
#define reg register

//stuff是标点符号
#define GREATER_THAN >

//stuff的更多表达方式
#define do_forever for(;;)
//若定义的stuff过长,可以分成几行写,除了最后一行外,每行的后面都加一个\(续行符)
#define DBBUG_PRINT printf("file:%s\tline:%d\t \
                    date:%s\ttime:%s\n",\
                    __FILE__,__LINE__,\
                    __DATE__,__TIME__)

总结:#define定义的name(宏名),在预编译阶段会将所有的宏名替换成stuff,stuff内容被替换到源代码中。称为“宏代换”或“宏展开”。

注意:define定义的标识符的时候,后面加上“;”会将“;”认为是stuff中的内容。

#define 定义宏函数

宏函数的申明方式:#define name(parament-list) stuff
parament-list:参数列表

 注意:参数列表的左括号必须与name紧邻,如果两者之间有空白存在,参数列表就会被解释为stuff的部分。

宏函数存在的问题1

#include <stdio.h>
#define SQUARE(x) x*x//定义一个宏函数求平方
int main()
{
    int x = SQUARE(3+1);//替换后x的计算结果是多少?答案是:7
 
}

 为什么呢?

在给宏函数传参时,如果传递的是一个表达式,不会先计算表达式的结果再进行传参,而是直接将表达式整体作为参数传递。

那么如何防止发生这样的情况呢?+()

 宏函数存在的问题2

#include <stdio.h>
#define SUM(x,y) (x)+(y)
int main()
{
    int a = 10;
    int b = 5;
    int c = SUM(a,b)*2;//替换后c的结果为20,why
    return 0;
}

我们看看替换后的结果

 这又该如何解决呢?

 总结:在对数值表达式进行求值的宏定义应该用这两种方式加上括号,避免在使用宏参数的操作符或邻近操作符之间不可预料的相互作用。 

 #define替换规则:

1.在使用宏函数时,首先对参数进行检查,看看参数中是否包含任何#define定义的标识符,如果有,他们首先被替换。

2.替换的内容被插入到源文件原来的位置。对于宏函数,参数名被他们的值替换

宏的更多规则特性

1.宏名一般用大写

2.使用宏可提高程序的通用性和易读性,便于修改。

3.宏定义末尾不加分号

4.宏定义写在函数的大括号外面,作用域为其后的程序,通常放在开头

5.宏函数不可递归

6.宏定义不分配内存,变量定义分配内存

7.字符串" "中永远不包含宏

8.宏定义不存在类型问题,他的参数也没有类型

宏的缺点

1.宏不能调试

2.宏由于与类型无关,不够严谨

3.宏可能带来运算符优先级的问题,导致容易出错

常见预处理指令

#define:宏定义

#undef:撤销已经定义过的宏名

#include:将另一个源文件嵌入到#include源文件中

#if~#endif:如果#if后面的常量表达式为真,则编译#if~#endif之间的代码,如果为假,跳过这些代码不编译。

#if~#elif~#else~#endif:和if~else if~else类似,可以建立更分支。

#ifdef symbol~endif:判断是否被定义,定义了编译他们之间内容

#ifndef symbol~endif:判断是否被定义,没定义编译他们之间的内容

#line:改变当前行数和文件名称,是在编译程序中预先定义的标识符命令的基本形式:#line number["filename"]

#error:编译程序时,只要遇到#error就会生成一个编译错误的提示信息,并停止编译。

#pragma: 可以设定编译程序完成一些特点的动作(可以通过编译程序的菜单中设置),可以向编译程序传送各种指令。

到此这篇关于C语言超全面define预处理指令的使用说明的文章就介绍到这了,更多相关C语言define内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Opencv实现拼图板游戏

    Opencv实现拼图板游戏

    这篇文章主要为大家详细介绍了Opencv实现拼图板小游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-03-03
  • QT生成随机验证码的方法

    QT生成随机验证码的方法

    这篇文章主要为大家详细介绍了QT生成随机验证码的方法,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-06-06
  • STL容器之vector源码详细解读

    STL容器之vector源码详细解读

    这篇文章主要介绍了STL容器之vector源码详细解读,vector的数据安排和array和类似,它们的主要差别在于空间的运用和灵活性,array是静态空间,一旦配置了就不能改变,需要的朋友可以参考下
    2024-01-01
  • C++四种case的详细介绍小结

    C++四种case的详细介绍小结

    本文主要介绍了C++四种case的详细介绍小结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-05-05
  • C++调用python(执行py文件)的全过程

    C++调用python(执行py文件)的全过程

    这篇文章主要给大家介绍了关于C++调用python(执行py文件)的相关资料,文中通过图文以及实例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2021-12-12
  • C++实现教务管理系统

    C++实现教务管理系统

    这篇文章主要为大家详细介绍了C++实现教务管理系统,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-06-06
  • C语言实现学生选课系统

    C语言实现学生选课系统

    这篇文章主要为大家详细介绍了C语言实现学生选课系统,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-02-02
  • C语言return, exit, abort的区别

    C语言return, exit, abort的区别

    这篇文章主要介绍了C语言return, exit, abort的区别,一般情况下,在C语言中退出一个程序用return,如果在main函数中,return在清理局部对象之后,会调用exit函数,和return相比,exit并不会销毁局部对象,下面一起进入文章了解更详细内容吧,需要的朋友也可以参考一下
    2022-01-01
  • C++ opencv将图片动漫化介绍

    C++ opencv将图片动漫化介绍

    大家好,本篇文章主要讲的是C++ opencv将图片动漫化介绍,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下
    2022-01-01
  • C++输出上三角/下三角/菱形/杨辉三角形(实现代码)

    C++输出上三角/下三角/菱形/杨辉三角形(实现代码)

    本篇文章是对C++中输出上三角/下三角/菱形/杨辉三角形的示例代码进行了详细的分析介绍,需要的朋友参考下
    2013-07-07

最新评论