• C语言关键字extern


    一.前言

    最近写一个代码,编译时老是出现:multiple definition,想到太恼火了,那就枚举一下可能出现的情况,再总结一下。

    二.代码

    编译环境:gcc version 9.4.0 (Ubuntu 9.4.0-1Ubuntu1~20.04)

    • a.c
    #include 
    #include "a.h"
    #include "b.h"
    
    int add()
    {
        printf("%d\n", ++aim);    
    }
    
    int main()
    {
        dec();
        add();
        dec();
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • a.h
    #ifndef _A_H
    #define _A_H
    
    int add();
    
    #endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • b.c
    #include 
    #include "b.h"
    
    int dec()
    {
        printf("%d\n", --aim);    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • b.h
    #ifndef _B_H
    #define _B_H
    
    int dec();
    
    #endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • makefile
    TARGET:=./out
    obj-y:=a.c b.c
    OBJS=$(patsubst %.c, %.o, $(obj-y))
    all:clean $(TARGET)
    	@./out
    $(TARGET):$(OBJS)
    	@gcc -o $(TARGET) $(OBJS)
    %.o:%.c
    	@gcc $(CFLAGS) -c $<
    clean:
    	@rm -rf $(OBJS) $(TARGET)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    三.排列组合

    int aim = 1extern int aim放在不同的文件下有4×3=12种情况,那么我们一一试试:

    a.ca.hb.cb.h结果
    aim=1extern××In function 'dec': 'aim' undeclared
    aim=1×extern×输出0 1 0 可用√
    aim=1××extern输出0 1 0 可用√
    externaim=1××In function 'dec': 'aim' undeclared
    ×aim=1extern×输出0 1 0 可用√
    ×aim=1×extern输出0 1 0 可用√
    extern×aim=1×输出0 1 0 可用√
    ×externaim=1×输出0 1 0 可用√
    ××aim=1extern输出0 1 0 可用√
    extern××aim=1b.o:multiple definition of "aim";
    a.o:fisrt defiined here
    ×extern×aim=1b.o:multiple definition of "aim";
    a.o:fisrt defiined here
    ××externaim=1b.o:multiple definition of "aim";
    a.o:fisrt defiined here

    只定义aim=1

    a.ca.hb.cb.h结果
    aim=1×××In function 'dec': 'aim' undeclared
    ×aim=1××In function 'dec': 'aim' undeclared
    ××aim=1×b.o:multiple definition of "aim";
    a.o:fisrt defiined here
    ×××aim=1b.o:multiple definition of "aim";
    a.o:fisrt defiined here

    .h声明int aim,在.c实例化int aim = 1

    a.ca.hb.cb.h结果
    aim=1aim;××In function 'dec': 'aim' undeclared
    aim=1×aim=1×b.o:multiple definition of "aim";
    a.o:fisrt defiined here
    aim=1××aim;输出0 1 0 可用√
    ×aim;aim=1×输出0 1 0可用√
    ×aim;×aim;输出-1 0 -1 也可用

    理想的标准化写法:

    a.ca.hb.cb.h结果
    aim=1aim;×aim;输出0 1 0可用√
    aim=1aim;×extern aim;输出0 1 0可用√
    ×aim;aim=1aim;输出0 1 0可用√
    ×extern aim;aim=1aim;输出0 1 0可用√

    再来个特殊的:

    a.ca.hb.cb.h结果
    ×int aim;static int aim=1×0 1 -1(这里的aim就是两个变量了哦static的全局作用)
    ×int aim;×static int aim=1static declaration of 'aim' follows non-static declaration
    ×static int aim;×static int aim=10 2 -1
    static int aim;××static int aim=10 2 -1

    四.恶心人的情况

    考虑到工程里面经常会出现头文件互相引用的情况,那么将b.c的代码增加一行#include "a.h",现在他们两个互相引用了。那么重复刚才的那些实验:

    a.ca.hb.cb.h结果
    aim=1extern××输出0 1 0 可用√
    aim=1×extern×输出0 1 0 可用√
    aim=1××extern输出0 1 0 可用√
    externaim=1××b.o:multiple definition of "aim";
    a.o:fisrt defiined here
    ×aim=1extern×b.o:multiple definition of "aim";
    a.o:fisrt defiined here
    ×aim=1×externb.o:multiple definition of "aim";
    a.o:fisrt defiined here
    extern×aim=1×输出0 1 0 可用√
    ×externaim=1×输出0 1 0 可用√
    ××aim=1extern输出0 1 0 可用√
    extern××aim=1b.o:multiple definition of "aim";
    a.o:fisrt defiined here
    ×extern×aim=1b.o:multiple definition of "aim";
    a.o:fisrt defiined here
    ××externaim=1b.o:multiple definition of "aim";
    a.o:fisrt defiined here

    只定义aim=1

    a.ca.hb.cb.h结果
    aim=1×××In function 'dec': 'aim' undeclared
    ×aim=1××b.o:multiple definition of "aim";
    a.o:fisrt defiined here
    ××aim=1×In function 'add': 'aim' undeclared
    ×××aim=1b.o:multiple definition of "aim";
    a.o:fisrt defiined here

    .h声明int aim,在.c实例化int aim = 1

    a.ca.hb.cb.h结果
    aim=1aim;××输出0 1 0 可用√
    aim=1×aim=1×b.o:multiple definition of "aim";
    a.o:fisrt defiined here
    aim=1××aim;输出0 1 0 可用√
    ×aim;aim=1×输出0 1 0可用√
    ×aim;×aim;输出-1 0 -1 也可用

    理想的标准化写法:

    a.ca.hb.cb.h结果
    aim=1aim;×aim;输出0 1 0可用√
    aim=1aim;×extern aim;输出0 1 0可用√
    ×aim;aim=1aim;输出0 1 0可用√
    ×extern aim;aim=1aim;输出0 1 0可用√

    五.分析与用法

    表格不是所有的情况,但至少可以大致的理解到.c文件和.h文件的作用域。
    作为最方便的使用方法:

    • 某个变量需要在其他文件里使用时,直接在其他文件的.hextern声明就好(当然在.c也可以)。
    • .h不要用来实例化,单纯的声明即可。
    • 同一个变量,当它.h声明后,这个变量的作用域就在该.c和.h范围内有效,如果被其他文件的.h嵌套后,这个变量的作用域就从该文件范围拓展到该文件和其他文件。如果在两个文件下有实例化,那么也就等于在同一个作用域有两个实例化,但是这个作用域没有先后顺序的,所以就存在了“同时”给一个变量赋两个值的情况,故会提醒multiple definition

    • 实例化,是指 int aim = 2;。赋值是aim = 2; 两者有很大不同,需要区分开。

    六.总结

    声明可以拓展作用域,所以可以存在多个,实例化只能有一次。

  • 相关阅读:
    图表展示X轴标题展示不全
    Spring(SpringBoot)--控制Bean注入的顺序
    使用cpolar内网端口映射技术实现U8用友ERP本地部署的异地访问
    Maven中的继承与聚合
    Linux下安装Redis
    快速完成工信部APP备案流程_以阿里云APP备案为例
    【PAT甲级】1005 Spell It Right
    pycharm配置python3.8版本专门用于undecteded_chromedriver测试
    jvm学习第三天
    LeetCode 第155题:最小栈(Java解法)- 剑指 Offer 30. 包含min函数的栈
  • 原文地址:https://blog.csdn.net/u011017694/article/details/125914089