linux下.so的制作_linux 生成so库-程序员宅基地

技术标签: linux  

作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明。谢谢!

 

在“纸上谈兵: 算法与数据结构”中,我在每一篇都会有一个C程序,用于实现算法和数据结构 (比如栈和相关的操作)。在同一个程序中,还有用于测试的main()函数,结构体定义,函数原型,typedef等等。

这样的做法非常不“环保”。算法的实际运用和算法的实现混在一起。如果我想要重复使用之前的源程序,必须进行许多改动,并且重新编译。最好的解决方案是实现模块化: 只保留纯粹的算法实现,分离头文件,并编译一个库(library)。每次需要使用库的时候(比如使用栈数据结构),就在程序中include头文件,连接库。这样,不需要每次都改动源程序。

我在这里介绍如何在UNIX环境中创建共享库 (shared library)。UNIX下,共享库以so为后缀(shared object)。共享库与Windows下的DLL类似,是在程序运行时动态连接。多个进程可以连接同一个共享库。

 

共享库

本文使用Ubuntu测试,使用gcc作为编译器。

 

程序清理

下面程序来自纸上谈兵: 栈 (stack),是栈数据结构的C实现:

复制代码
/* By Vamei */
/* use single-linked list to implement stack */
#include <stdio.h>
#include <stdlib.h>

typedef struct node *position;
typedef int ElementTP;

// point to the  head node of the list
typedef struct node *STACK;
 
struct node {
    ElementTP element;
    position next;
};

STACK init_stack(void);
void delete_stack(STACK);
ElementTP top(STACK);
void push(STACK, ElementTP);
ElementTP pop(STACK);
int is_null(STACK);

void main(void)
{
    ElementTP a;
    int i;
    STACK sk;
    sk = init_stack();
    push(sk, 1);
    push(sk, 2);
    push(sk, 8);
    printf("Stack is null? %d\n", is_null(sk));
    for (i=0; i<3; i++) {
        a = pop(sk);
        printf("pop: %d\n", a);
    }

    printf("Stack is null? %d\n", is_null(sk));    
    delete_stack(sk);
}

/*
 * initiate the stack
 * malloc the head node.
 * Head node doesn't store valid data
 * head->next is the top node
 */
STACK init_stack(void)
{
    position np;
    STACK    sk;
    np = (position) malloc(sizeof(struct node));
    np->next     = NULL;  // sk->next is the top node
    sk = np; 
    return sk;
}

/* pop out all elements 
 * and then delete head node
 */
void delete_stack(STACK sk)
{
    while(!is_null(sk)) {
        pop(sk);
    }
    free(sk);
}
/* 
 * View the top frame
 */
ElementTP top(STACK sk)
{
    return (sk->next->element);
}

/*
 * push a value into the stack
 */
void push(STACK sk, ElementTP value) 
{
    position np, oldTop;
    oldTop = sk->next;    

    np = (position) malloc(sizeof(struct node));
    np->element  = value;
    np->next     = sk->next;

    sk->next     = np; 
}

/* 
 * pop out the top value
 */
ElementTP pop(STACK sk)
{
    ElementTP element;
    position top, newTop;
    if (is_null(sk)) {
        printf("pop() on an empty stack");
        exit(1);
    } 
    else {
        top      = sk->next;
        element  = top->element;     
        newTop   = top->next;
        sk->next     = newTop;
        free(top);
        return element;
    } 
}

/* check whether a stack is empty*/
int is_null(STACK sk)
{
    return (sk->next == NULL);
}
复制代码

上面的main()部分是用于测试,不属于功能模块,在创建库的时候应该去掉。

 

程序中的一些声明,会被重复利用。比如:

复制代码
typedef struct node *position;
typedef int ElementTP;

// point to the  head node of the list
typedef struct node *STACK;
 
struct node {
    ElementTP element;
    position next;
};

STACK init_stack(void);
void delete_stack(STACK);
ElementTP top(STACK);
void push(STACK, ElementTP);
ElementTP pop(STACK);
int is_null(STACK);
复制代码

