C/C++——声明/定义/内联函数/类 在编译环节的具体实现

news/2024/6/15 17:36:53 标签: html, java-ee, myeclipse
htmledit_views">

目录

1.什么是声明,什么是定义—参考书籍《C语言深度解剖》

1.1 举个例子:

1.2 什么是定义?

1.3  什么是声明

2.内联函数

2.1 观察非内联函数的调用汇编说明

 2.2 观察内联函数的调用汇编说明

2.3 内联函数特性及分析

2.4 内联函数的定义和声明

3. 类、以及类中内联函数 / 及 / 成员方法的声明

3.1 类

3.2 类和内联函数的结合使用

3.3 类成员方法的声明和定义


1.什么是声明,什么是定义—参考书籍《C语言深度解剖》

1.1 举个例子:

(A)int i;
(B )extern int i 

 哪个是定义?哪个是声明?或者都是定义或者都是声明?

1.2 什么是定义?

所谓的定义就是(编译器)创建一个对象,为这个对象分配了一块内存,并给它取上一个名字,这个名字就是我们经常所说的变量名或对象名

注意:

  1.         这个名字 一旦和这块内存匹配起来,它们就同生共死, 终生不离不弃;并且这块内存的位置也不能被改变。 
  2.         一个变量或对象在一定的区域内(比如函数内、全局等)只能被定义一次; 如果定义多次,编译器会提示用户重复定义了同一个变量或对象。

 

 在头文件是否可以定义全局变量?

1.3  什么是声明

有两重含义:

  1.         告诉编译器 ,这个名字已经匹配到一块内存上了 ( “ 伊人已嫁 ,吾將何去何从?何以解优,唯有稀粥”),下面的代码用到变量或对象是在别的地方定义的。声明可以出现多次。
  2.         告诉编译器 ,这个名字已被预定了,别的地方再也不能用它来作为变量名或对象名。比如,如果图书馆自习室的某个座位上被放了一本书,就表明这个座位已经有人预定,别人不允许使用这个座位,其实这个时候占座位的本人并没有坐在该座位上。这种声明最典型的例子就是函数参数的声明,例如:void fin( int i , char c );

 编译器是否会报错?在yuan.c使用i是否成功?在yuan.c中仍声明i,是否报错?

通过解释 ,我们可以很清楚地判断上述

1.1举例中 :

        (A )是定义 ; ( B )是声明。

        extern 用来声明外部符号
        记住,定义和声明最重要的区别:定义创建了对象并为这个对象分配了内存,声明没有分配内存(“ 一个抱伊人,一个喝稀粥” )。

1.2举例中

        头文件不能定义全局变量。

        在其他文件包含头文件时,例如一个函数功能实现模块,一个测试模块,首先在预编译期间,会完成头文件替换,而此时两个文件都含有 int i ;在链接期间,就会报链接 Link 错误,因为 i 重定义。

        是否可以用其他C语言方法解决?

        可以使用static,使其具有内部链接属性,在编译期间,链接属性只在当前的obj文件可见,而引用其头文件在预编译期间会进行头文件替换,替换完成后是两个完全域不相同且不互相影响的具有内部链接属性的量,地址不同,在编译时不会进行符号汇总,所以汇编链接时不产生对应的符号表,不会重定义。

1.3举列中

        不会报错

        因为声明只是告诉编译器,这个名字匹配到了一块内存,而在预编译链接头文件,声明,会产生对应的符号汇总,在编译期间声明得变量会进入符号表,在链接期间通过符号表汇编指令 call地址 进行链接,因此地址都是一样的。

2.内联函数

以inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数调用建立栈帧的开销,内联函数提升程序运行的效率。

2.1 观察非内联函数的调用汇编说明

在属性页面通过设置,观察汇编代码中是否存在 call 指令,如果出现,说明内联函数并未完成替换

 示例代码 1:

int Add(int x, int y)
{
	return x + y;
}
int main()
{
	int ret = Add(1, 2);
	return 0;
}

汇编指令:

                第一步:

                 第二步:

                 第三步:

 以上便是非内联函数调用说明

 2.2 观察内联函数的调用汇编说明

 示例代码 2:

inline int Add(int x, int y)
{
	return x + y;
}
int main()
{
	int ret = Add(1, 2);
	return 0;
}

汇编指令:

 通过汇编指令过程可知,内联函数在此操作完成的时替换,直接替换函数操作

2.3 内联函数特性及分析

1. inline是一种以空间换时间的做法,如果编译器将函数当成内联函数处理,在编译阶段,会用函数体替换函数调用,缺陷:可能会使目标文件变大,优势:少了调用开销,提高程序运行效率。

2. inline对于编译器而言只是一个建议,不同编译器关于inline实现机制可能不同,一般建议:将函数规 模较小(即函数不是很长,具体没有准确的说法,取决于编译器内部实现)、不是递归、频繁调用的函数 采用inline修饰,否则编译器会忽略inline特性。

2.4 内联函数的定义和声明

内联函数的定义和声明是不能分开的,必须在同一文件中,才能完成替换。

代码分析:

 原理分析:

在预编译期间,编译器会进行以下操作并生成 .i 文件

1.头文件包含 #include——预处理指令,2.#define定义符号的替换——预处理指令

3.注释删除也是在此阶段完成的

此时在yuan.c文件中,会有内联函数的声明定义,并进行替换,替换完成后,在编译完成时,并不会将内联函数写入符号表,因此其他文件无法调用内联函数。

