程序环境和预处理(2)

文章目录

        • 3.2.7 命名约定
      • 3.3 #undef
      • 3.4 命令行定义
      • 3.5 条件编译
      • 3.6 文件包含
        • 3.6.1 头文件被包含的方式
        • 3.6.2 嵌套文件包含
    • 4. 其他预处理指令

3.2.7 命名约定

一般来讲函数的使用语法很相似,所以语言本身没法帮我们区分二者,那我们平时的一个习惯是:

把宏名全部大写
函数名不要全部大写

#define MAX(x, y) ((x)>(y)?(x):(y))int Max(int x, int y)
{return x > y ? x : y;
}//有一个特例
//offsetof - 宏 - 全小写int main()
{return 0;
}

3.3 #undef

这条指令用于移除一个宏定义。

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

#include <stdio.h>#define MAX(x, y) ((x)>(y)?(x):(y))int Max(int x, int y)
{return x > y ? x : y;
}int main()
{int c = MAX(3, 5);printf("%d\n", c);
#undef MAXc = MAX(5, -5);//编译器会报错:“MAX”未定义printf("%d\n", c);return 0;
}

3.4 命令行定义

许多C的编译器提供了一种能力,允许在命令行中定义符号,用于启动编译过程。

例如:当我们根据同一个源文件要编译出一个程序的不同版本的时候,这个特性有点用处。(假定某个程序中声明了一个某个长度的数组,如果机器内存有限,我们需要一个很小的数组;但是另外一个机器内存大些,我们需要一个数组能够大些。)

注:VS环境无法演示,gcc来演示

#include <stdio.h>int main()
{int arr [SZ];int i = 0;for(i = 0; i < SZ; i++){arr[i] = i + 1;}for(i = 0; i < SZ; i++){printf("%d " , arr[i]);}return 0;
}

命令行定义

3.5 条件编译

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

比如我们要在不同的操作系统上执行代码,那么就要执行不同的代码,我们就可以使用条件编译,满足相应的条件,就编译相应的代码,另外一份代码就不会被编译了。

常见的条件编译指令:

#if 常量表达式
//…
#endif
//常量表达式由预处理器求值。

#include <stdio.h>#define M 0int main()
{
#if 1 == M  //#if 后面的语句为真,就参与编译;否则就不参与编译printf("hehe\n");
#endifreturn 0;
}

条件编译例子的预处理
注: #if 条件编译不满足是不编译;而 if 条件语句不满足是不执行,但是它的代码运行时是存在的。

  1. 多个分支的条件编译

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

#include <stdio.h>#define M 0int main()
{
#if 1 == Mprintf("hehe\n");
#elif 2 == Mprintf("haha\n");
#elseprintf("heihei\n");
#endifreturn 0;
}

条件编译例子的预处理(2)

  1. 判断是否被定义

#if defined(symbol)
#ifdef symbol

#if !defined(symbol)
#ifndef symbol

#include <stdio.h>#define WIN 0int main()
{
#if defined(WIN)printf("windows");
#endifreturn 0;
}

条件编译例子的预处理(3)
也可以这样写:

#include <stdio.h>#define WIN 0int main()
{
#ifdef WINprintf("windows");
#endifreturn 0;
}

#include <stdio.h>#define WIN 0int main()
{
#if !defined(WIN)printf("windows\n");
#endifreturn 0;
}

条件编译例子的预处理(4)
也可以这样写:

#include <stdio.h>#define WIN 0int main()
{
#ifndef WINprintf("windows\n");
#endifreturn 0;
}
  1. 嵌套指令
#if defined(OS_UNIX)#ifdef OPTION1unix_version_option1();#endif#ifdef OPTION2unix_version_option2();#endif
#elif defined(OS_MSDOS)#ifdef OPTION2msdos_version_option2();#endif
#endif

我们在 stdio.h 这个头文件中就能看到条件编译的应用:
stdio.h中条件编译的应用


有些人会将条件编译当注释来用:

#if 0
int main()
{return 0;
}
#endif#include <stdio.h>int main()
{printf("hehe\n");return 0;
}

3.6 文件包含

我们已经知道, #include 指令可以使另外一个文件被编译,就像它实际出现于 #include 指令的地方一样。

这种替换的方式很简单:

预处理器先删除这条指令,并用包含文件的内容替换。

这样一个源文件被包含10次,那就实际被编译10次。

3.6.1 头文件被包含的方式
  • 本地文件包含

#include “filename”

查找策略:先在源文件所在目录下查找,如果该头文件未找到,编译器就像查找库函数头文件一样在标准位置查找头文件,如果找不到就提示编译错误。