这一段程序声明了一些结构体和指针,以及栈操作的函数原型。当我们其他程序中调用库时 (比如创建一个栈,或者执行pop操作),同样需要写这些声明。我们把这些在实际调用中需要的声明保存到一个头文件mystack.h。在实际调用的程序中,可以简单的include该头文件,避免了每次都写这些声明语句的麻烦。

 

经过清理后的C程序为mystack.c:

复制代码
/* By Vamei */
/* use single-linked list to implement stack */
#include <stdio.h>
#include <stdlib.h>
#include "mystack.h"


/*
 * initiate the stack
 * malloc the head node.
 * Head node doesn't store valid data
 * head->next is the top node
 */
STACK init_stack(void)
{
    position np;
    STACK    sk;
    np = (position) malloc(sizeof(struct node));
    np->next     = NULL;  // sk->next is the top node
    sk = np; 
    return sk;
}

/* pop out all elements 
 * and then delete head node
 */
void delete_stack(STACK sk)
{
    while(!is_null(sk)) {
        pop(sk);
    }
    free(sk);
}
/* 
 * View the top frame
 */
ElementTP top(STACK sk)
{
    return (sk->next->element);
}

/*
 * push a value into the stack
 */
void push(STACK sk, ElementTP value) 
{
    position np, oldTop;
    oldTop = sk->next;    

    np = (position) malloc(sizeof(struct node));
    np->element  = value;
    np->next     = sk->next;

    sk->next     = np; 
}

/* 
 * pop out the top value
 */
ElementTP pop(STACK sk)
{
    ElementTP element;
    position top, newTop;
    if (is_null(sk)) {
        printf("pop() on an empty stack");
        exit(1);
    } 
    else {
        top      = sk->next;
        element  = top->element;     
        newTop   = top->next;
        sk->next     = newTop;
        free(top);
        return element;
    } 
}

/* check whether a stack is empty*/
int is_null(STACK sk)
{
    return (sk->next == NULL);
}
复制代码

#include "..."; 语句将首先在工作目录寻找相应文件。如果使用gcc时,增加-I选项,将在-I提供的路径中寻找。

 

制作.so文件

我们的目标是制作共享库,即.so文件。

 

首先,编译stack.c:

$gcc -c -fPIC -o mystack.o mystack.c

-c表示只编译(compile),而不连接。-o选项用于说明输出(output)文件名。gcc将生成一个目标(object)文件mystack.o

注意-fPIC选项。PIC指Position Independent Code。共享库要求有此选项,以便实现动态连接(dynamic linking)。

 

生成共享库:

$gcc -shared -o libmystack.so mystack.o

库文件以lib开始。共享库文件以.so为后缀。-shared表示生成一个共享库。

 

这样,共享库就完成了。.so文件和.h文件都位于当前工作路径(.)。

 

使用共享库

我们编写一个test.c,来实际调用共享库:

复制代码
#include <stdio.h>
#include "mystack.h"

/*
 * call functions in mystack library
 */
void main(void)
{
    ElementTP a;
    int i;
    STACK sk;
    sk = init_stack();
    push(sk, 1);
    push(sk, 2);
    push(sk, 8);
    printf("Stack is null? %d\n", is_null(sk));
    for (i=0; i<3; i++) {
        a = pop(sk);
        printf("pop: %d\n", a);
    }

    printf("Stack is null? %d\n", is_null(sk));    
    delete_stack(sk);
}
复制代码

注意,我们在程序的一开始include了mystack.h。

 

编译上述程序。编译器需要知道.h文件位置。

  • 对于#include "...",编译器会在当前路径搜索.h文件。你也可以使用-I选项提供额外的搜索路径,比如-I/home/vamei/test。
  • 对于#include <...>,编译器会在默认include搜索路径中寻找。

编译器还需要知道我们用了哪个库文件,在gcc中:

  • 使用-l选项说明库文件的名字。这里,我们将使用-lmystack (即libmystack库文件)。
  • 使用-L选项说明库文件所在的路径。这里,我们使用-L. (即.路径)。

如果没有提供-L选项,gcc将在默认库文件搜索路径中寻找。

 

