首页 > 其他分享 >二三、编译器

二三、编译器

时间:2023-12-23 23:55:06浏览次数:37  
标签:htd 定义 int void 二三 编译器 define

二三、编译器

1、One Definition Rule

1)转化单元

我们写好的每个源文件(.cpp,.c)将其所包含的头文件(#include <xxx.h>)合并后,称为一个转化单元。

编译器单独的将每一个转化单元生成为对应的对象文件(.obj),对象文件包含了转化单元的机器码和转化单元的引用信息(不在转化单元中定义的对象)。

最后链接器将各个转化单元的对象文件链接起来,生成我们的目标程序。

比如在对象文件A中包含了定义在其它转化单元的引用,那么就去其它转化单元的对象文件中寻找这个引用的定义来建立链接,如果在所有的对象文件中都找不到这个定义,那么就会生成一个链接错误。

2)未定义行为

在编写代码中,C++标准未做规定的行为,称为未定义行为,未定义行为的结果是不确定的,具体不同的编译器下会有不同的效果,比如

c=2*a++ + ++a*6;

这里先算a++还是先算++a就是一个未定义行为,比如

int x = -25602;

x= x>>2;

x的结果在不同的编译器下是不确定的,因为这也属于未定义行为

3)One Definition Rule(ODR)

ODR是一系列规则,而不是一个规则,程序中定义的每个对象都应有着自己的规则;但是基本上来讲任何的变量、函数、类、枚举、模板、概念(C++20)在每个转化单元中都只允许有一个定义;

在整个程序中,非inline的函数或变量(C++17),有且仅能有一个定义

const声明的变量或函数只在当前的源文件中有效,可以在一个项目的不同源文件定义相同的const变量

4)名称的链接属性

程序中的变量、函数、结构等都有着自己的名字,这些名字具有不同的链接属性,链接器就是根据这些链接属性来把各个对象文件链接起来的。链接属性分为以下三种

①内部链接属性:该名称仅仅在本转化单元中有效,如static、const声明的变量、函数

②外部链接属性:该名称在其它转化单元中也有效。通过extern关键字可以定义外部链接属性

③无链接属性:该名称仅仅能够用于该名称的作用域内访问

注:static变量或函数在自己的转化单元有着自己的内存空间,而inline定义的变量只有一个内存地址

2、#define

1)用法一

#define A B          //将标识符A定义为B的别名

#define 整数 int        //将整数替换为int
整数 a{};
//#define实际用法
#include <iostream>
#define _HHHH_ int a  //将_HHHH_ 替换为int a
#define VERSION "V2.0"
int main()
{
	_HHHH_ { 250 };
	std::cout << a << std::endl;
	std::cout << VERSION<<std::endl;
}

2)C++中定义常量的方式

//C++定义常量的方法
const int width{1080};

//C语言中经常通常#define来定义常量
#define width 1080
#define的方式来定义常量存在一个问题,有时候并不安全

3)#define其它写法

#define H  //定义一个标识符H ,代码中的H将会被删除掉
int H a 相当于 int a;

//实际场景应用
#define _in_            //没有实际意义
#define _out_ 
int ave(_in_ int a,_out_ int& b)
{
    return a+b
}

4)取消宏的定义

//语法
#undef H   //

//应用场景
#define _H_
#undef _H_    //删除宏_H_的定义,后面的代码不能使用

注:执行顺序为代码编译的顺序(从上到下),而不是函数调用的顺序

5)定义复杂表达式宏

#define SUM(X,Y) X+Y                 //使用X+Y替换SUM(X,Y)
#define AVE(X,Y) (X+Y)/2             //使用(X+Y)/2替换AVE(X,Y)
#define BIGGER(X,Y) ((X)>(Y)?(X):(Y))

SUM(100,200);
AVE(100,200);
BIGGER(100,200);

//实际应用场景
#define RELEASE(x) delete[] x;x=nullptr

int main()
{
    int* a  = new int[50];
    RELEASE(a);
}

6)定义复杂表达式宏

// #可以将一个标识符参数字符串化
#define SHOW(X) std::cout<<#X          //通过#将X处理成了字符串
SHOW(1234fg);      //相当于std::cout<<"12345fg"

