情景描述

平时调用各种 C 库时,免不了各种链接。记得当初学习 C 语言时,为了使用 math 库,可没少折腾。Ncurses 作为一个字符终端的图形库,是相当实用的。不过,并不是所有的用户都会在他的系统上安装这个库,所以,我们可以考虑把 Ncurses 包含到我们的程序里。这就是静态链接。

先简要说明一下静态链接和动态链接。无论是静态库文件(.a)还是动态库文件(.so),都是二进制文件。不同的是,当我们的程序进行静态链接时,是直接把静态库文件给包含进程序里了。而动态链接不会这样做,程序会在运行时,加载库文件,同时,一个动态库文件,可以被多个程序共享加载。换言之,静态链接,是每一个程序都包含了库文件,动态链接是多个程序从同一个库文件共享代码。

显然,动态链接可以减小程序文件的大小,而且节约内存。毕竟一个库只需要加载到内存一次,其他程序使用时,不需要再次加载。而且程序本身并没有包含库文件,所以程序文件不会很大。但是,如果用户的系统没有这个库,就完了。程序就无法运行了。

静态链接就可以避免用户没有安装依赖库的问题。程序通过直接包含库文件,使得程序在加载到内存时,已经加载了库文件。但是,这种做法,会使程序文件比较大,而且,由于每个程序加载时,都加载了库文件,如果这些程序依赖同一个库,必然会造成内存浪费。不过,这样做不必考虑依赖问题,可以确信程序在用户那里能运行。

啰里啰嗦写了一堆不相关的,算是说明了为啥要静态链接 Ncurses,现在进入重点,如何静态链接。当然,你的系统肯定是要已经安装了 Ncurses 的,注意,不只是运行环境,还有开发环境。

安装开发环境

注意,这里是针对 DEB 系的 Linux 系统,Ncurses的版本是 5

sudo apt install libncurses5-dev ncurses-doc

测试代码

我们写一段测试代码,代码就放在主目录下吧。

// test.c
#include <ncurses.h>

int main(void) {
    initscr();
    printw("Hello world\n");
    refresh();
    getch();
    endwin();
    return 0;
}

常规链接

我们使用常规的编译指令,也是动态链接,编译一下。

gcc test.c -o test -lncurses

我们使用 ldd 命令查看生成的可执行文件的依赖

# xxx @ xxx in ~ [12:52:45]
$ ldd test
    linux-vdso.so.1 (0x00007ffd8990a000)
    libncurses.so.5 => /lib/x86_64-linux-gnu/libncurses.so.5 (0x00007f69d5891000)
    libtinfo.so.5 => /lib/x86_64-linux-gnu/libtinfo.so.5 (0x00007f69d5667000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f69d52c8000)
    libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f69d50c4000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f69d5cb6000)

静态链接

现在我们使用静态链接,注意指令的链接依赖顺序。

gcc test.c -o test -static -lncurses -ltinfo

注意,先链接 ncurses(-lncurses),然后是 tinfo(-ltinfo)。下面是 ldd 命令的输出。

# xxx @ xxx in ~ [12:55:13]
$ ldd test
    不是动态可执行文件

由于我们使用了静态链接,所以,可执行文件 test 里包含了所有的动态库的静态库版本。也就是,test 里包含了 linux-vdso、libncurses、libtinfo、libc、libdl 这些库。

现在的可执行文件 test 可以在一个没有安装 ncurses 的系统上运行了。

中文支持

如果你想使用 Ncursesw 来支持中文等字符(当然你要安装 ncursesw 对吧),可以参考如下。

// test.c 支持中文
#include <ncurses.h>
#include <locale.h> //setlocale()函数

int main(void) {
    setlocale(LC_ALL,""); //使用本地字符集
    initscr();
    printw("Hello world.\n");
    printw("打得好的.\n");
    refresh();
    getch();
    endwin();
    return 0;
}

编译指令:

gcc test.c -o test -static -lncursesw -ltinfo