可能有些朋友第一反应是,那肯定是编译不过喽:

// fun.c
#include
void func()
{printf("编程珠玑\n");
}// main.c
#include
void func()
{printf("公众号\n");
}
int main(void)
{func();return 0;
}

编译:

$ gcc -o main main.c fun.c
/tmp/ccKeACRk.o: In function `fun':
fun.c:(.text 0x0): multiple definition of `fun'
/tmp/cc4ezgqh.o:main.c:(.text 0x0): first defined here
collect2: error: ld returned 1 exit status

可以看到这里报错了,因为fun重复定义了。

但是重复定义就会报错,会编译不过吗?不全是!

再看下面的代码:

//var.c
int num;
void change()
{num = 1023;
}//main.c
#include
void change();
int num = 1024;
int main(void)
{printf("before:num is %d\n", num);change();printf("after:num is %d\n", num);return 0;
}

输出结果:

before:num is 1024
after:num is 1023 

从结果中可以看到,虽然num被定义了两次,但是仍然可以编译通过,并且正常运行。这又是为什么呢?

符号

在说明今天重点分享的内容之前,先简单了解一下什么是符号。ELF文件生成的最后阶段会经历链接,而链接阶段正是基于符号才能完成。每个目标文件都会有一个符号表。而链接过程正是通过符号表中的符号,将不同的目标文件“粘”在一起,形成最后的库或者可执行文件。要查看一个目标文件的符号信息也很容易:

// symbol.c
#include
int symbol = 1024;
int func_symbol()
{return 0;
}

编译:

$ gcc smbol.c #编译
$ nm symbol.o #查看符号信息
0000000000000000 T func_symbol
0000000000000000 D symbol

通过nm命令就可以查看符号信息,这里就有我们的func_symbol函数和全局变量symbol的符号。

除了上面提到的全局符号,目标文件中还有其他符号信息,不过这不是本文关注的重点。

强符号与弱符号

对于C/C 语言来说,编译器默认函数和初始化了的全局变量为强符号,未初始化的全局变量为弱符号。当然也可以通过

__attribute__((weak))

来定义一个强符号为弱符号。

通过下面的例子来看看哪些是强符号,哪些是弱符号:

#include
int weak; // 未初始化全局变量,弱符号
int strong = 1024; // 已初始化全局变量,强符号
__attribute__((weak)) int weak1 = 2222; // 使用标识修饰的弱符号
int main(void)
{printf("编程珠玑\n");return 0;
}

注意,这里的强符号与弱符号都是针对定义来说的。

同名时,用哪个?

对于多重定义,即标题提到的变量重名时,链接器有它的处理规则:

  • 1.强符号不允许重复

  • 2.有一个强符号和多个弱符号,使用强符号

  • 3.多个弱符号,则随意选择一个

关于第一点,在最开始的例子中你已经见到了,最常见的情况就是你重复定义了变量或者函数等等。

而第二点也有示例,示例中,虽然定义了两个num,但是var.c中未初始化的num是弱符号,main.c中的num是强符号,这种情况下编译正常。只是最终会使用强符号的num。

再看一个第三点的例子也是类似,当其中main.c的num无初始化时,也是可以编译过的。这种情况下的误用也就罢了,如果是重复的符号,但是类型不同,问题就更大了,即var.c的内容如下:

//var.c
double num;
void change()
{num = 1023;
}

这里的num变成了double,再次编译运行,你会发现意想不到的结果:

before:num is 1024
after:num is 0 

为什么修改后是0?原因在于double类型的数据存储与int类型数据存储格式不一样,且它们占用空间长度都不一样,在本文例子中,double占用8字节,而int占用4字节。

总之,这不是我们想要的结果,最终的后果可能比我们想象的要严重,要更难发现。

总结

如非特殊需求,应该尽量避免出现全局变量同名,以免造成意料不到的结果,例如使用变量时最小范围定义,即尽可能避免全局变量,或者使用命名空间(如C 中)。

声明:

本文于网络整理,版权归原作者所有,如来源信息有误或侵犯权益,请联系我们删除或授权事宜。