// ##可以连接两个标识符
#define T1(X,Y) void X##Y(){std::cout<<#Y;}
T1(test, 22);
3、namespace

有时候为了方便管理,把相关的函数、变量、结构体等会附加到一个命名空间中

//声明命名空间
namespace t
{
    int value;
}
//访问这个命名空间的变量
t::value

//直接使用命名空间,不推荐
using namespace t;
vlaue=255;

2)全局命名空间

虽有具有链接属性的对象,只要没有定义命名空间,就默认定义在全局命令空间中,全局命名空间中成员的访问不用显示的指定,当局部名称覆盖了全局名称时才需要显示的指定全局命令空间

int a;
::a=250;

3)命名空间的扩展

//第二个htd属于对htd命名空间的扩展,weight和heigth同属于一个命名空间
namespace htd
{
    int weigth{1980};
}
namespace htd
{
    int heigth{1080};
}

4)命名空间的声明

//htd.h
namespace std
{
    extern int height;      //变量声明
    void test();            //函数声明
}

//htd.cpp
#include <iostream>
#include "htd.h"

int htd::heigth{250};      //变量定义
void htd::test()          //函数定义
{
    std::cout<<htd::height;
}

5)命名空间的嵌套

//htd.h
namespace htd
{
    namespace hack               //命名空间的嵌套
    {
        void hackServer();
    }
    
}

//htd.cpp
void htd::hack::hackServer()
{
    ...
}
void htd::sendSms()
{
    ...
}

6)未命名的命名空间

​ 不给命名空间指定名称,将会声明一个未命名的命名空间。未命名的命名空间中声明的内容一律为内部链接属性,包括extern声明的内容,未命名的命名空间仅仅在本转化单元中有效

//t.cpp
void THack()
{
    
}
//x.cpp
namespace
{
    void THack()
	{
    
	}
}	
int main()
{
    THack();
}
//

7)命名空间的别名

namespace htd
{
    void sendSms();
    namespace hack
    {
        void hackServer();
    }
}
namespace hServer=htd::hack;
hServer::hackServer();
4、预处理指令逻辑

所有#开头的代码都是和编译器进行打交道

1)#ifdef

#define _HEIGHT_ 1080       //#ifdef和#endif成对出现
#ifdef _HIGHT_
#else
#endif


//hc.h
#ifdef _HC_        //如果定义了宏_HC_,就执行XXXX里面的代码。如果没有定义,则执行YYYYY
XXXX
#else
YYYYY
#endif

//常见用法
#ifndef _HC_     //如果没有定义了宏_HC_
#define _HC_     //则定义了宏_HC_
#else
#endif
//实际应用场景
#ifdef UNICODE     //如果使用了UNICODE字符集,则使用wchar_t定义变量
wchar_t a;
#else
char a;
#endif

//通过预处理指令进行版本控制
#define VERSION 101

#if VERSION==100           //当VERSION为100时,执行如下逻辑,否则执行else中的逻辑
	void SendSms()
    {
        
    }
#else
	void SendSms()
    {
        
    }
#endif 

2)#elif

//#elif语法
#define _HEIGHT_ 2080
#if _HEIGHT_ == 1080          //针对每一个分辨率执行不同的逻辑
...
#elif _HEIGHT_ == 720
...
#else
...
#endif
    
//预处理指令可以进行简单的计算//当VERSION为100时,执行如下逻辑,否则执行else中的逻辑
	void SendSms()
    {
        
    }
5、预定义宏

1)标准预定义标识符_fun_

编译器支持ISO C99和ISO C++ 11,即可使用该预定义标识符。用于返回函数的名称

__func__   //返回函数的名称

//用法示例
#include <iostream>

int main()
{
	std::cout << __func__ << std::endl;     //返回函数名称,输出main
}

2)标准预定义宏

编译器支持ISO C99和ISO C++17标准,即可使用以下预定义宏

说明
_DATE_ 返回源文件的编译日期
_TIME_ 返回当前转化单元的转化时间(可理解为代码修改的时间)
_FILE_ 返回源文件的名称
_LINE_ 返回当前的行
__cplusplus 当当前单元为C++时(即.cpp文件时),__cplusplus定义为一个整数,否则不是c++文件
#include <iostream>

