前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >const关键字你搞懂了?这个编译问题你可能都搞不明白!

const关键字你搞懂了?这个编译问题你可能都搞不明白!

作者头像
编程珠玑
发布2020-11-09 15:08:53
7140
发布2020-11-09 15:08:53
举报
文章被收录于专栏:编程珠玑编程珠玑

作者:守望先生

ID:shouwangxiansheng

const变量能被其他文件extern引用吗?为什么?

先来看一段代码:

// 来源:公众号编程珠玑
// main.cc
#include<stdio.h>
// 引用外部定义的const_int变量
extern const int const_int;
int main()
{
    printf("const_int:%d\n",const_int);
    return 0;
}
// const.cc
// 定义const 变量
const int const_int  = 10;

编译链接:

 $ g++ -o main main.cc const.cc
 /tmp/ccWHAXxB.o: In function `main':
main.cc:(.text+0x6): undefined reference to `const_int'
collect2: error: ld returned 1 exit status

我们发现出现了链接问题,说const_int没有定义的引用,但我们确实在const.cc文件中定义了。

我们去掉const修饰符再编译一次,发现是可以的。从上面这个编译问题,就引出今天要讲的内容了。至于为什么会编译不过,最后再做解释。

当然你会发现,按照C代码去编译,是可以编译出来的。后面再解释。

链接属性

我们都知道,C/C++代码的编译通常经过预编译,汇编,编译,链接(参考hello程序是怎么生成的)通常会有变量会有三种链接属性:外部链接,内部链接或无链接

具有函数作用域,块作用域或者函数原型作用域的变量都是无链接变量;具有文件作用域的变量可以是内部链接也可以是外部链接。而外部链接变量可以在多个文件中使用,内部链接变量只能在一个编译单元中使用(一个源代码文件和它包含的头文件)。

关于作用域,也可以参考《全局变量,静态全局变量,局部变量,静态局部变量》。 说了这么多,举个具体的例子:

 // 来源:公众号【编程珠玑】
 // 作者:守望先生
 #include<stdio.h>
 int external_link = 10;  // 文件作用域,外部链接
 static internal_link = 20; // 文件作用域,内部链接
 int main()
 {
     int no_link = 30;   // 无链接
     printf("%d %d %d \n",external_link,internal_link,no_link);
     return 0;
 }

这里无链接变量还是比较好区分的,只要不是文件作用域的变量,基本是无链接属性。而文件作用域变量是内部链接还是外部链接呢?只要看前面是否有static修饰即可。当然对于C++,还要看是否有const修饰,后面我们再说。

如何知道某个变量是什么链接属性?

举个例子,在前面的代码中,我们按照C代码进行编译:

 $ gcc -c const.c 
 $ nm const.o |grep const_int
 0000000000000000 R const_int

nm命令在《linux常用命令-开发调试篇》中略有介绍,它可以用来查看ELF文件的符号信息。

从这里的结果可以看到const_int前面是R修饰的, R:该符号位于只读数据区,READONLY的含义

该字母大写,其实也是表示它具有外部链接属性

再看看按照C++代码编译:

 $ g++ -c const.c
 $ nm const.o |grep const_int
 0000000000000000 r _ZL9const_int

可以看到,它的修饰符也是r,但是是小写的r,小写字母表示该变量具有内部链接属性

nm命令非常实用,但本文不是重点。

const关键字

说到const关键字,在《const关键字到底该怎么用》和《C++中的const与C中的const有何差别?》中已经分析过了,这里简单说一下,被const关键字修饰的变量,表明它是只读的,不希望被修改。

extern关键字

extern关键字可以引用外部的定义,想必很多朋友已经很熟悉了,举个例子,如果把最开始的例子中的const关键字去掉,main.cc中的extern的意思,就是说有一个const_int变量,但是它在别的地方定义的,因此这里extern修饰一下,这样在链接阶段,它就会去其他的编译单元中找到它的定义,并链接。

当然,还有一个不太被关注的作用是,在C++中,它可以改变const变量的链接属性。

是的,在C++中,它改变了const_int的链接属性。我们可以修改const.c的内容如下:

 extern const int const_int  = 10;

然后再查看一下:

  $ nm const.o |grep const_int
 0000000000000000 R const_int

发现没有,它前面的修饰变成大写的R了,所以这个时候,你再编译,就能编译过,而不会报错了,对于C,它本来就是外部链接属性,所以根本不会报错。

extern还有另外一个用法: 《C++是如何调用C接口的》?

解疑

所以,链接报错的通常问题就是找不到定义,原因无非就是:

  • 未定义
  • 在其他地方定义了,但是不具备外部链接属性
  • 定义了,具备外部链接属性,但是链接顺序有问题

由于在C++中,被const修饰的变量默认为内部链接属性,因为链接会找不到定义。

总结

本文从一个编译问题,引出了很多内容,包括:

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-11-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 编程珠玑 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 链接属性
  • 如何知道某个变量是什么链接属性?
  • const关键字
  • extern关键字
  • 解疑
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档