Linux环境的标准头文件的路径:

/usr/include

VS环境的标准头文件的路径:

C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include
//这是VS2013的默认路径

注意按照自己的安装路径去找。


  • 库文件包含

#include <filename.h>

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

这样是不是可以说,对于库文件也可以使用 “” 的形式包含?

答案是肯定的,可以

但是这样做查找的效率就低些,当然这样也不容易区分是库文件还是本地文件了。

3.6.2 嵌套文件包含

如果出现这样的场景:
嵌套文件包含
comm.h和comm.c是公共模块。

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

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

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

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

例子如下:
重复包含头文件例子

如何解决这个问题?

答案:条件编译。

每个头文件的开头写:

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

或者:

#pragma once

就可以避免头文件的重复引入。

例子:

//test.h#ifndef __TEST_H__
#define __TEST_H__int Add(int x, int y);#endif

或者这样写:

//test.h#pragma onceint Add(int x, int y);

4. 其他预处理指令

#error
#pragma
#line

不做介绍,自己去了解。
#pragma pack()在结构体部分介绍。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/718348.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

linux条件判断之if-then

if..then是最常见的条件判断语句&#xff0c;简而言之&#xff0c;就是当符合某个条件判断的时候&#xff0c;就予以进行某项工作。 1.if-then格式 if-then格式1&#xff1a; if [ 条件判断表达式 ];then 当条件判断表达式成立时&#xff0c;需执行的命令 fi if-then格式2…

Redis安全加固策略:绑定Redis监听的IP地址 修改默认端口 禁用或者重命名高危命令

Redis安全加固策略&#xff1a;绑定Redis监听的IP地址 & 修改默认端口 & 禁用或者重命名高危命令 1.1 绑定Redis监听的IP地址1.2 修改默认端口1.3 禁用或者重命名高危命令1.4 附&#xff1a;redis配置文件详解&#xff08;来源于网络&#xff09; &#x1f496;The Beg…

驱动开发面试复习

创建字符设备 1 创建设备号 alloc_chrdev_region 2.创建cdev cdev_init 3.添加一个 cdev,完成字符设备注册到内核 cdev_add 4.创建类 class_create 5.创建设备 device_create 1.内核空间与用户空间数据 copy_from_user 和copy_to_user 俩个函数来完成。 copy_from_user 函数…

618快递准点到达,别忘了感谢它!

进入6月以来&#xff0c;全国快递日均业务量飞速上涨。 虽然618大促是电商的主场&#xff0c;但作为不可或缺的物流环节&#xff0c;为了这场年中大考&#xff0c;快递企业在此期间也使尽浑身解数&#xff0c;竞相比拼配送速度。那么&#xff0c;为了更快的时效&#xff0c;快递…

uniapp 的video播放如何实现小窗功能

在页面中使用<video>组件来展示视频&#xff0c;并设置好相应的属性和事件监听&#xff1a; <video src"video.mp4" play"onVideoPlay" pause"onVideoPause"></video>在页面的data中定义一个变量来表示是否开启小窗模式&#…

【Wio Terminal】使用WiFi(3)- Wi-F的高级使用

使用WiFi&#xff08;3&#xff09; Wi-F的高级使用HTTPClient 的使用HTTP GETHTTPs GETHTTP POSTWebServerHTTP Authentication Web ServerDNSServermDNSmDNS-SDWiFiManager Wi-F的高级使用 本节介绍了一些WiFi的高级库用法&#xff0c;如HTTPClient、DNSServer和WebServer库…

美国亚利桑那州立大学宣布与OpenAI建立合作伙伴关系!

美国亚利桑那州立大学 (Arizona State University) 在官网宣布—— 将与OpenAI建立合作伙伴关系&#xff01; 该校也成为了第一个与OpenAI合作的高等教育机构。 来源&#xff1a;亚利桑那州立大学官网 亚利桑那州立大学校长表示&#xff1a; “我们认识到人工智能系统将持续…

高并发IO底层原理浅析(四)

Java NIO中的Selector&#xff08;选择器&#xff09;是一个用于检测多个非阻塞通道&#xff08;Channel&#xff09;是否准备就绪进行读写操作的关键组件&#xff0c;它实现了I/O多路复用技术。在单个线程中&#xff0c;Selector可以监听和管理多个Channel上的事件&#xff0c…

Redis安全加固策略:配置文件权限设置 配置本地日志存储目录 连接超时时间限制