你可以使用下面的命令,来获知自己电脑上的include默认搜索路径:

$`gcc -print-prog-name=cc1` -v   

获知库默认搜索路径:

$gcc -print-search-dirs

 

我们所需的.h和.so文件都在当前路径,因此,使用如下命令编译:

$gcc -o test test.c -lmystack -L.

将生成test可执行文件。

 

使用

$./test

执行程序

 

运行程序

尽管我们成功编译了test可执行文件,但很有可能不能执行。一个可能是权限问题。我们需要有执行该文件的权限,见Linux文件管理背景知识

另一个情况是:

./test: error while loading shared libraries: libmystack.so: cannot open shared object file: No such file or directory

 

这是因为操作系统无法找到库。libmystack.so位于当前路径,位于库文件的默认路径之外。尽管我们在编译时(compile time)提供了.so文件的位置,但这个信息并没有写入test可执行文件(runtime)。可以使用下面命令测试:

$ldd test

ldd用于显示可执行文件所依赖的库。显示:

    linux-vdso.so.1 =>  (0x00007fff31dff000)
    libmystack.so => not found
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fca30de7000)
    /lib64/ld-linux-x86-64.so.2 (0x00007fca311cb000)

这说明test可执行文件无法找到它所需的libmystack.so库文件。

 

为了解决上面的问题,我们可以将.so文件放入默认搜索路径中。但有时,特别是多用户环境下,我们不享有在默认搜索路径写入的权限。

一个解决方案是设置LD_LIBRARY_PATH环境变量。比如:

$export LD_LIBRARY_PATH=.

这样,可执行文件执行时,操作系统将在先在LD_LIBRARY_PATH下搜索库文件,再到默认路径中搜索。环境变量的坏处是,它会影响所有的可执行程序。如果我们在编译其他程序时,如果我们不小心,很可能导致其他可执行文件无法运行。因此,LD_LIBRARY_PATH环境变量多用于测试。

另一个解决方案,即提供-rpath选项,将搜索路径信息写入test文件(rpath代表runtime path)。这样就不需要设置环境变量。这样做的坏处是,如果库文件移动位置,我们需要重新编译test。使用如下命令编译test.c:

$gcc -g -o test test.c -lmystack -L. -Wl,-rpath=.

-Wl表示,-rpath选项是传递给连接器(linker)。

 

test顺利执行的结果为:

Stack is null? 0
pop: 8
pop: 2
pop: 1
Stack is null? 1
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/huangbo1221/article/details/53456391

智能推荐

用C#实现二进制的减法(包括二进制小数)-程序员宅基地

欢迎使用Markdown编辑器写博客本Markdown编辑器使用StackEdit修改而来,用它写博客,将会带来全新的体验哦:Markdown和扩展Markdown简洁的语法代码块高亮图片链接和图片上传LaTex数学公式UML序列图和流程图离线写博客导入导出Markdown文件丰富的快捷键快捷键加粗 Ctrl + B 斜体 Ctrl + I 引用 Ctrl

思考题:如何获取当天的农历日期?-程序员宅基地

万年历大家肯定都用过,一般都有阳历、农历、节气等信息,但是你是否想过农历日期是如何获取的?阳历日期的获取很简单,以 Javascript 为例,有 Date 对象,可以调用它的 API 获取年、月、日信息,但是农历日期并不像阳历一样有规律,更别谈 API 了。所以,对于农历日期的获取我们只能打表。纳尼,打表?岂不是 1 年 365 天要打 365 个值的表么?非也。我们先来了解下农历的一些基本...

linux忘记mysql密码解决方法-程序员宅基地

执行如下脚本保存至.sh文件,并使用chmod +x 改变文件的属性,并执行改脚本#!/usr/bin/bashecho “skip-grant-tables”>>/etc/my.cnfsystemctl restart mysqldmysql -e “select user,host,authentication_string from mysql.user where us...

解决IDEA项目正常运行,但java文件出现红色波浪线问题_idea红色波浪线怎么解决_汤米的技术空间的博客-程序员宅基地

