牛客模拟面试-03
静态库和动态库如何制作及使用,区别是什么
静态库在程序的链接阶段链接到可执行文件中,其实静态库就是一堆目标文件的集合
构建静态库
使用这个命令也可以编译成功,但是这样很繁琐,不如将其功能打包成一个静态库 gcc main.c ./src/add.c ./src/div.c ./src/mult.c ./src/sub.c ./include/head.h -o app
静态库制作
cd src
将源文件编译成目标文件
gcc -c add.c div.c mult.c sub.c
之后会编译而不链接生成 add.o dic.o mult.o sub.o
使用ar命令构建静态库
注意libcalc.a在使用-l时,链接器会忽略lib和.a前缀和后缀,所以只需要-lcalc即可
ar rv ../lib/libcalc.a add.o div.o mult.o sub.o
显示打包文件的内容
ar t ../lib/libcalc.a
编译main.c
gcc -c main.c
链接静态库并编译目标文件main.o,这里需要指定连接静态库的目录和包含头文件的目录
gcc main.o -o app -lcalc -L ./lib/ -I ./include/
运行可执行文件(编译链接后的) ./app
构建动态库
到了程序中;动态库在链接阶段没有被复制到程序中,而是程序在运行时由系统动态加 载到内存中供程序调用。
cd src
PIC表示位置无关代码的意思 -f表示设置PIC为true
gcc -c -fPIC add.c sub.c mult.c div.c
gcc -shared add.o sub.o mult.o div.o -o ../lib/libcalc.so
gcc main.c -o app -I ./include/ -l calc -L ./lib/
直接运行./app会报错,找不到动态库的路径
- 静态库:GCC 进行链接时,会把静态库中代码打包到可执行程序中
- 动态库:GCC 进行链接时,动态库的代码不会被打包到可执行程序中
- 程序启动之后,动态库会被动态加载到内存中,通过 ldd (list dynamic dependencies)命令检查动态库依赖关系
- 如何定位共享库文件呢? 当系统加载可执行代码时候,能够知道其所依赖的库的名字,但是还需要知道绝对路径。 此时就需要系统的动态载入器来获取该绝对路径。 对于elf格式的可执行程序,是 由ld-linux.so来完成的, 它先后搜索elf文件的 DT_RPATH段 ——> 环境变量 LD_LIBRARY_PATH ——> /etc/ld.so.cache文件列表 ——>/lib/,/usr/lib 目录找到库文件后将其载入内存。
通过ldd指令检查app的动态依赖关系如下,发现找不到所以来的动态链接库libcalc.so的绝对路径。 以下方法只在当前终端有效
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/yeahqing/git/CppLearn/CxxProjects/静态库和动态库的构建/动态库的构建/lib
以下方法永久有效
方法一: 修改当前用户的环境变量 vim ~/.bashrc 最后一行插入: export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/usr_name/linux/lession06/library/lib source ~/.bashrc
修改系统环境变量 方法二: sudo vim /etc/profile 最后一行插入:export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/usr_name/linux/lession06/library/lib source /etc/profile
方法三 配置/etc/ld.so.cache文件列表 sudo vim /etc/ld.so.conf 最后一行插入:/home/usr_name/linux/lession06/library/lib sudo ldconfig
方法四 将所依赖度动态库复制到/lib或/usr/lib目录下也可,但不推荐,因为这两个目录下有大量的系统库文件
修改Ubuntu20.04下的终端只显示当前目录名
修改当前用户的~/.bashrc 使用:62找到第62行。 将里面的小写w改为大写W即可。之后保存退出,使用source ~/.bashrc使其生效。
if [ "$color_prompt" = yes ]; then
PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
else
PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ '
fi
说一说STL 中常见容器的实现原理
【得分点】
vector、deque、stack、queue、list、set、map
【参考答案】
标准回答
- vector
vector 是一种动态数组,在内存中具有连续的存储空间,支持快速随机访问,由于具有连续的存储空间,所以在插入和删除操作方面,效率比较慢。
- deque
deque 是一种双向开口的连续线性空间,元素在内存连续存放,随机存取任何元素都在常数时间完成,在两端增删元素具有较佳的性能。
- stack
stack 是一种先进后出(First In Last Out,FILO)的数据结构,它只有一个出口。stack 容器允许新增元素,移除元素,取得栈顶元素,但是除了最顶端外,没有任何其他方法可以存取 stack 的其他元素,stack 不允许有遍历行为。
- queue
queue 是一种先进先出(First In First Out,FIFO)的数据结构,它有两个出口,queue 容器允许从一端新增元素,从另一端移除元素。
- list
list 内部实现的是一个双向链表,元素在内存不连续存放,在任何位置增删元素都能在常数时间完成,不支持随机存取。
-
map map内部自建一棵红黑树(一种自平衡二叉树),这棵树具有数据自动排序的功能,所以在map内部所有的数据都是有序的,以二叉树的形式进行组织。
-
set
set 底层使用红黑树实现,插入删除操作时仅仅移动指针即可,不涉及内存的移动和拷贝,所以效率比较高。set 中的元素都是唯一的,而且默认情况下会对元素进行升序排列。不能直接改变元素值,因为那样会打乱原本正确的顺序,要改变元素值必须先删除旧元素,再插入新元素。不提供直接存取元素的任何操作函数,只能通过迭代器进行间接存取。
简述一下堆和栈的区别
【得分点】
管理方式、空间大小、是否产生内存碎片、生长方向、分配方式、分配效率
【参考答案】
标准回答
堆和栈主要有如下几点区别:管理方式、空间大小、是否产生内存碎片、生长方向、分配方式、分配效率。
- 管理方式
对于栈来讲,是由编译器自动管理,无需手动控制;对于堆来说,分配和释放都是由程序员控制的。
- 空间大小
总体来说,栈的空间是要小于堆的。堆内存几乎是没有什么限制的;但是对于栈来讲,一般是有一定的空间大小的。
- 碎片问题
对于堆来讲,由于分配和释放是由程序员控制的(利用new/delete 或 malloc/free),频繁的操作势必会造成内存空间的不连续,从而造成大量的内存碎片,使程序效率降低。对于栈来讲,则不会存在这个问题,因为栈是先进后出的数据结构,在某一数据弹出之前,它之前的所有数据都已经弹出。
- 生长方向
对于堆来讲,生长方向是向上的,也就是沿着内存地址增加的方向,对于栈来讲,它的生长方式是向下的,也就是沿着内存地址减小的方向增长。
- 分配方式
堆都是动态分配的,没有静态分配的堆。栈有两种分配方式:静态分配和动态分配,静态分配是编译器完成的,比如局部变量的分配;动态分配由alloca函数进行分配,但是栈的动态分配和堆是不同的,它的动态分配是由编译器实现的,无需我们手工实现。
- 分配效率
栈是机器系统提供的数据结构,计算机会在底层对栈提供支持,分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率很高。堆则是C/C++ 函数提供的,它的机制是很复杂的,例如为了分配一块内存,库函数会按照一定的算法在堆内存中搜索可用的足够大小的空间,如果没有足够大小的空间(可能是由于碎片太多),就有可能调用系统功能去增加程序数据段的内存空间,这样就有机会分到足够大小的内存,然后进行返回。显然,堆的效率要比栈底的多。
编程题: 01背包问题,动态规划
版本1
int knapsack(int V, int n, vector<vector<int> >& vw) {
vector<vector<int>> dp(n+1, vector<int>(V+1));
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= V; j++)
{
dp[i][j] = dp[i-1][j];
if (j >= vw[i-1][0])
{
dp[i][j] = max(dp[i][j], dp[i-1][j-vw[i-1][0]] + vw[i-1][1]);
}
}
}
return dp[n][V];
}
int knapsack(int V, int n, vector<vector<int> >& vw) {
vector<int> dp(V+1, 0);
for(int i = 0; i < n; i++){
for (int j = V; j >= vw[i][0]; j--)
{
dp[j] = max(dp[j], dp[j - vw[i][0]] + vw[i][1]);
}
}
return dp[V];
}