int main()
{
	std::cout << __func__ << std::endl;     //返回函数名称,输出main
	std::cout << __DATE__ << std::endl;
	std::cout << __TIME__ << std::endl;
	std::cout << __FILE__ << std::endl;
	std::cout << __LINE__ << std::endl;
	std::cout << __cplusplus << std::endl;
}

3)MSVC的预定义宏(可理解为微软的VC编译器中,预定义的一些宏)

说明
_CHAR_UNSIGNED 如果char类型为无符号,该宏定义为1,否则为未定义
_COUNTER_ 用于计数,从0开始,每使用一次都会递增1
_DEBUG 如果设置了_DEBUG宏,代表当前为调试状态/lDd /mDd /mTd该宏定义为1,否则为未定义
_FUNCTION_ 返回函数名称 ,但是不包含修饰名
_FUNCDNAME_ 函数名称 包含修饰名
_FUNCSIG_ 包含了函数签名的函数名
_WIN32 当编译为32位ARM,64位ARM,X68或X64定义为1,否则未定义
_WIN64 当编译为64位ARM或x64定义为1,否则未定义。用于区别
_TIMESTAMP_ 最后一次源代码修改的时间和日期
#include <iostream>

int main()
{
#ifdef _CHAR_UNSIGNED  //如果char为无符号类型,可以通过该预定义宏进行检验
	std::cout << "无符号类型";
#endif
	std::cout << "有符号类型" << std::endl;
	std::cout << __COUNTER__ << std::endl;   //代表计数
	std::cout << __COUNTER__ << std::endl;
	std::cout << __COUNTER__ << std::endl;
#ifdef _DEBUG       //代表调试状态
	std::cout << "调试状态" << std::endl;
	std::cout << __FUNCTION__ << std::endl;  //返回函数名,不包含修饰名
	std::cout << __FUNCDNAME__ << std::endl; //返回函数名,包含修饰名
	std::cout << __FUNCSIG__ << std::endl;  //包含函数签名,即调用、约定等信息
	std::cout << __TIMESTAMP__ << std::endl; //最后一次源代码修改的时间和日期

#endif
#ifdef _WIN32  //用于区分Win32架构或者Win64架构
	std::cout << "X86" << std::endl;
#endif // _WIN32

}

只要【项目属性】-【C/C++】-【代码生成】-【运行库】,选择了调试,则_DEBUG就会显示

注:上述宏只在微软的VS编辑器才可以使用,其它的编译器无法使用

6、调试

​ 我们编写好程序以后,可能存在一些bug和错误,对于语法上错误,编译器能够直接给出提示,而对于逻辑上的错误,编译器不能够直接发现,调试就是一个找错误和改错误的过程

1)调试建议:为了方便调试,在编程风格上提出如下建议

①功能能模块化就模块化

②使用能够体现出具体意义的函数名和变量名

③使用正常的缩进和代码块

④良好的注释习惯

2)利用集成调试器调调试程序

VS2019继承了一个调试器,可以利用断点、流程跟踪等方式来调试自己的程序

断点就是当程序执行到断点位置,程序就会停下来

3)利用其它调试器

OllyDbg

X96Dbg

WinDbg

4)利用预处理指令来输出调试信息

#define _dbg_i    //先定义一个宏

#ifdef _dbg_i    //如果宏存在,则执行下面的代码块
	std::cout<<"调试信息";
#endif
7、assert

assert宏需要头文件cassert

1)assert语法

//assert语法
assert(bool表达式);   //如果括号内的bool表达式为false,则会调用std::abort()函数,弹出下面的对话框,

2)关闭assert

//关闭assert
#define NDEBUG  //可以在当前转化单元关闭assert,但是这个定义必须放在#include <cassert>之前

3)static_assert(静态断言)

//static_assert用于编译时检查条件
static_assert(bool表达式,"Error information");   //先检查表达式,若表达式为假,则输出后面的错误信息。如果表达式为0,则程序是不进行编译的,此处二点bool表达式只能用于常量