IDEA项目能够正常运行,但是点击每一个JAVA文件会出现好多红色的下划线:解决方法:1.可能是没有清除原来的历史缓存,导致一些错误,解决办法是File–Invalidate Caches,清除缓存,然后重启IDEA。2.若是不成功,在IDEA的setting–plugins,在Marktplace搜索lombok下载此插件安装重启即可。OK,这样项目就没有红色波浪线了,亲测有效..._idea红色波浪线怎么解决

的列换行_Excel中,如果要在公式中输入换行符,需要用Char函数-程序员宅基地

在 Excel 中,如果要在公式中输入换行符,需要用Char函数;如果要批量删除单元格中的空行、换行符和回车符,有两种方法,一种为用查找替换法,另一种为用公式法;如果用前者不能把所有空行、换行符和回车符都替换掉,可以用公式法;以下就是它们的具体操作方法,实例操作所用版本均为 Excel 2016。一、Excel公式输入换行符1、假如要在“Excel函数”与“Excel公式”之间输入一个换行符。双击..._excel换行符代码是什么

关于多态的理解_多态 父类实现方法就是浪费-程序员宅基地

多态:由于对象不同可能会有不同的行为,例如父类是人,他里面的方法是休息,那么子类如果是小孩的话,他的方法可能就是玩玩具,如果子类是程序员,他的方法可能就是敲代码,这些方法都叫重写。显而易见,小孩和程序员都叫人,但人不一定就是小孩就是程序员,子类一定属于父类,但父类不一定就是子类多态的实现其实就是通过父类或接口调用子类或实现类里的重写方法或者实现方法,除非继承的子类没有重写任何父类的方法,否则..._多态 父类实现方法就是浪费

随便推点

Kinect的介绍和应用【转】-程序员宅基地

一、Kinect简介 Kinectfor Xbox 360,简称Kinect,是由微软开发,应用于Xbox 360 主机的周边设备。它让玩家不需要手持或踩踏控制器,而是使用语音指令或手势来操作Xbox360的系统界面。它也能捕捉玩家全身上下的动作,用身体来进行游戏,带给玩家“免控制器的游戏与娱乐体验”。其在2010年11月4日于美国上市,建议售价149美金。Kinect在销售前

smiley-http-proxy-servlet 反向代理-程序员宅基地

背景 反向代理(Reverse Proxy)方式是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求连接的客户端,此时代理服务器对外就表现为一个反向代理服务器。 简单来说,你的反向代理服务器会接收请求,但其自身不处理该..._proxyservlet

【分支限界法】求解TSP问题_分支限界法求解tsp问题csdn-程序员宅基地

问题描述TSP问题是指旅行家要旅行n个城市,要求各个城市经历且仅经历一次然后回到出发城市,并要求所走的路程最短。求解思路 采用贪心法求得近似解为1→3→5→4→2→1,其路径长度为1+2+3+7+3=16,这可以作为TSP问题的上界。 把矩阵中每一行最小的元素相加,可以得到一个简单的下界,其路径长度为1+3+1+3+2=10,但是还有一个信..._分支限界法求解tsp问题csdn

Universal Serial Bus-程序员宅基地

http://wiki.osdev.org/Universal_Serial_BusThe Universal Serial Bus was first introduced in 1994 with the intention of replacing various specialized interfaces, and to simplify the conf

Resolve error: unable to load resolver "node" src\main.js:1:1-程序员宅基地

✘ https://google.com/#q=import%2Fno-duplicates Resolve error: unable to load resolver "node" src\main.js:1:1 // The Vue build version to load with the `import` command ^ 1 https://googl..._resolve error

5. SAP S/4 运维基础知识(Basic Knowledge) - SAP S/4 Basis Learning_sap basis csdn高原gy-程序员宅基地

本系列基于 SAP S/4 HANA version 1709 -On Premise前言本文用于读者快速了解SAP Basis的一些基本概念,不对具体的操作进行详细说明目录目录前言目录系统架构(Structure)S/4 HANA架构-应用视角S/4 HANA架构-服务器视角Profile参数(Profile Parameters)基本概..._sap basis csdn高原gy

推荐文章

热门文章

相关标签