【学习笔记之我要C】预处理

简介: 【学习笔记之我要C】预处理

预定义符号:


__FILE__      //进行编译的源文件
__LINE__     //文件当前的行号
__DATE__    //文件被编译的日期
__TIME__    //文件被编译的时间
__STDC__    //如果编译器遵循ANSI C,其值为1,否则未定义

#define

  #define定义标识符:


#define reg register    //为 register这个关键字,创建一个简短的名字

#define do_forever for( ; ; )    //用更形象的符号来替换一种实现

#define CASE break;case    //在写case语句的时候自动把 break写上。

// 如果定义的 stuff过长,可以分成几行写,除了最后一行外,每行的后面都加一个反斜杠(续行符)。

#define DEBUG_PRINT printf(“file:%s\tline:%d\t \

                 date:%s\ttime:%s\n” ,\

                 FILE,LINE , \

                 DATE,TIME )


注意:在预编译时#define之后的东西都会被直接替换,所以一般我们不会在最末尾加上 ; ,因为这样容易导致语法错误。


  #define定义宏:

#define 机制包括了一个规定,允许把参数替换到文本中,这种实现通常被我们称为宏(定义宏)。

#define name( parament-list ) stuff

其中的parament-list是一个由逗号隔开的符号表,它们可能出现在stuff中。

我们一般讲宏的名字全部写成大写,用于与函数名进行区分

注意:

参数列表的左括号必须与name紧邻。

如果两者之间有任何空白存在,参数列表就会被解释为stuff的一部分


#define SQUARE( x ) x * x
int main() {
  printf("%d", SQUARE(5));//25
  return 0;
}
#define SQUARE( x ) x * x
int main() {
  int a = 4;
  printf("%d", SQUARE(a + 1));//9
  return 0;
}


上面两个代码结果不同,因为第二个代码传过去的是4 + 1,而不是计算之后的结果5,所以用于对数值表达式进行求值的宏定义都应该加上括号,避免在使用宏时由于参数中的操作符或邻近操作符之间不可预料的相互作用。

#define SQUARE( x )  ( ( x ) * ( x ) )
int main() {
  int a = 4;
  printf("%d", SQUARE(a + 1));//9
  return 0;
}

#define替换规则:


  1. 在调用宏时,首先对参数进行检查,看是否包含任何由#define定义的符号。如果包含,他们首先被替换;
  2. 替换文本后被插入到程序中原来的文本位置。对于宏,参数名被他们的值所替换;
  3. 最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果包含,则重复上述操作。
  4. 宏参数和#define定义中可以出现其他#define定义的符号。但是对于宏,不能出现递归;
  5. 当预处理器搜索#define定义的符号时,字符串常量的内容并不被搜索。


宏和函数对比:


  1. 用于调用函数和从函数返回的代码可能比实际执行这个小型计算工作所需的时间更多,所以宏比函数在程序的规模和速度方面更胜一筹
  2. 函数的参数必须声明为特定的类型,所以函数只能在类型合适的表达式上使用。而宏可以适用于整型、长整型、浮点型等类型。因为宏是类型无关的,但这也导致了宏是不够严谨的。
  3. 每次使用宏时,一份宏定义的代码将被插入到程序中。除非宏比较短,否则可能大幅度增加程序的长度。
  4. 宏无法进行调试,并且可能会导致运算符优先级问题的出现,导致程序出错。


image.png

#undef

用于移除一个宏定义

#undef NAME
//如果现存的一个名字需要被重新定义,那么它的旧名字首先要被移除。

条件编译

  在编译一个程序的时候我们如果要将一条语句(一组语句)编译或者放弃是很方便的。因为我们有条件编译指令。

#if 常量表达式
//...
#endif
//常量表达式由预处理器求值。
如:
#define __DEBUG__ 1
#if __DEBUG__
 //..
#endif


多个分支的条件编译

#if 常量表达式
 //...
#elif 常量表达式
 //...
#else
 //...
#endif


判断是否被定义

#if defined(symbol)
#ifdef symbol
#if !defined(symbol)
#ifndef symbol


嵌套指令

#if defined(OS_UNIX)
  #ifdef OPTION1
    unix_version_option1();
  #endif
  #ifdef OPTION2
    unix_version_option2();
  #endif
#elif defined(OS_MSDOS)
  #ifdef OPTION2
    msdos_version_option2();
  #endif
#endif


文件包含


#include “filename”

查找策略:先在源文件所在目录下查找,如果该头文件未找到,编译器就像查找库函数头文件一样在标

准位置查找头文件。

如果找不到就提示编译错误。

#include <filename>

查找头文件直接去标准路径下去查找,如果找不到就提示编译错误。