Redis安全加固策略&#xff1a;配置文件权限设置 & 配置本地日志存储目录 & 连接超时时间限制 1.1 配置文件权限设置1.2 配置本地日志存储目录1.3 连接超时时间限制 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 1.1 配置文件权限…

如何理解template<size_t N>?

template<size_t N> 是C中的模板参数&#xff0c;用于定一个非类型参数的值。它可以在编译时确定&#xff0c;并且可以在编译时进行计算和使用。 例如&#xff0c;我们可以定义一个模板函数&#xff0c;接受一个大小为N的数组作为参数&#xff1a; template<size_t …

Qt/事件分发器/事件过滤器

事件分发器 //事件分发器bool event(QEvent* e); //事件分发器&#xff1a;&#xff1a;用途 分发事件 bool MyLabel::event(QEvent* e) {if(e->type() QEvent::MouseButtonPress){//如果是鼠标摁下 拦截事件 不向下分发QMouseEvent* ev static_cast<QMouseEvent*>…

Qt-Qss 样式表属性大全

目录 前言 源码文件信息 QSS属性大全 1.控件常用的属性 2.控件常用属性值 3.控件常用状态 4.其他 前言 相信很大一部分开发者真的被Qt样式表的属性搞挺头痛的&#xff01; 一方面Qss的示例Demo太过简单&#xff0c; 另一方面&#xff0c;Qss的所有属性在Qt的官方文…

软考高级:逆向工程相关概念和例题

作者&#xff1a;明明如月学长&#xff0c; CSDN 博客专家&#xff0c;大厂高级 Java 工程师&#xff0c;《性能优化方法论》作者、《解锁大厂思维&#xff1a;剖析《阿里巴巴Java开发手册》》、《再学经典&#xff1a;《Effective Java》独家解析》专栏作者。 热门文章推荐&am…

Deeplearning4j【基础 01】初识Java深度学习框架DL4J

初识Java深度学习框架DL4J 1.起因2.简介3.组件3.1 Deeplearning4j/ScalNet3.1.1 Deeplearning4jf&#xff08;Java&#xff09;3.1.2 ScalNet&#xff08;Scala&#xff09; 3.2 ND4J/LibND4J3.3 SameDiff3.4 DataVec3.5 Arbiter3.6 RL4J 4.总结 内容来自网络&#xff0c;基于官…

Redis--线程模型详解

Redis线程模型 Redis内部使用的文件事件处理器&#xff08;基于Reactor模式开发的&#xff09;file event handler是单线程的&#xff0c;所以Redis线程模型才叫单线程模型&#xff0c;它采用IO多路复用机制同时监听多个socket&#xff0c;当被监听的socket准备好执行accep、r…

072:vue+cesium 实现下雪效果

第072个 点击查看专栏目录 本示例的目的是介绍如何在vue+cesium中实现下雪效果,这里使用着色器来实现实例特效。 直接复制下面的 vue+cesium源代码,操作2分钟即可运行实现效果. 文章目录 示例效果配置方式示例源代码(共120行)着色代码实现心得:专栏目标示例效果

Python世界之附加

一、数据类型转换 1.隐式类型转换 在隐式类型转换中&#xff0c;Python 会自动将一种数据类型转换为另一种数据类型。 对两种不同类型的数据进行运算&#xff0c;较低数据类型就会转换为较高数据类型以避免数据丢失。 2.显式类型转换 在显式类型转换中&#xff0c;用户将对…

Linux设置静态IP地址

在Linux系统中设置静态IP地址的步骤如下&#xff1a; 1.使用管理员权限登录系统。 2.编辑网卡配置文件&#xff1a; 网卡配置文件通常位于 /etc/sysconfig/network-scripts/ 目录下&#xff0c;可以使用命令 cd /etc/sysconfig/network-scripts 切换到该目录。 …

Wifi技术知识

参考文章 https://www.163.com/dy/article/FDFT60T70550I80C.html https://www.zhihu.com/tardis/bd/art/485711752?source_id1001 wifi代数 以前大家看电脑手机路由器参数&#xff0c;WiFi都是802.11a/b/n/g/ac/ax&#xff0c;这学名读起来有些麻烦&#xff0c;现在好了&…

第12届智能计算与无线光通信国际会议(ICWOC 2024)即将召开!

2024年第12届智能计算与无线光通信国际会议&#xff08;ICWOC 2024&#xff09;将于2024年6月21-23日在中国重庆召开。随着深度学习等人工智能技术的不断进步&#xff0c;以自动化、自治为特征的智能应用预计将激增。本届会议主题为“光通信智能链接”&#xff0c;旨在为相关技…