13.强符号和弱符号

我们在编写代码的过程中经常会遇到一种叫做符号重复定义(Multiple Definition)的错误,这是因为在多个源文件中定义了名字相同的全局变量,并且都将它们初始化了。

例如,在 a.c 中定义了全局变量 global:

int global = 10;

在 b.c 中又对 global 进行了定义:

int global = 20;

那么在链接时就会出现下面的错误:

b.o: multiple definition of `global'
a.o: first defined here

这种符号的定义可以被称为强符号。

在C语言中,编译器默认函数和初始化了的全局变量为强符号(Strong Symbol),未初始化的全局变量为弱符号(Weak Symbol)强符号之所以强,是因为它们拥有确切的数据,变量有值,函数有函数体;弱符号之所以弱,是因为它们还未被初始化,没有确切的数据。

链接器会按照如下的规则处理被多次定义的强符号和弱符号:
1) 不允许强符号被多次定义,也即不同的目标文件中不能有同名的强符号;如果有多个强符号,那么链接器会报符号重复定义错误。

2) 如果一个符号在某个目标文件中是强符号,在其他文件中是弱符号,那么选择强符号。

3) 如果一个符号在所有的目标文件中都是弱符号,那么选择其中占用空间最大的一个。

比如目标文件 a.o 定义全局变量 global 为 int 类型,占用4个字节,目标文件 b.o 定义 global 为 double 类型,占用8个字节,那么被链接后,符号 global 占用8个字节。请尽量不要使用多个不同类型的弱符号,否则有时候很难发现程序错误。

在 GCC 中,可以通过__attribute__((weak))来强制定义任何一个符号为弱符号。假设现在有下面的一段代码:

extern int ext;
int weak1;
int strong = 100;
__attribute__((weak)) weak2 = 2;
int main(){return 0;
}

weak1 和 weak2 是弱符号,strong 和 main 是强符号,而 ext 既非强符号也非弱符号,它是一个对外部变量的引用(使用)。

为了加深理解,我们不妨再来看一个多文件编程的例子。

main.c 源码:

#include <stdio.h>
//弱符号
__attribute__((weak)) int a = 20;
__attribute__((weak)) void func(){printf("C Language\n");
}
int main(){printf("a = %d\n", a);func();return 0;
}

module.c 源码:

#include <stdio.h>
//强符号
int a = 9999;
void func(){printf("c.biancheng.net\n");
}

在 GCC 中,使用下面的命令来运行程序:

$gcc main.c module.c
$./a.out
a = 9999
c.biancheng.net

在 main.c 中,a 和 func 都是弱符号,在 module.c 中,a 和 func 都是强符号,强符号会覆盖弱符号,所以链接器最终会使用 module.c 中的符号,输出结果也印证了这一点。

需要注意的是,__attribute__((weak))只对链接器有效,对编译器不起作用,编译器不区分强符号和弱符号,只要在一个源文件中定义两个相同的符号,不管它们是强是弱,都会报“重复定义”错误。请看下面代码:

#include <stdio.h>
__attribute__((weak)) int a = 20;
int a = 9999;
int main(){printf("a = %d\n", a);return 0;
}

这段代码在编译阶段就会报错,编译器会认为变量 a 被定义了两次,属于重复定义。

弱符号对于库来说十分有用,我们在开发库时,可以将某些符号定义为弱符号,这样就能够被用户定义的强符号覆盖,从而使得程序可以使用自定义版本的函数,增加了很大的灵活性。

本文转自:强符号和弱符号

转载于:https://www.cnblogs.com/yongdaimi/p/8084634.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/467074.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C语言——预编译

C语言——预编译 宗旨&#xff1a;技术的学习是有限的&#xff0c;分享的精神是无限的。 在C 语言中&#xff0c;并没有任何内在的机制来完成如下一些功能&#xff1a;在编译时包含其他源文件、定义宏、根据条件决定编译时是否包含某些代码。要完成这些工作&#xff0c;就需要使…

深入理解cache对写好代码至关重要

There are only two hard things in Computer Science: cache invalidation and naming things.-- Phil Karlton全文目录CACHE基础CACHE的组织TAG,INDEXVIVT,VIPT,PIPTCache别名问题CACHE一致性icache、dcache同步多CPU核cache同步CPU与设备cache同步意识到CACHE的编程perf中的…

[推举]网络工程师必懂的专业术语

路由器问题&#xff1a; 1、什么时候使用多路由协议&#xff1f; 当两种不同的路由协议要交换路由信息时&#xff0c;就要用到多路由协议。当然&#xff0c;路由再分配也可以交换路由信息。下列情况不必使用多路由协议&#xff1a; 从老版本的内部网关协议&#xff08; Interio…

基础练习 特殊回文数

问题描述123321是一个非常特殊的数&#xff0c;它从左边读和从右边读是一样的。输入一个正整数n&#xff0c; 编程求所有这样的五位和六位十进制数&#xff0c;满足各位数字之和等于n 。输入格式输入一行&#xff0c;包含一个正整数n。输出格式按从小到大的顺序输出满足条件的整…

IO流--buffer

示例展示&#xff1a; package buffer;import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.I…

Linux 用户空间和内核空间

最近在微信群里看到有人提这个问题&#xff0c;然后查了下资料&#xff0c;觉得这篇文章是写得最能让人看懂的&#xff0c;分享给大家。欢迎大家评论说出自己的见解&#xff0c;让更多的人更容易理解这部分知识。之前的相关文章Linux内存&#xff0c;先看这篇文章Linux物理内存…

数据库的学习

自从搬到那边学校去了&#xff0c;就好久都没有写过我的学习了。因为没那么多时间上网了&#xff0c;有时候去网吧上网也不想写了。又开始了一本书的学习&#xff0c;那就是数据库。还记得去年那个时候学数据库的时候&#xff0c;大家都说我们学会最多的就是ctrlc和ctrlv&#…

2017.12.20-21

1.今天&#xff0c;继续学习公司产品DDS的开发手册。 首先&#xff0c;其中有几个概念需要认识&#xff1a; CORBA(Common Object Request Broker Architecture):公共对象的请求代理体系结构&#xff0c;OMG组织定义的一种面向对象应用的标准国际规范架构。为简化跨平台应用而提…

C语言——宏定义

C语言——宏定义 宗旨&#xff1a;技术的学习是有限的&#xff0c;分享的精神是无限的。 注意&#xff1a;宏定义不是函数&#xff01;&#xff01; 一般用来简化操作的&#xff0c;但又能避免函数调用那样需要进行切换环境&#xff0c;花费时间。例如&#xff1a; #define ma…

一个深入学习Linux/C/C++的原创技术号

今天给大家推荐一个Linux/C/C领域的公众号&#xff0c;大家都知道这个领域的号不多&#xff0c;而【编程珠玑】就是这样一个专注该领域的原创类公众号&#xff0c;原创占比95%以上&#xff0c;目前已有原创文章200多篇&#xff0c;而且原创间环环相扣&#xff0c;皆有关联。公众…

Redis Java调用

Redis Java调用 package com.stono.redis;import redis.clients.jedis.Jedis;public class RedisJava {public static void main(String[] args) {Jedis jedis new Jedis("localhost");System.out.println("ok");System.out.println("ping "jed…

Linux 资料大全

Hello all&#xff0c;给大家分享一些 Linux 学习资料&#xff0c;包含&#xff1a;社区网站、在线教程、命令大全、在线模拟器、经典书籍、镜像站点等 ...从入门到进阶&#xff0c;应有尽有。无论你是小白&#xff0c;还是 Linux 高手&#xff0c;都不容错过&#xff0c;100% …

C语言——字符串函数

C语言——字符串函数 宗旨&#xff1a;技术的学习是有限的&#xff0c;分享的精神是无限的。 常用字符串操作函数的实现&#xff1a;注释部分你们注意一下&#xff0c;我没加/&#xff0c;加了/就看不到注释了。 ************************************************* *功能:实…

创业思考点滴

创业的理由应该是为了做更有意义的事&#xff0c;而不是赚更多钱&#xff0c;是为了更大程度地实现自身价值&#xff0c;而不是得到更多财富。 创业对个人是很大的挑战&#xff0c;这种挑战会让人成长得更快。 在创业过程中&#xff0c;自省很重要&#xff0c;很多时候问题与困…

各种排序方法的比较

简单排序包括直接插入排序、冒泡排序、和简单选择排序。 排序方法的稳定性&#xff1a;假设KiKj&#xff08;1<i<n,1<j<n,i!j&#xff09;&#xff0c;若在排序前的序列中Ri领先于Rj&#xff08;即i<j&#xff09;&#xff0c;经过排序后得到的序列中Ri领先于Rj…

生命很短,我用tldr

我们平时使用命令的时候&#xff0c;如果忘记的或者不知道这个命令如何使用&#xff0c;然后你就会去百度&#xff0c;也会去使用man 或者 -- help 查看&#xff0c;但是看到的一般都是长篇大论。如果你看了这篇文章&#xff0c;就会知道tldr是怎么样的存在。tldr 的含义TL;DR …

Linux安装vsftpd

卸载vsftpd sudo yum remove vsftpd 安装vsftpd sudo yum -y install vsftpd 创建一个文件夹用来当作ftp得仓库 cd / sudo mkdir ftpfile 创建一个用户,仅对文件夹有上传权限,又没有登陆权限 sudo useradd ftpuser -d /ftpfile/ -s /sbin/nologin//赋值权限sudo chown -R ftpus…

EJB 学习笔记

1、ejb 基础知识&#xff08;1&#xff09; 无状态会话bean不保存客户机的会话状态优点&#xff1a;使用小量的实例即可满足大量的客户。每个实例都没有标识&#xff0c;相互之间是等价的。等?的无状态会话bean&#xff1a; 多次和一次调用的结果和效应相同。在集群中可以…

C语言——项目规范

C语言——项目规范 宗旨&#xff1a;技术的学习是有限的&#xff0c;分享的精神是无限的。 //基本原则 1、尽量少使用全局变量&#xff0c;或者说禁止使用全局变量&#xff1b; 2、实在要在其他文件使用本文件中的变量&#xff0c;以函数接口返回其值&#xff0c;在其他文件…

折半查找判定树及平均查找长度

折半查找判定树及平均查找长度 从折半查找的过程看&#xff0c;以有序表的中间记录作为比较对象&#xff0c;并以中间记录将表分割为两个子表&#xff0c;对子表继续上述操作。所以&#xff0c;对表中每个记录的查找过程&#xff0c;可用二叉树来描述&#xff0c;二叉树中的每个…