linux下c程序的链接、装载和库(2)
5. 重定义错误。
一个最终的可执行文件里,绝对不允许出现两个同名的全局变量,也不允许出现同名的全局函数。
全局函数:只要不用 static 修饰符修饰的函数,全部都是全局的。
全局变量:函数外声明定义,且不加 static 修饰符修饰的变量。
例如,one.c 里有一个函数 function, 那么你如果想让 main.c 生成的 main.o 能够链接 one.o 的话,那么 main.c 里就不能再有一个函数叫做 function 了。否则就会报重定义错误。
这就好像,你的班上有两个人都叫 小明, 这个情况确实麻烦,你肯定会用各种办法区分他们,比如说,大小明,小小明,这种方法,实际上,你已经为他们重新命名了。
6. 声明和定义。
你也许发现了一个问题,那就是:main.c 里 有一句
extern void function();
这一句不是跟 one.c 里的 function 重名了么。那么编译器为啥不报错呢?
想一想整个过程。
第一步:
gcc -c main.c -o main.o
这一步就是编译 main.c 的过程,好吧,这一步,编译器完全不用关心在某个地方还有个 one.c,这个源文件里有一个同名的function。
仔细解释一下这句话:
extern void function();
你写程序的时候,要有一种跟编译器时刻交流的感觉。
这一句是你写给编译器看的,你就是要告诉编译器这样一个事实:
--Hey!编译器!
--main.c 这个源文件里要用到一个函数 function,是void 类型的,没有参数,你暂时不用管这个函数到底在哪,你先编译通过,这个函数最终会被你的哥们链接器链接过来!
编译器:
--ok!我相信我的哥们链接器!
这个过程就叫做“声明”。如果没有这一句,没有这个过程,编译器就会在 main.c 里遇见一个陌生的函数调用 function,嗯,结果就是,败!
而这个函数 function 的真正定义就是在 one.c 里。所谓定义,就是具体的实现,就是这个函数大括号里的东西。也可以这么说,没有大括号(即使没有 extern 修饰符)的地方就是声明,有大括号(即使大括号里是空的)的地方,那就是定义。
还有一句,声明可以多次,比如说,每一个用到了函数 function 的地方,都要声明(你可以不用 extern 这个修饰符,试试吧)。但是,定义只有一个,这也符合上一小节说的:不能重定义!
7. 从硬盘到内存--装载。
这一节不准备总结的太细。
你写好的源文件是放在硬盘上的,你编译成的目标文件也是放在硬盘上的,链接成的可执行文件也是放在硬盘上的。
当你,运行这个可执行文件的时候,操作系统就会做一件事情:装载。
可以简单粗暴的假想,操作系统把你的可执行文件直接复制到内存的某个地方,然后,cpu开始在这个地方去找 main 函数,进而执行整个程序。
所以你的程序才会占内存的空间:变量会占,函数会占,动态开辟(例如malloc)的更会占。
8. 节省一点内存。
你的同事十分能干,他用他闲暇的时间,积极地扩充他所维护的 one.c,使这个 one.c 更加丰富。比如说,他增加了一个函数
int add(int a, int b)
{
//省略代码
}
可是,你知道,函数最终是要占内存空间的。并且,你完全用不到这个新加的函数 add。
问题就是,你链接的时候,已经把整个 one.o 链接到你的 可执行文件里了。这个可执行文件确实包含了 add 的具体实现的代码,也就是说,最终运行的时候,内存里确实会有这一部分,而且是完全没用的部分。
你仅仅用到了 one.o 里的一个函数 function 就要链接整个 one.o,这就好像,你到饭店,只想吃一个汉堡,却不得不花钱买一份套餐,浪费。
你不得不跟你的同事说明一下这个问题,最终你们商量出了一个办法。
你的同事决定,将他写的每一个函数单独放到一个源文件里去,比如说,function函数放到 function.c ,add函数放到 add.c。
这样,一个函数对应一个源文件,也对应一个目标文件,也就是说,一个目标文件里只有一个函数,没有其他的东西。
你使用的时候,就能够随便挑选,去链接哪个目标文件了。
比如说,你的同事已经有了这些:
one.c 包含了 void one(){} 函数
two.c 包含了 void two(){} 函数
three.c 包含了 void three(){} 函数
等等……。而且也提供了一份头文件叫做 all.h, 这个all.h包含了所有他编写的函数的声明。
你使用的时候先包含这个all.h, 像这样
#include "all.h"
然后,编译;然后链接的时候,你用到了哪个,就在你的gcc命令里加上那个目标文件就行。例如,用到了 void three() 这个函数:
ld main.o three.o -o go
这样就行了。
但是即便是这样, 你还是觉得麻烦,你得在心里记录一下,你用了哪些函数,并且去手动敲下命令进而链接,这样容易出错。
任何问题都是有可能解决的,这次也不例外。
linux下c程序的链接、装载和库(2)的更多相关文章
- linux下c程序的链接、装载和库(1)
读完<程序员的自我修养--链接.装载和库>相关章节,想来总结一下,若有错误,请指正,多谢. 1. 什么叫目标文件? 你的工程里有很多xxx.c这样的源文件,这些文件是文本文件,只有人能够认 ...
- Linux下进行程序设计时,关于库的使用:
一.gcc/g++命令中关于库的参数: -shared: 该选项指定生成动态连接库: -fPIC:表示编译为位置独立(地址无关)的代码,不用此选项的话,编译后的代码是位置相关的,所以动态载入时,是通过 ...
- linux下c程序的链接、装载和库(3)
9. 目标文件放在一起-->静态库. 你的同事给出的目标文件太多了,从 one.o two.o …… …… 一直到 xxx.o. 好的,你如果真正想用,你的同事提供的这些现有的目标文件,你得做三 ...
- Linux下C程序的编辑,编译和运行以及调试
国内私募机构九鼎控股打造APP,来就送 20元现金领取地址:http://jdb.jiudingcapital.com/phone.html 内部邀请码:C8E245J (不写邀请码,没有现金送) 国 ...
- linux下软、硬链接的创建和删除
linux下软.硬链接的创建和删除 在Linux系统中,内核为每一个新创建的文件分配一个Inode(索引结点),每个文件都有一个惟一的inode号.文件属性保存在索引结点里,在访问文件时,索引结点被复 ...
- Linux下显示运行时链接(运行时加载)
目录 介绍 如何加载动态库 dlopen() 第一个参数: 被加载动态库的路径 第二个参数: flag表示函数符号的解析方式 dlopen 返回值 dlsym() 参数: 返回值 符号优先级 dler ...
- linux下c程序调用reboot函数实现直接重启【转】
转自:http://www.blog.chinaunix.net/uid-20564848-id-73878.html linux下c程序调用reboot函数实现直接重启 当然你也可以直接调用syst ...
- 位图文件(BMP)格式以及Linux下C程序实现(转)
源:位图文件(BMP)格式以及Linux下C程序实现 说到图片,位图(Bitmap)当然是最简单的,它是Windows显示图片的基本格式,其文件扩展名为*.BMP.由于没有经过任何的压缩,故BMP图 ...
- Linux下C程序内存泄露检测
在linux下些C语言程序,最大的问题就是没有一个好的编程IDE,当然想kdevelop等工具都相当的强大,但我还是习惯使用kdevelop工具,由于没有一个习惯的编程IDE,内存检测也就成了在lin ...
随机推荐
- Git命令汇总
1. 工作区和版本库 说明: 工作区(Working Directory)就是创建仓库的文件夹 版本库(Repository)就是工作区的隐藏目录.git,版本库中有暂存区(stage/index)和 ...
- iOS-即时通讯-环信
下载地址:http://www.easemob.com/downloads SDK目录讲解 1.从官网下载下来的包分为如下四部分: 环信iOS SDK 开发使用 环信iOS release note ...
- LINQ系列:LINQ to SQL Take/Skip
1. Take var expr = context.Products .Take(); var expr = (from p in context.Products select p) .Take( ...
- vue实例属性(vm.$els)
不需要表达式 参数: id(必需) 用法: 为 DOM 元素注册一个索引,方便通过所属实例的 $els 访问这个元素. 注意: 因为 HTML 不区分大小写,camelCase 名字比如 v-el:s ...
- ListView+CheckBox实现全选 单击效果
在网上也找了一些案例,但都是用Map来实现的.我的是把对象绑定到当前控件上.代码稍微简洁. main布局文件:main.xml <?xml version="1.0" enc ...
- 从零开始编写自己的C#框架(4)——文档编写说明
在写本系列的过程中,了解得越多越不知道从哪里做为切入点来写,几乎每个知识点展开来说都可以写成一本书.而自己在写作与文档编写方面来说,还是一个初鸟级别,所以只能从大方面说说,在本框架开发所需的范围内来讲 ...
- Android随笔之——Activity中启动另一应用
最近在写语音交互程序,在语音打开应用这块碰到如何用代码控制应用启动的问题.百度了一下,有两种方案:1.获取应用的包名:2.获取应用的包名.入口类名. 之前对两种方案都进行了尝试,发现方案二中存在一个弊 ...
- 关于z-index鲜为人知的事情
关于z-index很少有人去深入的了解它,因为它看起来一点儿也不复杂,不就是谁的数字大,谁就显示在前面吗?然而今天所摘录的这篇博文,让我震惊了.我承认我从来没有花时间去看具体的z-index相关文档, ...
- 分析MariaDB初始化脚本mysql_install_db
在初始化MySQL的过程中经常会碰到各种问题,如 FATAL ERROR: Could not find ./bin/my_print_defaults ERROR: Can't create/wri ...
- C#开发微信公众平台-就这么简单(附Demo)
写在前面 阅读目录: 服务号和订阅号 URL配置 创建菜单 查询.删除菜单 接受消息 发送消息(图文.菜单事件响应) 示例Demo下载 后记 最近公司在做微信开发,其实就是接口开发,网上找了很多资料, ...