菜单开关

周梦康 发表于 2017-09-30 1816 次浏览 标签 : C语言快速入门

免费领取阿里云优惠券 我的直播 - 《PHP 进阶之路》

概念

我们编写的文件,需要经过一定的处理才能转换成机器上可运行的可执行文件。我们将对C语言的这种处理过程称为编译与链接。
编译就是把文本形式源代码翻译为机器语言形式的目标文件过程。
链接是把目标文件、操作系统的启动代码和用到的库文件进行组织最终形成可执行代码的过程。

实验

对下面的代码进行编译

#include <stdio.h>
#include <stdlib.h>

int e = 10;

int hello();

int main(int argc, char const *argv[]) {
    int a[3];
    char b[3];
    char *c = (char *) malloc(sizeof(char) * 3);
    char *d = "hello world";

    printf("a: %p\n", a); // 栈
    printf("b: %p\n", b); // 栈
    printf("c: %p\n", c); // 堆
    printf("d: %p\n", d); // 只读区(常量区)
    printf("&d: %p\n", &d); // d 是指针,本身的地址是在栈上
    printf("&e: %p\n", &e);
    printf("main: %p\n", main); // 代码区
    printf("hello: %p\n", hello);

    return hello();
}
int hello(){
    return 1;
}

编译

-c只编译不链接

$ gcc test.c -c -o test.o
$ nm test.o
                 U ___stack_chk_fail
                 U ___stack_chk_guard
000000000000012c D _e
0000000000000120 T _hello
0000000000000000 T _main
                 U _malloc
                 U _printf

链接

找到符号,为找到的符号确定地址

$ gcc test.o -o test
$ nm test
                 U ___stack_chk_fail
                 U ___stack_chk_guard
0000000100000000 T __mh_execute_header
0000000100001030 D _e
0000000100000f10 T _hello
0000000100000df0 T _main
                 U _malloc
                 U _printf
                 U dyld_stub_binder

运行

$ ./test
a: 0x7fff5405aa9c
b: 0x7fff5405aa85
c: 0x7fb54ac02780
d: 0x10bba5f5e
&d: 0x7fff5405aa70
&e: 0x10bba6030
main: 0x10bba5df0
hello: 0x10bba5f10

原理

链接

摘录于《C专家编程》

静态链接

Unix 早期编译时,将需要使用的每个库函数一份拷贝加入到可执行文件中,也就是静态链接

动态链接

后来一种更为现代和优越的被称为动态链接的方法逐渐被采用。动态链接允许系统提供一个庞大的函数库集合,可以提供许多有用的服务。程序在运行时寻找它们,而不是把这些函数库的二进制代码作为自身可执行文件的一部分。
如果可执行只是包含了文件名,让载入器在运行时能够寻找程序所需要的函数库,那么我们称之为动态链接

动态链接的优势

  1. 可执行文件体积非常小;
  2. 链接-编辑阶段时间也会缩短;
  3. 解耦,把程序和它们使用的函数库分离(关键字ABI);
  4. 提升计算机整理性能

如何提升整理性能

  1. 动态链接的可执行文件比功能相同的静态链接可执行文件的体积小,能够节省磁盘空间和虚拟内存,因为函数库只有在需要时才被映射到进程中。
  2. 所有动态链接到某个特定函数库的可执行文件在运行时共享该函数库的一个单独拷贝。操作系统内核保证映射到内存中的含数据库可以被使用它的其他进程共享,提供了更好的I/O和交换空间利用率,节省了物理内存,从而提高了系统的整体性能。如果可执行文件是静态链接的,每个文件都将拥有一份函数库的拷贝,显然极为浪费空间(系统调用mmap把文件映射到进程的地址空间中)。

静态库和动态库

任何人都可以创建静态或者动态的函数库,源码文件不含有main函数。

  1. 静态库(archive),通过ar来创建和更新,以.a作为扩展名。过时了,不推荐使用。动态库,通过ld来创建和更新,以.so作为扩展名。
  2. 通过-lthread选项告诉编译链接到libthread.so-l{name}-lib{name}.so的缩写。
  3. 编译器期望在确定的目录找到库,除了系统默认的一些目录外,可以在链接时使用-Lpathname-Rpathname来指定。
  4. 通过头文件确认使用的库函数
  5. 始终将-l函数库选项放在编译命令行的最右边

嗨,老铁,欢迎来到我的博客!

如果觉得我的内容还不错的话,可以关注下我在 segmentfault.com 上的直播。我主要从事 PHP 和 Java 方面的开发,《深入 PHP 内核》作者之一。

[视频直播] PHP 进阶之路 - 亿级 pv 网站架构的技术细节与套路 直播中我将毫无保留的分享我这六年的全部工作经验和踩坑的故事,以及会穿插着一些面试中的 考点难点加分点

评论列表

回复 大头 2017-11-12 21:56:21
扎心了