而在test.c文件中,只有内联函数的声明,(在没有链接之前),生成对应的函数符号表

在链接期间,内联函数定义的文件已经完成此文件内,函数的替换,并没有写入相应的符号表,此时test.c文件想要调用函数,因此会报链接错误,无法解析的外部符号。

3. 类、以及类中内联函数 / 及 / 成员方法的声明

3.1 类

可知类的两种定义方式:

1. 声明和定义全部放在类体中,需注意:成员函数如果在类中定义,编译器可能会将其当成内联函数处理。

2. 类声明放在.h文件中,成员函数定义放在.cpp文件中,注意:成员函数名前需要加类名::

3.2 类和内联函数的结合使用

由类的定义方式可知,成员函数如果在类中定义,C++在编译会根据其指定的多少判断其是否按照内联函数进行处理。而内联函数的处理中定义和声明是不能分开的,由call指令的是否执行,便可判断成员函数其是否按照内联函数进行处理。

代码分析1:此时类方法的定义和声明分开进行;

 汇编分析:

 由      call 函数         指令可以看出来,类中函数的分离,便需要通过call 调用函数

代码分析2:此时类方法和声明同时进行,通过反汇编判断是否是内联函数

 

由此看出,在类方法定义和声明在同一文件中,类方法的调被当作内联函数完成的是替换

3.3 类成员方法的声明和定义

类方法的声明便是在类里进行声明的,类成员方法并不占用空间,只有类实例化时,并调用类成员方法时才会开辟空间

 类定义了一个新的作用域,类的所有成员都在类的作用域中。在类体外定义成员时,需要使用 :: 作用域操作 符指明成员属于哪个类域。

class Person
{
public:
	void PrintPersonInfo();
private:
	char _name[20];
	char _gender[3];
	int _age;
};



// 这里需要指定PrintPersonInfo是属于Person这个类域
void Person::PrintPersonInfo()
{
	cout << _name << " " << _gender << " " << _age << endl;
}


http://www.niftyadmin.cn/n/177923.html

相关文章

算法---找树左下角的值

题目 给定一个二叉树的 根节点 root&#xff0c;请找出该二叉树的 最底层 最左边 节点的值。 假设二叉树中至少有一个节点。 示例 1: 输入: root [2,1,3] 输出: 1 示例 2: 输入: [1,2,3,4,null,5,6,null,null,7] 输出: 7 提示: 二叉树的节点个数的范围是 [1,104] -231 …

打造有竞争力的SaaS 营销策略,赢得客户和市场份额

事实上&#xff0c;SaaS 营销不过是推广您的产品以提高产品知名度并将其销售给更多受众的过程。您可以通过各种数字渠道和内容营销平台来营销您的 SaaS 业务。 SaaS内容营销 内容营销是任何 SaaS 业务中最受欢迎和最有效的营销策略之一。最重要的是要确保您发布的内容符合目标…

2023年多领域控股行业研究报告

第一章 行业概况 1.1 多领域控股概况 多领域控股是指同时在三个或更多经济领域内控股投资&#xff0c;但没有单个业务贡献一半以上收入的企业集团&#xff0c;旗下通常有多个从事不同业务、各自独立运作的子公司。多领域控股公司通常通过收购或者战略合作的方式&#xff0c;获…

【刷题之路】LeetCode 746. 使用最小花费爬楼梯

【刷题之路】LeetCode 746. 使用最小花费爬楼梯一、题目描述二、解题方法——动态规划思路分析代码实现改进一、题目描述 原题连接&#xff1a; 746. 使用最小花费爬楼梯 题目描述&#xff1a; 题目描述&#xff1a; 给你一个整数数组 cost &#xff0c;其中 cost[i] 是从楼梯…

JAVA---数据输入与输出

1.什么是输入流与输出流 在Java中&#xff0c;把所有的输入和输出都当作流&#xff08;stream&#xff09;来处理。流是按一定顺序排列的数据集合。例如&#xff0c;从键盘或文件输入的数据&#xff0c;向显示器或文件输出的数据等都可以看作是一个个的数据流。 输入数据时&…

网络编程套接字( TCP协议通讯流程)

目录 1、绑定失败问题 2、TCP协议通讯流程 三次握手的过程 数据传输的过程 四次挥手的过程 TCP和UDP对比 1、绑定失败问题 当我们测试网络代码时&#xff0c;先将服务端绑定8080端口运行&#xff0c;然后运行客户端&#xff0c;并让客户端连接当前服务器&#xff1a; 当有客户…

摸着OpenAI过河,百度文心一言能否“重拳出击”?

“文心一言”对标ChatGPT&#xff0c;饱含争议。文心一言作为一款语言大模型&#xff0c;并提出了自己在技术对就业的影响方面的理解&#xff0c;现阶段正处于摸着OpenAI过河的时候&#xff0c;路该如何走&#xff1f; GPT-4太惊艳&#xff0c;压力给到文心一言 这段时间&…

Android生成签名证书(.keystore)

命令行方式&#xff1a; 首先安装JRE环境&#xff0c;然后使用JRE自带的keytool命令生成签名证书。 keytool -genkey -alias testalias -keyalg RSA -keysize 2048 -validity 36500 -keystore test.keystore -alias是证书别名&#xff0c;建议使用英文字母和数字 -keystore是…