库函数也可以用""包含来查找,但是这样会先从本地文件中查找,效率会有一定的降低,并且如果本地文件与库文件有重名的,也不能进行区分


嵌套文件包含


305b437b82d340b1903cd5652f9efd93.png


comm.h和comm.c是公共模块。

test1.h和test1.c使用了公共模块。

test2.h和test2.c使用了公共模块。

test.h和test.c使用了test1模块和test2模块。

这样最终程序中就会出现两份comm.h的内容。这样就造成了文件内容的重复。

这时我们可以使用条件编译解决这一问题。

我们一般使用#pragma once来避免头文件重复引入。


注:个人觉得#和##很鸡肋,所以没写。

目录
相关文章
|
存储 运维 jenkins
放弃"Jenkins"的种种理由,期待更好赋能研发的"持续交付平台"
Jenkins 很酷,但是不完美,有历史局限性造成的问题。本文仅从“如何更好给研发团队赋能的角度”,剖析Jenkins, 探讨理想的持续交付平台, 不带货无广告~
259 3
|
XML 物联网 API
服务端和客户端 RESTful 接口上传 Excel 的 Python 代码
本文作者木头左是物联网工程师,分享如何使用 Python 和 Flask-RESTful 构建一个简单的 RESTful API,实现文件上传功能,特别支持Excel文件。通过安装Flask和Flask-RESTful库,创建Flask应用,实现文件上传接口,并将其添加到API。该方法具有简单易用、灵活、可扩展及社区支持等优点。
服务端和客户端 RESTful 接口上传 Excel 的 Python 代码
|
人工智能 自然语言处理 IDE
CodeFuse IDE 0.7 版本发布,支持 Lint Error 智能改写
CodeFuse IDE 0.7 版本发布,支持 Lint Error 智能改写和 zsh 终端自然语言生成命令。它基于蚂蚁自研大模型和 OpenSumi 框架开发,提供代码编写建议、解释、单测生成等功能,提升开发效率。内置插件升级至 VS Code 1.88.1,未来将支持更多模型服务。下载地址:[GitHub](https://github.com/codefuse-ai/codefuse-ide/releases)。
376 4
|
算法 数据可视化 调度
基于PSO粒子群优化的车间调度问题求解matlab仿真,输出甘特图
基于PSO粒子群优化的MATLAB仿真解决车间调度问题,输入机器与工作完成时间,输出甘特图与收敛图,实现多机器多任务最优并行调度。使用MATLAB 2022a版本运行,通过模拟鸟群觅食行为,不断更新粒子速度与位置寻找最优解,采用工序编码,总加工时间为适应度函数,实现快速收敛并可视化调度结果。
512 16
|
NoSQL Unix Linux
Linux 设备驱动程序(一)(上)
Linux 设备驱动程序(一)
383 62
|
监控 数据可视化 JavaScript
Total.js Flow
Total.js Flow
347 61
|
网络协议 Ubuntu Linux
Linux系统之mtr命令的基本使用
【7月更文挑战第5天】Linux系统之mtr命令的基本使用
429 4
|
缓存 前端开发 Java
【Java面试题汇总】Spring,SpringBoot,SpringMVC,Mybatis,JavaWeb篇(2023版)
Soring Boot的起步依赖、启动流程、自动装配、常用的注解、Spring MVC的执行流程、对MVC的理解、RestFull风格、为什么service层要写接口、MyBatis的缓存机制、$和#有什么区别、resultType和resultMap区别、cookie和session的区别是什么?session的工作原理
【Java面试题汇总】Spring,SpringBoot,SpringMVC,Mybatis,JavaWeb篇(2023版)
|
存储 关系型数据库 Serverless
PolarDB产品使用问题之要获取并解析Binlog,该如何操作
PolarDB产品使用合集涵盖了从创建与管理、数据管理、性能优化与诊断、安全与合规到生态与集成、运维与支持等全方位的功能和服务,旨在帮助企业轻松构建高可用、高性能且易于管理的数据库环境,满足不同业务场景的需求。用户可以通过阿里云控制台、API、SDK等方式便捷地使用这些功能,实现数据库的高效运维与持续优化。
240 0
|
开发框架 算法 Java
28天读完349页,这份阿里面试通关手册,助我闯进字节跳动
今年的面试比往年要难得多,各个互联网企业对于Java岗位的要求越来越多,也越来越高,主要是初级岗位已经趋近饱和,但高级岗位又相对来说缺乏,这类的人才偏少,因此作为Java开发人员,我们应该思考的是怎么去往高级人才发展,而不是转学其他语言,要懂得去成为金字塔顶端的人,而不是逃避一直做基层的人。