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
    • 系统指定目录

    到这,头文件的部分就完成了。之后的事情与它再无相关。

  • 第二步: lmlscientific 告诉程序,等等我这个程序的执行阶段,需要用到动态链接库 libm.so 和我安装的 libscientific.so。记得要把这两个库的名字记下来,等等执行的时候到系统指定的一些搜索路径下按顺序去找。记住,这里记下的只是一个名字而已。

  • 第三步:编译链接。依次从编译链接命令中的 -L 选项 -L/usr/local/lib 和环境变量的LIBRARY_PATH(编译时库路径) 指定的路径中去查找是否存在刚刚我们链接的两个文件 libm.solibscientific.so,如果两个当中有一个找不到,就报错。如果都找到了,就暂时地把这两个文件一起拿过来,帮助主程序编译。所谓的“暂时”是说,主程序的编译可能依赖这两个文件,比如说一些变量和函数的声明可能在那两个文件里面。等到主程序编译成功完成了,这两个文件的使命就完成了,主程序就一脚把这两个暂时借来的家伙一脚踢开,不让你里面的任何一部分称为我里面的任何一部分,我就是利用你们完成编译而已。编译完了,你们就滚蛋,有点过河拆桥的意思。链接编译之后,生成可执行文件 generatedataset-L/usr/local/lib 记下的信息后面就没用了。

    一般来说,调用一个东西的时候,申明都是写在头文件里面,实现写在 cpp 文件(编译后可成为 .so 库文件)里面,但是也有例外,所以,我们都把他暂时借过来,辅助一些编译。事实上,如果主程序调用了库里的某个函数,编译的时候,我们主程序需要的,只不过是整个函数的一个声明而已,至于具体的实现,执行的时候再给它就可以。

生成可执行文件时, -Rpath 选项链接程序会在可执行文件本身中记录共享库的路径。-L 选项在生成时指示到哪里查找 -llibrary 选项所指定的库,只是暂时地借过来帮助编译,不会将该路径记录到二进制可执行文件中。

执行

 ./generatedataset 3

执行的时候,把我们在编译的时候记下的两个名字libm.solibscientific.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_PATHexport 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

https://blog.csdn.net/lusongno1/article/details/106650029

https://blog.csdn.net/lusongno1/article/details/100129086

以上是关于C 编译链接与执行总结(头文件路径库文件链接环境变量……都是什么鬼?)的主要内容,如果未能解决你的问题,请参考以下文章

Linux添加可执行文件/头文件/链接库的环境变量

GCC编译器编译链接

Linux环境下如何编译和执行c程序

实验一(开发环境的熟悉)问题总结

编译链接实战添加头文件路径和库路径

程序的环境和预处理