zoukankan      html  css  js  c++  java
  • 为什么 extern 使用 const 修饰的变量会编译不过?

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

    先来看一段代码:

    // 来源:公众号编程珠玑
    // main.cc
    #include<stdio.h>
    // 引用外部定义的const_int变量
    extern const int const_int;
    int main()
    {
        printf("const_int:%d
    ",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 没有定义的引用,但我们确实在 http://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 
    ",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 关键字去掉,http://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 修饰的变量默认为内部链接属性,因为链接会找不到定义。

    总结

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

    懒惰不会让你一下子跌到 但会在不知不觉中减少你的收获; 勤奋也不会让你一夜成功 但会在不知不觉中积累你的成果 越努力,越幸运。
  • 相关阅读:
    安装MySQL5.7.19 网上的文章参考 并做了部分修改
    从hadoop一路配置到spark
    java面试问题收集(2)
    JAVA的 IO NIO AIO笔记
    Shiro
    Spring注解使用注意点
    oracle RAC
    spark随笔
    Storm知识点笔记
    真机调试手机程序,电脑插上手机数据线虚拟机中的系统就死掉
  • 原文地址:https://www.cnblogs.com/Rainingday/p/15006390.html
Copyright © 2011-2022 走看看