链接与自定义函数名同名的库函数
遇到一个问题: 封装SQLite3成静态库,过程中发现SQLite3的源码的shell.c中有main函数:
int SQLITE_CDECL main(int argc, char **argv){char *zErrMsg = 0;ShellState data;const char *zInitFile = 0;int i;int rc = 0;int warnInmemoryDb = 0;int readStdin = 1;int nCmd = 0;char **azCmd = 0;...
将其封装成静态库.a文件自然是被使用者调用的,也就是说使用者也要有自己的main函数才行。如此说来,在使用该.a的项目中就有了两个main函数,那应该是一定编译不过的,然而事实并非如此,程序能正常编译且符合设计逻辑运行,这涉及gcc中链接器ld的链接过程,于是通过自行编写测试程序试验一番。
新建如下文件:
addLib.和subLib.将编译为库函数使用,其实现为:
//addLib.h
#ifndef __ADDLIB_H__
#define __ADDLIB_H__int add(int a, int b);#endif /* __ADDLIB_H__ *///addLib.c
#include <stdio.h>
#include "addLib.h"int add(int a, int b)
{printf("%d + %d = %d\n", a, b, a + b);return 0;
}
//subLib.h
#ifndef __SUBLIB_H__
#define __SUBLIB_H__#include <stdio.h>
void sub(int a, int b);#endif /* __SUBLIB_H__ *///subLib.c
#include "subLib.h"void sub(int a, int b)
{printf("%d - %d = %d\n", a, b, a - b);
}
而main.c是对该库函数的调用:
#include <stdio.h>
#include "addLib.h"
#include "subLib.h"int main(void)
{add(4, 6);return 0;
}
简单写个makefile:
all : addLib.o subLib.oar -r libcal.a addLib.o subLib.o gcc main.c -L./ -lcaladdLib.o : addLib.cgcc -c $<subLib.o : subLib.cgcc -c $<clean:rm *.o *.a -rf
编译通过:
运行正确:
Linux的nm可列出目标文件的符号清单,通过它查看libcal.a:
图中的T表示该符号位于代码段,U表示在当前文件是未定义的,即该符号是定义在别的文件。
在这个例子中,链接的命令为
gcc main.c -L./ -lcal
其中gcc main.c其实是做了编译和链接,所以最后一步的链接操作是
gcc main.o -L./ -lcal
链接器从左向右扫描链接命令行参数中的.o和.a,最终目的是确定“最终.o文件集合”和各个.o文件中的外部符号的定义位置。
以本程序为例,
(1)首先是扫描main.o,main.o会无条件被加入到“最终.o文件集合”中,该文件引用了main符号,它是程序开始执行的符号,会将main放入“已定义符号表”中,接着又引用了外部符号符号add,因此会将add放入“未定义符号表”中。
(2)扫描到libcal.a中addLib.o,“未定义符号表”中存放的add在addLib.o中找到了定义,于是将addLib.o文件加入到“最终.o文件集合”中,且将add符号从“未定义符号表”中转换到“已定义符号表”中。但是在扫描addLib.o中,发现了外部符号printf,于是printf符号被放入“未定义符号表”。
(3)扫描到libcal.a中的subLib.o,此时“未定义符号表”中存放的是printf,并不能在subLib.o中找到定义,直接略过该文件,所以subLib.o并不加入到“最终.o文件集合”中,其中的符号信息也没有被加载“未定义符号表”和“已定义符号表”。
(4)“未定义符号表”里仍然存在printf符号,所以链接器会继续扫描,它往哪里扫描?链接命令上写到-lcal就截止了,其实c程序默认会去链接标准c库的,找到标准c库的定义printf符号的.o文件,并把该文件加入“最终.o文件集合”中,链接操作至此完成。注意链接完成的标志是“未定义符号表”中为空,也就是不能出现未定义的符号。
如上分析,因为main.c程序中并没有调用sub函数,subLib.o并不会被加入到“最终.o文件集合,那么在subLib.c中加上如下代码且不调用,同样是能编译通过咯:
void sub(int a, int b)
{printf("%d - %d = %d\n", a, b, a - b);
}int main(void)
{printf("in lib mian!\n");return 0;
}
果然如此:
main.c的main符号在库函数外部,链接器已经认识这个符号了,会将其放入“已定义的符号表”中,所以不会去扫描cal库内的subLib.o里的main符号。
再做改动,在main.c的main函数中调用subLib.c的sub函数:
int main(void)
{add(4, 6);sub(9, 2);return 0;
}
再编译就出现重定义错误了:
原因也很简单,因为main函数调用了sub函数导致subLib.c中的sub符号一开始放入到“未定义符号集”,再找到subLib.o后,该符号会被放入到“已定义符号集”,subLib.o也会随之加入“最终.o文件集合”中,问题就出现了:该集合中main.o和subLib.o均包含了main符号,自然就报错了!
综上所述,我们可以推论,链接器对目标文件(.o)和库文件(.a)是区别对待的。我们知道,可执行程序是由一系列的.o文件“合并”而成,以静态链接为例,“最终.o文件集合”中除了包含我们显示提供的由.c编译而来.o文件外,还有从.a库文件提取出来的.o文件,可执行程序对由.c编译而成的.o文件无条件的包含到“最终.o文件集合”中,而对从.a库提取的.o并非全盘提取,而是“按需”提取,“按需”是根据“未定义符号表”中的符号去提取的。这也符合软件设计的思想,尽可能使得可执行文件的size小。
实际开发中,我还遇到这样一个问题: 可执行程序链接了n个静态库和一个动态库,动态库想要调用静态库里的某个函数,运行时报错找不到该符号,通过前面的学习,可以分析原因就是在于,可执行程序虽然链接了这个静态库但并没有使用静态库的某个.o文件,导致.o没有真正被链接到可执行程序,而该.o文件的某个符号又要被动态库使用,这就出现了上面的报错了。解决办法无非就是让可执行程序去调用一下该符号了。
链接与自定义函数名同名的库函数相关推荐
- 对象名和函数名同名引起的莫名错误
<html> <head><script type="text/javascript"> var Validator = function() ...
- python自定义函数名_使用自定义名称创建Python动态函数
如果这个问题已经提出并得到了回答,我深表歉意. 我需要做的是非常简单的概念,但不幸的是,我还没有找到一个在线答案. 我需要在Python(Python2.7)中使用运行时的自定义名称创建动态函数.每个 ...
- error C3861: “XXXX(自定义函数名)”: 找不到标识符
问题描述 调试程序时,输出窗口提示找不到标识符错误! 错误原因 程序中没有对自定义函数进行声明 解决方案 在主函数前对自定义函数进行声明,问题解决!
- C语言 函数 (库函数 · 自定义函数 · 函数参数 · 函数调用 · 嵌套调用链式访问 · 递归)
文章目录: 一.函数是什么? 二.库函数 2.1 为什么要有库函数? 2.2 如何学习库函数? 2.3 我们就以 strcpy( ) 函数,来参照文档自学一下: 2.4 总结: 三.自定义函数 3.1 ...
- java调mongodb自定义函数,自定义UDF函数,从hive保存到mongodb
(可以通过idea工具调试UDF函数,第二步中会提供参考) 一.自定义UDF函数: 1.首先是pom.xml文件 xmlns:xsi="http://www.w3.org/2001/XMLS ...
- LTspice基础教程-027.自定义函数;func指令用法
在LTspice中,我们可以自定义函数.语法如下: .func <name>([args]) {<expression>} func是function的缩写:name是自定义函 ...
- MySQL自定义函数(CREATE FUNCTION)
在使用 MySQL 的过程中,MySQL 自带的函数可能完成不了我们的业务需求,这时候就需要自定义函数. 自定义函数是一种与存储过程十分相似的过程式数据库对象.它与存储过程一样,都是由 SQL 语句和 ...
- mysql自定义函数的分号_MySQL 第八篇:自定义函数、存储过程、游标-阿里云开发者社区...
我把MySQL的内容整理成9篇博客,学完这9篇博客虽不能说能成为大神,但是应付一般中小企业的开发已经足够了,有疑问或建议的欢迎留言讨论. 自定义函数 一.函数的概念与定义 1.理解函数 函数可以看作是 ...
- python学习笔记之自定义函数
live long and prosper 自定义函数 def greet_user():"""现实简单的问候语"""print(" ...
- mysql自定义函数示例代码,以及属性介绍
以下是一个MySQL自定义函数的示例代码,该函数实现了将指定字符串中的大写字母转换为小写字母的功能: DELIMITER $$ CREATE FUNCTION to_lower(str VARCHAR ...
最新文章
- Spring Boot整合Spring Data JPA操作数据
- 用户请求队列化_分布式消息队列选型分析
- cocos2d-x 帧动画学习
- python控制语句中的条件语句_『Python』条件控制语句
- 多线程与高并发(五):强软弱虚四种引用以及ThreadLocal的原理与源码
- bootstrap获取弹框数据_Bootstrap模态弹出框的实例教程
- 可燃气体浓度多少合格_安燃无恙 | 可燃气体报警器的常见故障处理
- Windows Server 版本信息及支持期 Win10系统各版本服务起止日期。
- ssh整合步骤之二(架构设计)
- vue权限问题解决方案
- MySQL查询语句(select)详解(1)
- 电商自营藏猫腻 苏宁国美京东的套路谁最深?
- 计算机思维导论raptor实验报告,计算机基础实验报告Raptor.pdf
- 联想用u盘重装系统步骤_联想笔记本u盘重装系统,详细教您联想笔记本怎么使用u盘重装系统...
- Vue3源码阅读(八)effect
- 一次CSDN客户体验经历
- Poto Editor for Mac(mac照片编辑器)
- 幻灯片放映时无法切换到下一张
- 1046: 数值统计
- 【苹果推iMessage】软件安装通过ApplseScript节制iMessage客户端