函数或全局变量重复定义时会怎样?相关推荐

  1. 解决C/C++语言中全局变量重复定义的问题

    前言 今天,在整理自己的代码的时候,考虑到我写的代码从一至终都是在一个cpp文件里面.于是,想把自己的代码中的各个模块分离开来,以便更好地阅读和管理. 遇到的问题 我的做法是: 宏定义.结构体定义.函 ...

  2. c51语言定义全局变量,全局变量的定义和使用

    在用VB开发软件时,经常需要在不同的窗体间共享数据,但在过多的使用全局变量时不便于软件的调试和修改.通常有两种解决方法. 第一种方法:定义全局变量,然后在各个窗体中直接使用,例如 Public str ...

  3. extern 用法,全局变量与头文件(重复定义)

    转自 https://www.cnblogs.com/chengmin/archive/2011/09/26/2192008.html 当你要引用一个全局变量的时候,你就要声明,extern int  ...

  4. 条件include_10_函数原型、条件编译与重复定义错误 | C语言入门

    10_函数原型.条件编译与重复定义错误 KeyPoint 函数原型,如果未定义,编译器会猜测为都是int类型 条件编译 #ifndef __FUNC_H__#define __FUNC_H__doub ...

  5. C++ link2005 error 错误 解决方法汇总(一般重复定义,如果都是不就是 函数定义和实现没有分离)...

    一般都是重复定义. 可以按照VS给出的信息去找相关的变量或者宏定义,还有函数. 这里需要注意include,不要重复include,不要重复定义宏. 但上述这些,都是很好理解的-- 如果大家按照上边说 ...

  6. C++中不允许重复定义全局变量

    文章目录 1 C++中不允许重复定义全局变量 1 C++中不允许重复定义全局变量 C++中不允许重复定义全局变量: 在C语言中,重复定义多个同名的全局变量是合法的. 在C++中,不允许定义多个同名的全 ...

  7. Python基础day04【函数(定义与调用、文档说明、传参函数、全局变量、返回值、嵌套调用)】

    视频.源码.课件.软件.笔记:超全面Python基础入门教程[十天课程]博客笔记汇总表[黑马程序员] Python基础day04[字典]    目录 3.函数 函数的定义和调用 函数定义 PEP8规范 ...

  8. 多文件中函数的重复定义

    简单来说,首先,头文件被include到相应的cpp文件中,然后,将cpp编译生成obj文件,然后将obj文件连接生成exe文件. 所以,由上面的编译过程,要注意以下几点: 1.include的应该是 ...

  9. python 函数递归一次增加一次变量_python3--函数(函数,全局变量和局部变量,递归函数)...

    1.1函数 1.1.1什么是函数 函数就是程序实现模块化的基本单元,一般实现某一功能的集合. 函数名:就相当于是程序代码集合的名称 参数:就是函数运算时需要参与运算的值被称作为参数 函数体:程序的某个 ...

最新文章

  1. 一线城市中高端人才月薪超 2 万,电子通信行业应届生薪资涨幅最高
  2. 如何将UI5应用部署到Fiori On-Premise和On-Cloud的Launchpad上去
  3. html读取文本框变量,Html和文本框元件上变量
  4. AngularJS开发指南4:指令的详解
  5. Intel Realsense D435 测试视频流的直方图均衡化
  6. Android源代码下载方法具体解释
  7. 两篇同年硕士论文高度雷同!电子科技大学回应:启动调查!
  8. 信息学奥赛一本通 1342:【例4-1】最短路径问题
  9. Mac的一些使用技巧
  10. 图层照片如何扣头发丝
  11. 百分点感知智能实验室:语音识别技术发展阶段探究
  12. ​24小时企业级微信小程序全套开发视频教程
  13. Python Flask微信公众号开发
  14. 安森美的全局快门图像传感器解决机器视觉的成像需求
  15. 2207.16吃货联盟设计大纲和全部代码
  16. Python程序员必备的四款开发工具
  17. 反垄断法正确实施的三大关键点
  18. 灰狼算法(GWO)优化支持向量机的数据回归预测,GWO-SVM回归预测,多输入单输出模型。
  19. 如何实现产销平衡_如何让产品产销平衡,利润最大化?
  20. Android 12 “致命”崩溃解决之路

热门文章

  1. 怎么简单的锁定文件夹_简单性与鲁棒性–在锁定文件处理中展示
  2. 从Spring Data JPA访问EntityManager
  3. 为@Cacheable设置TTL – Spring
  4. jax-rs jax-ws_Tomcat上具有JAX-WS的Web服务
  5. jsf集成spring_JSF 2,PrimeFaces 3,Spring 3和Hibernate 4集成项目
  6. 如何与Java 8,NetBeans Platform 8,Jenkins,Jacoco和Sonar进行持续集成
  7. 使用ANTLR和Java创建外部DSL
  8. 从迁移到Java 7的小技巧
  9. 自定义Spring Data JPA存储库
  10. JBoss AS 7 EJB3池配置