C 编译链接与执行总结(头文件路径库文件链接环境变量……都是什么鬼?)
Posted 陆嵩
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C 编译链接与执行总结(头文件路径库文件链接环境变量……都是什么鬼?)相关的知识,希望对你有一定的参考价值。
C 编译链接与执行总结(头文件路径、库文件链接、环境变量……都是什么鬼?)
问:为什么程序编译链接又报找不到 .so 文件的错误?明明手动都能找到,也指定了路径,有 bug 吧?
答:确实有 bug ,你有 bug。经常郁于程序的编译链接或者执行失败而直呼 C 是个玄学的人,十有八九都是对 C 的编译链接执行过程不慎了解,把各种搜索路径环境变量搞得一塌糊涂的人。还有救吗?有救,何弃疗。
有病就得治,首先祭出杀手锏——
回魂丹:包治百病,不服来辩
不过现在某度搜索越来越垃圾了,经常搜不到你想搜到的东西。有条件的用 GOOGLE 吧。没条件的自己创造条件吧。
深入理解 C 的编译链接与执行
从一个例子开始
举个简单的例子。
假设我现在安装了一个包,我们先来搜索一下安装之后它的位置:
sudo updatedb
locate scientific.h
locate libscientific.so
结果:
/usr/local/include/scientific.h
/usr/local/lib/libscientific.so
我再写一个简单的 c 文件 generatedataset.c (下面称为主程序)来调用安装好的这个包:
#include <stdio.h>
#include <stdlib.h>
#include <scientific.h>
#include <math.h>
#include <time.h>
#include <sys/time.h>
size_t curr_milliseconds(){
struct timeval te;
gettimeofday(&te, NULL); // get current time
return (size_t) te.tv_usec/1000;
}
double rnorm(x, avg, var)
{
return 1./sqrt(2*_pi_*var) * exp( -1 * square(x-avg)/(2*var));
}
double randBetween(double min, double max)
{
return ((double)rand()/RAND_MAX) * (max - min) + min;
}
void MatrixRandomFill(matrix *m, double min, double max)
{
size_t i, j;
for(i = 0; i < m->row; i++){
for(j = 0; j < m->col; j++){
m->data[i][j] = randBetween(min, max);
}
}
}
void ConvertToRNormMatrixValues(matrix *m, double avg, double var)
{
size_t i, j;
for(i = 0; i < m->row; i++){
for(j = 0; j < m->col; j++){
//double x = randBetween(-2.4, 2.4);
double x = m->data[i][j];
m->data[i][j] = rnorm(x, avg, var);
}
}
}
void SaveMatrixToFile(matrix *m, char *outfilename)
{
size_t i, j;
FILE *fptr;
fptr = fopen(outfilename, "a");
for(i = 0; i < m->row; i++){
for(j = 0; j < m->col-1; j++){
fprintf(fptr,"%.4f,", m->data[i][j]);
}
fprintf(fptr, "%.4f\\n", m->data[i][m->col-1]);
}
fprintf(fptr, "END\\n");
fclose(fptr);
}
int main(int argc, char **argv){
size_t n_train, n_val, n_singular, msize, n_max;
double rmin = atof(argv[3]);
double rmax = atof(argv[4]);
int det;
char smx[35], simx[35], tmx[35], timx[35], vmx[35], vimx[35];
matrix *m, *m_inv;
if(argc != 5){
printf("\\nUsage: %s [matrix size] [number of samples] [range min] [range max]\\n", argv[0]);
}
else{
msize = atoi(argv[1]);
n_max = atoi(argv[2]);
sprintf(smx, "sing_matrix_%lux%lu.mx", msize, msize);
sprintf(simx, "sing_moore-penrose_inverse_%lux%lu.mx", msize, msize);
sprintf(tmx, "train_matrix_%lux%lu.mx", msize, msize);
sprintf(timx, "train_target_inverse_%lux%lu.mx", msize, msize);
sprintf(vmx, "val_matrix_%lux%lu.mx", msize, msize);
sprintf(vimx, "val_target_inverse_%lux%lu.mx", msize, msize);
//srand(14159265358979323846264338327950288419716939937510);
srand(curr_milliseconds());
n_train = 0;
n_singular = 0;
/*
* Generate the training set
*/
while(n_train < n_max){
NewMatrix(&m, msize, msize);
MatrixRandomFill(m, rmin, rmax);
det = (int)floor(fabs(MatrixDeterminant(m)));
if(det == 0){
if(n_singular < n_max){
/* Ops.. a singular matrix was generated... */
printf("Ops... this matrix is singular!! -> ");
printf("Determinant == %d\\n", det);
SaveMatrixToFile(m, smx);
initMatrix(&m_inv);
MatrixMoorePenrosePseudoinverse(m, &m_inv);
SaveMatrixToFile(m_inv, simx);
DelMatrix(&m_inv);
n_singular++;
}
else{
continue;
}
}
else{
initMatrix(&m_inv);
MatrixInversion(m, &m_inv);
SaveMatrixToFile(m, tmx);
SaveMatrixToFile(m_inv, timx);
DelMatrix(&m_inv);
n_train++;
}
DelMatrix(&m);
}
/*
* Generate the external testing set
*/
n_val = 0;
while(n_val < n_max){
NewMatrix(&m, msize, msize);
MatrixRandomFill(m, rmin, rmax);
det = (int)floor(fabs(MatrixDeterminant(m)));
if(det == 0){
/* Ops.. a singular matrix was generated... */
if(n_singular < n_max){
printf("Ops... this matrix is singular!! -> ");
printf("Determinant == %d\\n", det);
SaveMatrixToFile(m, smx);
initMatrix(&m_inv);
MatrixMoorePenrosePseudoinverse(m, &m_inv);
SaveMatrixToFile(m_inv, simx);
DelMatrix(&m_inv);
n_singular++;
}
else{
continue;
}
}
else{
initMatrix(&m_inv);
MatrixInversion(m, &m_inv);
SaveMatrixToFile(m, vmx);
SaveMatrixToFile(m_inv, vimx);
DelMatrix(&m_inv);
n_val++;
}
DelMatrix(&m);
}
/*
* Generate the singular matrix testset.
* For those matrix determinant must be 0 and
* thus no inverse matrix is possible.
*/
while(n_singular < n_max){
/*
Simplest method to get singular matrix
A is a n x n square matrix.
If two rows are = then there the matrix is singular..
*/
NewMatrix(&m, msize, msize);
MatrixRandomFill(m, rmin, rmax);
int r1_row = (int)randBetween(0.f, (double)m->row);
int r2_row = (int)randBetween(0.f, (double)m->row);
//printf("copy row %d into %d\\n", r2_row, r1_row);
for(int j = 0; j < m->col; j++){
m->data[r1_row][j] = m->data[r2_row][j];
}
int det = (int)floor(fabs(MatrixDeterminant(m)));
if(det == 0){
SaveMatrixToFile(m, smx);
initMatrix(&m_inv);
MatrixMoorePenrosePseudoinverse(m, &m_inv);
SaveMatrixToFile(m_inv, simx);
DelMatrix(&m_inv);
n_singular++;
}
else{
continue;
}
DelMatrix(&m);
/*
More complex method to get singular matrix
A is a n x m matrix
singular matrix = A * A_T
NewMatrix(&m, 3*i, i);
NewMatrix(&m_t, i, 3*i);
NewMatrix(&m_m_t, i, i);
MatrixRandomFill(m, rmin, rmax);
MatrixTranspose(m, m_t);
MatrixDotProduct(m_t, m, m_m_t);
double det = MatrixDeterminant(m_m_t);
if(FLOAT_EQ(det, 0.f, 1e-1)){
SaveMatrixToFile(m_m_t, "singular_matrix.mx");
}
else{
printf("Determinant != 0 -> %.4f\\n", det);
PrintMatrix(m_m_t);
}
DelMatrix(&m);
DelMatrix(&m_t);
DelMatrix(&m_m_t);
*/
}
}
return 0;
}
接下来就是这个文件的编译链接和运行了。
个人偏好使用 locate 查找文件。前面哪里没有看明白的,上滚第一页服一颗回魂丹。
编译链接
gcc -o generatedataset generatedataset.c -O2 -I/usr/local/include -lm -L/usr/local/lib -lscientific
简单解释一下这句话依次做了什么事情,当然,我不讲什么预处理、编译、汇编和链接,那说的都不是人话,都是扯淡。
-
第一步:generatedataset 文件里面包含了一些头文件,gcc 按如下的顺序,查找这个问题,把它们里面的内容原封不动地拷贝到对应的位置。
-I
指定的目录,本例-I/usr/local/include
- 环境变量
C_INCLUDE_PATH
,CPLUS_INCLUDE_PATH
,OBJC_INCLUDE_PATH
- 系统指定目录
到这,头文件的部分就完成了。之后的事情与它再无相关。
-
第二步:
lm
和lscientific
告诉程序,等等我这个程序的执行阶段,需要用到动态链接库libm.so
和我安装的libscientific.so
。记得要把这两个库的名字记下来,等等执行的时候到系统指定的一些搜索路径下按顺序去找。记住,这里记下的只是一个名字而已。 -
第三步:编译链接。依次从编译链接命令中的 -L 选项
-L/usr/local/lib
和环境变量的LIBRARY_PATH
(编译时库路径) 指定的路径中去查找是否存在刚刚我们链接的两个文件libm.so
和libscientific.so
,如果两个当中有一个找不到,就报错。如果都找到了,就暂时地把这两个文件一起拿过来,帮助主程序编译。所谓的“暂时”是说,主程序的编译可能依赖这两个文件,比如说一些变量和函数的声明可能在那两个文件里面。等到主程序编译成功完成了,这两个文件的使命就完成了,主程序就一脚把这两个暂时借来的家伙一脚踢开,不让你里面的任何一部分称为我里面的任何一部分,我就是利用你们完成编译而已。编译完了,你们就滚蛋,有点过河拆桥的意思。链接编译之后,生成可执行文件generatedataset
。-L/usr/local/lib
记下的信息后面就没用了。一般来说,调用一个东西的时候,申明都是写在头文件里面,实现写在 cpp 文件(编译后可成为 .so 库文件)里面,但是也有例外,所以,我们都把他暂时借过来,辅助一些编译。事实上,如果主程序调用了库里的某个函数,编译的时候,我们主程序需要的,只不过是整个函数的一个声明而已,至于具体的实现,执行的时候再给它就可以。
生成可执行文件时,
-Rpath
选项链接程序会在可执行文件本身中记录共享库的路径。-L
选项在生成时指示到哪里查找-llibrary
选项所指定的库,只是暂时地借过来帮助编译,不会将该路径记录到二进制可执行文件中。
执行
./generatedataset 3
执行的时候,把我们在编译的时候记下的两个名字libm.so
和 libscientific.so
拿出来,从如下路径中去找着两个文件:
- 编译目标代码时
-rpath
指定的执行时动态库搜索路径。 /etc/ld.so.conf
指定的路径- 环境变量
LD_LIBRARY_PATH
(执行时动态链接库路径) 给出的路径。 - 系统指定的路径,如
/lib
和/usr/lib/
等等。
找到这两个文件之后,把它们作为主程序可以调用的家庭成员,执行程序。
至此,就是整个编译链接执行的过程。还没明白?左转直走,找回魂丹。
强调
需要强调的是,编译的时候链接的路径:-L/usr/local/lib
,不一定在执行的时候所需要的库搜索路径里面,如果不在,就会告诉你找不到。不要以为你编译的时候,通过 -L
把动态链接库链接进来了,执行的时候,就一定会去搜索这个路径。
编译时的动态链接库路径和执行时的动态链接库路径,是两码事
编译仅仅是编译,是个主程序的个体完善行为。只有在运行的时候,才会在指定的动态库搜索路径集合中按顺序去查找库链接进来。-L
只在编译时指定搜索路径,编译完成后,这个搜索路径不会在二进制可执行文件中留下任何痕迹。也就是说,在执行二进制文件时,这个路径可以作为运行时搜索库文件路径时使用,如果找不到,再按照系统默认的运行时路径搜索库文件。某种意义下来说,实际链接发生在运行时而不是生成时。生成时的链接只不过是暂借。
如果你需要的库文件路径不在执行时的被搜索路径里面怎么办呢?只要把它添加到如下所说的搜索路径里面即可。
error while loading shared libraries: libscientific.so: cannot open shared object file: No such file or directory
- 修改
/etc/ld.so.conf
配置文件:路径加入/etc/ld.so.conf
,运行ldconfig
。 - 把当前路径加入环境变量
LD_LIBRARY_PATH
:export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib
。 - 把动态链接库文件拷如已有的执行时动态链接库搜索路径。
ldd xxx
可查询当前执行搜索路径中能否找到我运行程序所需要的库文件。
C 编译常规操作查询字典(用来复制黏贴)
查文件
sudo updatedb
locate scientific.h
locate libscientific.so
添加执行时动态链接库文件搜索路径
sudo gedit /etc/ld.so.conf
ldconfig
临时修改环境变量
export PATH=$PATH:/usr/local/bin #可执行文件搜索路径
export C_INCLUDE=$PATH:/usr/local/include #C 头文件搜索路径
export CPP_INCLUDE_PATH=$PATH:/usr/local/include #CPP 头文件搜索路径
export LIBRARY_PATH=$PATH:/usr/local/lib #CPP 程序编译器链接库搜索路径
export LD_LIBRARY_PATH=$PATH:/usr/local/lib #CPP 程序运行时动态链接库搜索路径
永久修改环境变量
sudo vim /etc/profile
export PATH=$PATH:/usr/local/bin #可执行文件搜索路径
reboot
方法不唯一,嫌弃,请服文头回魂丹。
查执行依赖是否满足
ldd a.out
编译库指定头文件和库文件目录
gcc main.c -I/usr/local/include
gcc main.c -L/usr/local/lib -lfoo
gcc main.c -L/usr/local/lib -static -lfoo
参考链接
https://blog.csdn.net/lusongno1/article/details/80802096
以上是关于C 编译链接与执行总结(头文件路径库文件链接环境变量……都是什么鬼?)的主要内容,如果未能解决你的问题,请参考以下文章