(仅供自我学习使用)C++学习记录-3

        Cherno第七课,主要讲解链接器是如何工作的。

 

目录

        1. LNK error--缺少main函数

  · 阅读输出窗口的报错

2. C error--未添加函数声明 

3. LNK error--函数名使用错误

4. 函数名重复出现 

(1)同一个文件下重复定义函数 

(2)在不同文件下定义同一个函数

(3)更隐形的函数重复定义问题


        1. LNK error--缺少main函数

        创建一个exe文件,需要通过编译和链接两步。而每个exe文件都需要一个入口点,程序从入口点开始运行。链接也是从入口点进入的。而在C++程序内的入口点就是main函数

        接下来第一个实验:测试当工程内缺少main函数时会产生什么。

        在工程里仅建立源文件Math.cpp,代码如下:

         当只是进行编译时,输出窗口并不会报错;而在调试之后,报错才会产生。这是因为编译只是对每个cpp文件处理为obj文件;而调试时链接才会进行,链接以main函数为进入口,本工程缺少main函数,LNK error产生。

编译时没有报错

 调试后产生LNK error

        对于这种错误的解决办法就是将main函数写出来。程序如下就可以运行了:

运行结果 

        P.S.一般程序的入口都是main函数。但是我们也可以在工程属性里进行更改,实现自定义主函数。

         · 阅读输出窗口的报错

        这个补充是学会看报错,知道是产生了什么类型的错误。输出窗口报错主要由两种组成——C开头的编译错误,compile error;LNK错误,link error。

(1)Compile error

       

         编译后的输出窗口:

        

 (2)Link error

        LNK error在第一个实验中已经出现。

 

2. C error--未添加函数声明 

        如果我们新创建一个源文件Log.cpp,并将上面提到的Log函数移动到这个里面。此时由于main函数调用了multiply函数,而multiply函数又需要调用Log函数。由于Log函数被放置在另一个源文件Log.cpp中,所以对于Math.cpp,它并不知道文件里有Log。此时必须要在Math.cpp里声明Log函数,才能形成链接去成功调用Log。

 

把Log函数移动到Log.cpp,没有添加声明就会产生报错。

 添加了声明后程序就可以正常运行了

 

3. LNK error--函数名使用错误

      

         第一种情况,函数链接出错。如果函数名称写的不是Log,而错写成了Logr之类的,那么最后会产生LNK错误。因为在外部找不到Log。

         再进一步研究链接函数出错。第二种情况,如果在主函数不使用Log函数(即删去Log声明,在Multiply函数中也取消对Log的使用),那么主函数最终也不需要对Log函数进行链接,Log.cpp里定义的Logr函数也不会起作用了,这种情况下,程序最终是可以运行的

         其次第二种情况,如果我们直接取消Multiply函数的使用(在主函数里不调用Multiply,也就是整个运行中不使用Log函数),这种情况下程序会报错

         这种情况会出错的原因是:从技术上讲,就算Multiply函数在Math.cpp这个文件里面没有被使用,但是不排除函数会在其他文件里被使用。所以链接器还是会去链接Multiply这个函数。

        为了避免这个问题,我们就需要告诉编译器:Multiply这个函数只会在本文件中使用。这样就不会对Multiply产生多余的外部链接。为实现这个功能,我们可以在函数前面加上“static”。(static让Multiply变为内部链接函数):


补充:static的3个作用

  1. 修饰局部变量。变量作用域不变,但生命周期延长。(每次进入函数,该变量还储存着上次函数运行的结果。)
  2. 修饰全局变量。全局变量本身具有外部链接属性--可以在不同文件中使用;被static修饰后,就被修改为内部链接属性--该全局变量只能在所在源文件中使用。
  3. 修饰函数。和修饰全局变量相似。static修饰函数后,该函数的外部链接属性就变为了内部链接属性--该函数只能在所在源文件中使用。

这样是可以运行的

 

4. 函数名重复出现 

        这个大的错误类型里又大概分有3种不同类型的错法,每种错法对应的原因各不相同。

(1)同一个文件下重复定义函数 

        先看正常的代码如下:

 

         假设在Log.cpp里,Log函数重复出现两次。这样会出现C error——重复在一个文件里出现的代码是无效的。解决方法就是,删掉一个Log函数即可。

(2)在不同文件下定义同一个函数

        在正常代码的基础上,如果我们在Math.cpp里又重新定义一遍Log:

         这样程序运行时依旧会报错。不过这次出现的是LNK error,原因是链接不知道应该去链接哪一个Log函数了(产生了二义性)。解决方法就是只留下一个Log函数即可。

 (3)更隐形的函数重复定义问题

        先给出例子:在正常程序的背景下,新建一个Log.h来记录Log函数的功能;然后将Log.cpp里的函数改为InitLog函数,其内容要引用Log.h头文件;最后在Math.cpp中也用Log.h来代替先前Log函数的功能,开头也要引用Log.h的头文件。程序如下:

         这个程序乍一看好像没什么问题。功能就是输出一个Multiply和3*8的结果,然后Log.cpp里的功能完全不用。但是,这样的程序运行时会出错:

         原因在于,我们在Log.cpp和Math.cpp中都inlcude了Log.h头文件,这其实导致在两个文件里都对Log函数进行了定义,导致函数重复定义,链接出错。

        要想纠正这个错误,有三种方法:

solution1 添加static修饰

        在头文件里的Log函数定义前加上static。这样在后面文件中引用的Log函数都只能在函数内部使用,对其他obj文件不可见。

 

 solution2 添加inline

        在头文件里的Log函数定义前加上inline。可惜的是我还没有学到inline,暂时不知道这是什么......挖个坑。

solution3 将定义只放到一个翻译单元中去

        一个翻译单元——一个cpp文件。

        在头文件里只留下函数声明,而将函数主体放到Log.cpp中去(或者Math.cpp,总之是一个翻译单元中去)。这样每次调用头文件得到的都是函数声明,而函数声明又会链接到翻译单元里的Log函数中去。程序最终可以运行。

 

 


总结:

        写下来感觉前半部分知识容易理解,但到了最后关于inline和转存到翻译单元中去两方面的理解还是觉得不够深入。inline也许在后期就可以再次学到,尤其要注意栈、堆、静态区等相关知识,和c要联系起来对比记忆。还有就是对于static的应用要加强理解。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值