【第十一节】C/C++的预处理及命名空间

目录

一、预处理

1.1 文件包含(#include)

1.2 宏定义(#define)

1.2.1 常量宏定义

1.2.2 宏函数

1.2.3 宏取消

1.3 条件编译

1.4 预处理使用例子

1.4.1 extern "C" 块的应用

1.4.2 选择编译

1.4.3 注释大段代码

1.4.4 避免头文件被重复引用

二、命名空间

2.1 命名空间介绍

2.2 需要命名空间的原因

2.3 命名空间的定义

2.4 使用命名空间和别名

2.4.1 使用

2.4.2 别名


一、预处理

        在C/C++中,预处理是编译过程的第一阶段,主要由预处理器完成。预处理的主要功能是根据预处理指令对源代码进行相应的文本处理,然后输出到下一阶段。下面,我将详细介绍预处理中的三种主要操作:文件包含、宏定义和条件编译。

1.1 文件包含(#include)

        文件包含是预处理中最常用的指令之一,它允许程序员在一个源文件中包含另一个文件的内容。这通常用于包含头文件(.h.hpp),这些头文件通常包含函数声明、类定义、宏定义等。文件包含有两种形式:

  • 尖括号形式 (#include <filename>): 用于包含标准库头文件或系统头文件。预处理器会在标准库路径中查找这些文件。
  • 双引号形式 (#include "filename"): 用于包含用户自定义的头文件。预处理器首先在当前目录或指定的项目目录中查找这些文件,如果找不到,则可能回退到标准库路径中查找。

        文件包含的主要目的是实现代码的模块化,避免重复编写相同的代码,同时提高代码的可维护性和可重用性。

1.2 宏定义(#define)

        宏定义是预处理的另一个重要功能,它允许程序员定义常量或宏函数。宏定义在预处理阶段进行文本替换,不占用任何存储空间,也不分配内存地址。

1.2.1 常量宏定义

常量宏定义通常用于表示一个不会改变的值。例如:

#define PI 3.14159

在代码中,每当出现PI时,预处理器都会将其替换为3.14159

1.2.2 宏函数

宏函数类似于函数,但它实际上只是文本替换,没有函数调用的开销。例如:

#define MAX(a, b) ((a) > (b) ? (a) : (b))

使用宏函数时,要注意参数周围的括号,以避免出现运算优先级和结合性错误。

1.2.3 宏取消

宏取消使用#undef预处理指令。它用于取消之前定义的宏,使该宏在后续代码中不再可用。

例如:

#define DEBUG 1
// ... 一些使用DEBUG宏的代码 ...
#undef DEBUG

        在这个例子中,DEBUG宏最初被定义并赋值为1。然后,在代码中使用DEBUG宏进行条件编译或其他操作。最后,使用#undef DEBUG取消DEBUG宏的定义,以确保在后续代码中不会意外地使用到它。

        宏定义可以提高代码的可读性和可维护性,但也要注意避免过度使用宏定义,因为宏只是简单的文本替换,没有类型检查,可能会导致一些不易察觉的错误。

注意事项:

  1. 没有类型检查:宏只是简单的文本替换,因此预处理器不会检查类型是否正确。
  2. 多次求值:如果宏的参数在替换后的代码中多次出现,那么它会被多次求值,这可能导致不期望的副作用。
  3. 括号问题:在定义带参数的宏时,必须注意参数的括号问题,以确保宏替换后的表达式具有正确的优先级和结合性。
  4. 作用域:宏定义在定义它的文件以及包含该文件的任何文件中都可见,直到它被#undef取消或到达文件的末尾。因此,要小心避免在不同文件中定义同名宏而导致的冲突。

1.3 条件编译

        条件编译允许程序员根据特定的条件决定是否包含某段代码。这在跨平台编程、调试代码或根据不同的配置选项构建程序时非常有用。

条件编译的主要预处理指令包括:

  • #if: 判断后面的条件是否为真,如果为真则编译下面的代码,直到遇到#else#elif#endif
  • #ifdef: 判断是否定义了某个宏,如果已定义则编译下面的代码。
  • #ifndef: 与#ifdef相反,判断某个宏是否未定义。
  • #else: 与#if#ifdef#ifndef配对使用,当前面的条件不满足时,编译#else后面的代码。
  • #elif: 类似于#else if,用于提供额外的条件判断。
  • #endif: 结束条件编译块。

例如:

#ifdef DEBUG
// 这段代码只在定义了DEBUG宏时才会被编译
printf("Debug information\n");
#endif

条件编译使得程序员能够灵活地控制哪些代码应该被编译和执行,从而满足不同的需求和环境。

1.4 预处理使用例子

条件编译:应用在条件编译中的条件表达式不必加括号。


1.4.1 extern "C" 块的应用

    #ifdef __cplusplusextern "C" {#endif/* Assume C declarations for C++ */#ifdef __cplusplus}#endif

        __cplusplus是C++的预定义宏,表示当前开发环境是C++。在C++语言中,为了支持重载机制,在编译生成的汇编代码中,会对函数名字进行进行一些处理(通常称为函数名字改编),如加入函数的参数类型或返回类型等,而在C语言中,只是简单的函数名字而已,并不加入其它信息:
    void show(int demo);
    void show(double demo);
        C无法区分上面两个函数的不同,因为C编译器产生的函数名都是 _show,而C++编译器产生的名字则可能是 _show_Fi 和 _show_Fd,这样就很好地把函数区别开了。
        所以,在C/C++混合编程的环境下, extern "C" 块的作用就是告诉C++编译器这段代码要按C标准编译,以尽可能地保持C++与C的兼容。
        再比如说用C++开发了一个DLL库,为了能够让C语言也能够调用该DLL输出(Export)的函数,需要用extern "C"来强制编译器不要修改你的函数名。

1.4.2 选择编译

        有时候程序中的某些调试代码,只需要在调试的时候被编译,而不希望在程序的发行版中被编译,你可能会看到类似这样的代码段:

    #ifdef _DEBUG_printf("File: %s, Line: %d | x = %d\n", __FILE__, __LINE__, x);#elseprintf("x = %d\n", x);#endif

        当符号 _DEBUG_ 没有被定义的时候,仅编译 #else 与 #endif 之间的代码;定义了 _DEBUG_ 符号之后,只编译 #ifdef 与 #else 之间的代码。要想定义一个符号很简单,只需要在文件头部加上像这样的一条语句:
    #define _DEBUG_

1.4.3 注释大段代码

        注释大段代码可以使用/*--*/,但不支持嵌套注释,有时容易出错。通常忽略大段代码的正确方法是使用条件编译指令,示例代码如下:

    #if 0............#endif

1.4.4 避免头文件被重复引用

        头文件通常包含有声明及定义语句,头文件还要以包含其它的头文件,即头文件嵌套,因此需要注意“重复引用”的情况,因为某些声明或定义如typedef,在一次编译中仅允许出现一次,重复包含会导致编译器提示出错信息。头文件嵌套如:
    //a.h
    #include "b.h"
    //main.cpp
    #include "a.h"//已经包含了b.h
    #include "b.h"
 
    技巧是定义一个与头文件相关的宏定义符号,如果预处理器已经见过这个符号,那么就不会重复嵌入文件的内容:
    // b.h
    #ifndef __B_H__
        #define __B_H__
      <定义或声明>
    #endif

二、命名空间

2.1 命名空间介绍

        在C++中,预处理器指令、静态变量和函数等特性可能会引起命名冲突,因为它们在编译时会直接插入到代码中,而不会考虑命名空间。然而,命名空间提供了一种机制来解决这种问题,通过将相关的标识符(如变量、函数、类等)组织在同一个命名空间中,可以避免命名冲突。

        命名空间是一个逻辑上的类型组织系统,它允许你对程序中的类型进行逻辑上的分组,并使定义在同一个命名空间中的类可以直接相互调用。标准的C++有命名空间机制,使用namespace关键字来定义命名空间。库或程序中的每一个C++定义集都被封装在一个命名空间中,如果其他的定义中有相同的名字,但它们在不同的命名空间,则不会产生命名冲突。

        命名空间的主要优点是它可以避免全局作用域中的命名冲突,并提供了一种组织和管理大型代码库的方法。通过使用命名空间,你可以将相关的代码和标识符封装在一个特定的上下文中,这样可以减少命名冲突的可能性,并使代码更易于理解和维护。

2.2 需要命名空间的原因

        在C++中,命名空间是ANSI C++引入的一种机制,它允许用户创建可以由用户命名的作用域,以处理程序中常见的同名冲突。这种机制在C语言中已经存在,但C++在此基础上进行了扩展,引入了类作用域。

        在C语言中,定义了三个层次的作用域:文件(编译单元)、函数和复合语句。在C++中,除了这三个作用域外,还引入了类作用域。类作用域是出现在文件内的,这意味着在一个文件中可以定义多个类,每个类都有自己的作用域。

        在不同的作用域中,可以定义相同名字的变量,这些变量在各自的作用域内是互不干扰的,系统能够区分它们。这就是命名空间的基本概念。通过使用命名空间,可以创建一个独立的命名空间,在这个命名空间中定义的变量、函数、类等在该命名空间内是唯一的,不会与其他命名空间中的同名标识符发生冲突。

2.3 命名空间的定义

格式:
namespace 命名空间名{
    //命名空间成员(其他命名空间或类的定义)
}

命名空间定义说明:

  1. 命名空间只能在全局范围内定义。

  2. “命名空间名”是C++合法的标识符,也可以是用作用域分解运算符(::)来构成命名空间的完全限定名。可以没有命名空间名(无名的命名空间),其成员的名称不需要限定就可以使用。

  3. 不允许在定义命名空间时使用任何访问修饰符,系统默认命名空间具有public访问属性。

  4. 命名空间的成员可以是另一个命名空间(嵌套)或类型(类、结构、接口、枚举等)的定义。

  5. 一个命名空间可以在多个头文件中用一个标识符来定义。

  6. 一个命名空间的名字可以用另一个名字来作它的别名。

  7. 不能像类那样去创建一个命名空间的实例。

  8. 可以在一个编译单元中包含一个未命名的命名空间,但只能包含一个未命名的命名空间。

  9. 命名空间在命名空间的结尾,右花括号的后面可以跟一个分号(;)。

代码示例:

#include<iostream>
using namespace std;//命名空间Outer的定义
namespace Outer {int nNumA;//子命名空间Inner的内部定义namespace Inner {void fun_a() { nNumA++; } //Outer::nNumAint nNumA;void fun_b() { nNumA++; } //Inner::nNumAvoid fun_c();}void fun_c();//namespace Inner2;//错误,不能声明了命名空间
}
void Outer::fun_c() { nNumA--; }//命名空间outer成员fun_c()的外部定义void Outer::Inner::fun_c() { nNumA--; }//命名空间Inner成员fun_c()的外部定义
//namespace Outer::Inner2 {/**/ }//错误,不能在外部定义子命名int main() {cout << "hello" << endl;system("pause");return 0;
}

2.4 使用命名空间和别名

2.4.1 使用

访问一个命名空间中的成员可以采用下面三种方法:


A. 直接访问命名空间的成员(完全限定名):用作用域分解运算符(:),例如:
std::cout<<"hello world!"<<std::endl;
缺点:输入繁琐

B. 使用声明:用using声明一次性引用名字,例如:
using std::cout;
cout<<"hello world!"<<std: :endl;
缺点:缺乏类型信息,不能克服重载问题

C. 使用指令:用using指令把所有名字引入到命名空间中,例如:
using namespace std;
cout<< "hello world!"<<endl;
缺点:可能会产生命名冲突

2.4.2 别名

        标准C++引入命名空间,主要是为了避免成员的名称冲突。若果用户都给自己的命名空间取简短的名称,那么这些(往往同是全局级的)命名空间本身,也可能发生名称冲突。如果为了避免冲突,而为命名空间取很长的名称,则使用起来就会不方便。这是一个典型的两难问题。


标准C++为此提供了一种解决方案--命名空间别名,格式为
namespace 别名 = 命名空间名;

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

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

相关文章

运筹学_4.整数规划

文章目录 引言4.1 分枝定界方法求解整数规划问题整数规划的分类整数规划解法概述分支定界法 4.2 0-1整数规划0-1整数规划的数学模型隐枚举法求解0-1规划问题 4.3 指派问题(分配问题)的匈牙利解法指派问题的数学模型指派问题的匈牙利解法 引言 规划中的决策变量(全部或部分)限制…

个人开发者能用TensorFlow做什么

个人开发者可以使用 TensorFlow 实现许多有趣且有用的机器学习和深度学习项目。以下是一些具体的应用和项目示例&#xff1a; 1. 图像处理 图像分类&#xff1a;训练一个模型将图片分类到不同类别&#xff0c;如猫和狗的分类。物体检测&#xff1a;检测图像中的多个对象及其位…

【备战蓝桥杯】蓝桥杯省一笔记:算法模板笔记(Java)

蓝桥杯 0、快读快写模板1、回文判定2、前缀和3、差分4、二分查找5、快速幂6、判断素数7、gcd&lcm8、进制转换9、位运算10、字符串常用API11、n的所有质因子12、n的质因子个数13、n的约数个数14、n阶乘的约数个数15、n的约数和16、阶乘 & 双阶乘17、自定义升序降序18、动…

2024盘古石初赛(服务器部分)

赛后总结 这次初赛就有20道服务器部分赛题&#xff0c;做的情况一般&#xff0c;错了5道题这样&#xff0c;主要原因就是出在第二个网站服务器没有重构起来 今天来复现一下 这次的服务器部分我直接用仿真仿起来就开找了 第一台IM前期配置 先把网配置好&#xff0c;然后ssh…

C++常用算法函数

1、排序 &#xff1a;std::sort #include <algorithm> #include <vector>std::vector<int> v {4, 2, 5, 3, 1}; std::sort(v.begin(), v.end()); // 将v中的元素按升序排序2、查找: std::find #include <algorithm> #include <vector>std::ve…

如此简单,一文带你玩转接口自动化上(Python + Pytest + Requests + Allure )

一. 前言 哈喽大伙们好&#xff0c;好久不见距离上次更新博客已经有一年之久了&#xff0c;这将近一年的时间小编主要的时间都花在了实习和24届校招上面了&#xff0c;最终也是收获满满&#xff0c;选择了一个还不错的offer&#xff0c;感谢一路走来的自己和身边朋友的帮助&…

基于Three.js实现的3D立方体动画

本文由ScriptEcho平台提供技术支持 项目地址&#xff1a;传送门 基于Three.js实现的3D立方体动画 应用场景 该代码段适用于需要在网页中创建交互式3D场景的场景。例如&#xff0c;可以用于展示产品、创建游戏或制作视觉效果。 基本功能 此代码段使用Three.js库创建了一个…

汽车企业如何通过神秘顾客调查确保销售服务规范

在当今竞争激烈的汽车市场&#xff0c;购车体验已成为消费者选择品牌的重要考量因素。为了确保汽车门店的销售服务符合规范&#xff0c;许多汽车企业开始采用神秘顾客调查这一策略。 那么&#xff0c;汽车企业在进行神秘顾客调查时&#xff0c;应该从那些方面着手才能保证调查…

【机器学习】随机森林:深度解析与应用实践

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 随机森林&#xff1a;深度解析与应用实践引言1. 随机森林基础1.1 什么是随机森林…

Android更新优化 - 增量更新是如何节省用户时间和流量的

增量更新和全量更新 我想玩过大型手游的人都知道&#xff0c;手游的安装包非常大&#xff0c;因为资源图片众多。而你每次更新都把所有文件都更新下来&#xff0c;是非常耗时的&#xff0c;对吧。耗时是一个方面&#xff0c;有些人在户外开的是移动网络&#xff0c;动不动就几…

计算机组成原理·海明编码及其实验

前言&#xff1a;海明编码这一块在刚开始的时候没有弄懂&#xff0c;后面通过做实验、复习慢慢摸清了门道。在学习计算机组成原理的过程中&#xff0c;实验实践是很重要的&#xff0c;它会让你去搞清楚事情背后的原理&#xff0c;逼着你学会你没听懂的东西。这篇文章会从海明码…

Check Point 安全网关任意文件读取漏洞复现(CVE-2024-24919)

Check Point 安全网关任意文件读取漏洞复现(CVE-2024-24919) 1.漏洞描述 Check Point Security Gateways 是 Check Point Sofware 提供的一系列 网络安全Q解决方案。这些解决方案包括下一代防火墙(NGFW)、数据中心安全网关和 A1驱动的量子网关&#xff0c;旨在为企业提供针对…

@Value 读取环境变量配置

在项目开发过程中&#xff0c;有必要使用一些灰色规则&#xff08;即仅用于开发使用过程中的逻辑控制变量&#xff09;。 比如&#xff0c;本地开发中&#xff0c;一些业务逻辑需要调用第三方代码&#xff0c;但又在本地调不通&#xff0c;怎么办。只能通过 if(本地开发) {mock…

JavaScript基础知识1(引入方式、注释、输入输出、变量、数组)

JavaScript基础知识1&#xff08;引入方式、注释、输入输出、变量、数组&#xff09; javascript 引入方式内部方式外部形式 注释和结束符单行注释多行注释 结束符 输入和输出输出输入 alert(你好JS);document.write(js我来了);console.log(看看对不对);prompt(输入您的姓名&am…

cxgrid列内容居中

笑掉大牙&#xff0c;需要这个功能了&#xff0c;找遍了全网&#xff0c;没有一个可用的成功案例&#xff0c;唯一可用的就是表中添加字段&#xff0c;然后选择properties然后选择对齐方式&#xff0c;根本不实用&#xff0c;因为列都是动态生成的&#xff0c;不可能提前做好所…

【开源】渔具租赁系统 JAVA+Vue.js+SpringBoot+MySQL

目录 一、项目介绍 1.1渔具档案模块 1.2渔具租赁模块 1.3渔具归还模块 1.4在线留言模块 二、项目截图 三、核心代码 一、项目介绍 Vue.jsSpringBoot前后端分离新手入门项目《渔具租赁系统》&#xff0c;包括渔具档案模块、渔具租赁模块、渔具归还模块、在线留言模块和部…

当新媒体运营开始说真话,这些道理你真的懂么?沈阳新媒体运营培训

运营新人&#xff0c;尤其是刚毕业、啥都不会的大学生&#xff0c;一定要认清的现实就是&#xff1a;虽然新媒体运营这个岗位门槛比较低&#xff0c;薪资也比较香&#xff0c;但绝不是养老型的工作。 平时大家还是很忙的&#xff0c;所以一定要摒弃学生思维&#xff0c;千万别…

【数据分析】打造完美数据分析环境:Python开发环境搭建全攻略

打造完美数据分析环境&#xff1a;Python开发环境搭建全攻略 在数据分析的世界中&#xff0c;搭建一个稳定且高效的Python开发环境是至关重要的。本文将介绍三种主要的环境搭建方式&#xff1a;使用pip、Anaconda和Miniconda。 1. 使用pip从清华镜像安装Python包 pip是Pytho…

02--nginx代理缓存

前言&#xff1a;比较常用的用法反向代理&#xff0c;和缓存的一些操作&#xff0c;用虚拟环境复刻出来&#xff0c;里面参数不用详细记录&#xff0c;用作复习&#xff0c;使用时直接查找即可。环境搭建过程参考前一篇文章nginx基础。 1、基础环境 IP角色作用192.168.189.143…

编译安装gcc-11及可能遇到的bug

编译安装脚本 GCC_VERSION11.1.0 PACKAGE_DIR/path/to/gcc/source/code GCC_DIR$PACKAGE_DIR/gcc-$GCC_VERSION GCC_INSTALL_DIR/path/to/install/gccmkdir -p $GCC_INSTALL_DIR cd $GCC_INSTALL_DIR rm -rf * cd $PACKAGE_DIR rm -rf gcc-$GCC_VERSION if [ ! -f "gcc-$…