//C++17新语法
static_assert(bool表达式);

标签:htd,定义,int,void,二三,编译器,define
From: https://www.cnblogs.com/piaolaipiaoqu/p/17923864.html

相关文章

  • 《TVM编译器原理与实践》新书推荐
    《TVM编译器原理与实践》新书推荐作者:吴建明,吴一昊;出版社:机械工业出版社;出版时间:2023年12月 本书已经出版,目前在淘宝天猫,京东,当当上可以购买。谢谢!天猫:https://detail.tmall.com/item.htm?abbucket=8&id=757068341348&ns=1&spm=a21n57.1.0.0.2b9b523ckBk0aH京东:https://it......
  • JavaScript 引擎 V8 年度回顾:新编译器、修改基础架构、改进 GC……
    V8官方博客回顾了2023年的重要变化:通过创新的性能优化,V8不断突破Web领域的可能性界限。比如引入新的中间层编译器,对顶层编译器基础架构、运行时和垃圾回收进行多项改进,从而全面提升速度。除了性能改进之外,V8团队还为JavaScript和WebAssembly添加了许多新功能。比如通......
  • 2023-12-15 保存文件会生成一个dist包 ==》编译器配置冲突问题
    如题,每次保存文件都会生成个dist包,这个包有时候后会阻碍程序运行,所以必须要解决!在一番排查下,发现是编译器的问题,我原来用的是vscode,换成hbuildex后就不会生成了,我之前还怀疑是终端的问题,然后我在vscode保存文件,用其它终端(powershell)运行项目,结果还是生成了dist包,所以可以确定是v......
  • 浏览器中的编译器和解释器
    之所以存在编译器和解释器,是因为机器不能直接理解我们所写的代码,所以在执行程序之前,需要将我们所写的代码“翻译”成机器能读懂的机器语言。按语言的执行流程,可以把语言划分为编译型语言和解释型语言。编译型语言在程序执行之前,需要经过编译器的编译过程,并且编译之后会直接保留机器......
  • riscv gnu编译器
    官网https://github.com/riscv-collab/riscv-gnu-toolchain编译器基于RISC-V交叉编译器包括32bit和64bit两种类型,其中每种类型又包括裸机版本(newlib)和动态链接库版本(linuxglibc)newlib./configure--prefix=/opt/riscvmakeglibc#默认仅支持64位target./configure--......
  • Termux安装GCC编译器
    cctoolspkgupdatepkginstall-ycoreutilsgnupgcurl-sLo$PREFIX/etc/apt/trusted.gpg.d/cctools.asc--create-dirshttps://cctools.info/public.keyecho"deb[trusted=yes]https://cctools.infotermuxcctools"|tee$PREFIX/etc/apt/sources.list......
  • 编译器上手指南,算子开发及开源项目指导手册,直播课程报名通道限时开启!
    「MegEngine开发者说」直播课程第二期火爆来袭!本期既有技术大佬带来深度学习编译器MegCC的详细教程,又有实习经历丰富的在校同学分享MegEngine算子开发流程及开源经历,手把手带你学习,精彩不容错过!更有直播间有奖答疑,现在报名,即有机会获得MegEngine周边大礼包!关于「MegEngi......
  • go的编译器安装
    中文网:https://studygolang.com/dl 点击下载 next-next后安装路径:D:\Go\  next-next-install gobuild生成可执行文件.exe./可执行文件.exe执行 ......
  • 最佳 C++ 编译器
    最佳C++编译器Incredibuild​已认证账号​关注 134人赞同了该文章 C++是一个“开放”的编程语言,任何人都可以使用自己喜欢的编译器。当然,C++编译器的种类也很多。同样, C++IDE 也不少,我在之前的一个博客中讨论过这个话题。编译和运行C++......
  • vim编译器
    一、vim是什么?vim是从vi发展出来的一个文本编辑器。代码补全、编译及错误跳转等方便编程的功能特别丰富,在程序员中被广泛使用。简单的来说,vi是老式的字处理器,不过功能已经很齐全了,但是还是有可以进步的地方。vim则可以说是程序开发者的一项很好用的工具。 二、vim的使用......