之前讲gcc编译的时候,参看:C语言再学习 -- GCC编译过程 提到过静态库和共享库,那时只是简单的讲了下它们相关的编译链接,接下来就该详细介绍它们了。不过再讲解之前还需了解一下编程相关的环境变量。
一、环境变量
参看:百度百科--环境变量
环境变量一般是指在操作系统中用来指定操作系统运行环境的一些参数,如:临时文件夹位置和系统文件夹位置等。
环境变量时在操作系统中一个具有特定名字的对象,它包含了一个或者多个应用程序所将使用的信息。
1、Windows下的环境变量
(1)环境变量配置
右击我的电脑->属性->高级系统设置->高级->环境变量->系统变量->选中Path 点击编辑->在Path变量值的最后增加分号(;),再增加新的路径。
注意:(千万不要改动之前的变量值)->一路点击确定即可
(2)常见环境变量:(了解)
%ALLUSERSPROFILE% 局部 返回所有“用户配置文件”的位置。
%APPDATA% 局部 返回默认情况下应用程序存储数据的位置。
%CD% 局部 返回当前目录字符串。
%CMDCMDLINE% 局部 返回用来启动当前的 Cmd.exe 的准确命令行。
%CMDEXTVERSION% 系统 返回当前的“命令处理程序扩展”的版本号。
%COMPUTERNAME% 系统 返回计算机的名称。
%COMSPEC% 系统 返回命令行解释器可执行程序的准确路径。
%DATE% 系统 返回当前日期。使用与 date /t 命令相同的格式。由 Cmd.exe 生成。有关 date 命令的详细信息,请参阅 Date。
%ERRORLEVEL% 系统 返回使用过的命令的错误代码。通常用非零值表示错误。
%HOMEDRIVE% 系统 返回连接到用户主目录的本地工作站驱动器号。基于主目录值的设置。用户主目录是在“本地用户和组”中指定的。
%HOMEPATH% 系统 返回用户主目录的完整路径。基于主目录值的设置。用户主目录是在“本地用户和组”中指定的。
%HOMESHARE% 系统 返回用户的共享主目录的网络路径。基于主目录值的设置。用户主目录是在“本地用户和组”中指定的。
%LOGONSEVER% 局部 返回验证当前登录会话的域控制器的名称。
%NUMBER_OF_PROCESSORS% 系统 指定安装在计算机上的处理器的数目。
%OS% 系统 返回操作系统的名称。Windows 2000 将操作系统显示为 Windows_NT。
%PATH% 系统 指定可执行文件的搜索路径。
%PATHEXT% 系统 返回操作系统认为可执行的文件扩展名的列表。
%PROCESSOR_ARCHITECTURE% 系统 返回处理器的芯片体系结构。值: x86,IA64。
%PROCESSOR_IDENTIFIER% 系统 返回处理器说明。
%PROCESSOR_LEVEL% 系统 返回计算机上安装的处理器的型号。
%PROCESSOR_REVISION% 系统 返回处理器修订号的系统变量。
%PROMPT% 局部 返回当前解释程序的命令提示符设置。由 Cmd.exe 生成。
%RANDOM% 系统 返回 0 到 32767 之间的任意十进制数字。由 Cmd.exe 生成。
%SYSTEMDRIVE% 系统 返回包含 Windows XP 根目录(即系统根目录)的驱动器。
%SYSTEMROOT% 系统 返回 Windows XP 根目录的位置。
%TEMP% and %TMP% 系统和用户 返回对当前登录用户可用的应用程序所使用的默认临时目录。有些应用程序需要 TEMP,而其它应用程序则需要 TMP。
%TIME% 系统 返回当前时间。使用与 time /t 命令相同的格式。由 Cmd.exe 生成。有关 time 命令的详细信息,请参阅 Time。
%USERDOMAIN% 局部 返回包含用户帐户的域的名称。
%USERNAME% 局部 返回当前登录的用户的名称。
%UserProfile% 局部 返回当前用户的配置文件的位置。
2、Linux下的环境变量(重点)
参看:Linux环境变量
linux是一个多用户的操作系统,每个用户登录系统之后,都会有一个专用的运行环境。通常每个用户默认的环境都是相同的。这个默认的环境实际上就是一组环境变量的定义,用户可以对自己的运行环境进行定制,其方法就是修改相应的系统环境变量。
在linux中,环境变量一般用大写加下划线命名(例如,PATH、ORACLE_HOME)。环境变量就相当于一个指针,当我们要查看指针所指向的值的时候需要解引用。同样的,当我们想要查看环境变量里面的值的时候,需要在前面加 $ 引用。
linux的变量分为环境变量和本地变量:
环境变量:是一种全局变量,存在所有的shell中,在登录的时候就有系统定义的环境变量了。linux的环境变量具有继承性,即shell会继承父shell的环境变量。
本地变量:当前shell中的变量,本地变量中包含环境变量。Linux的本地变量的非环境变量不具备继承性。
在Linux下面的变量按照生存周期可分为两类:
永久的:需要修改配置文件,变量永久生效。
临时的:使用export命令声明即可,变量在关闭shell时失效。
修改和查看环境变量的指令
(1)echo:查看单个环境变量
例如,显示环境变量HOME
# echo $HOME /root
(2)env:查看所有环境变量
例如,查看所有的环境变量,可以看一下都有哪些环境变量。
# env LC_PAPER=en_US.UTF-8 LC_ADDRESS=en_US.UTF-8 SSH_AGENT_PID=1768 LC_MONETARY=en_US.UTF-8 GPG_AGENT_INFO=/tmp/keyring-rB9csr/gpg:0:1 TERM=xterm SHELL=/bin/bash XDG_SESSION_COOKIE=619793d1806621208703bfca00000005-1489544306.787815-2133755939 WINDOWID=31457285 LC_NUMERIC=en_US.UTF-8 GNOME_KEYRING_CONTROL=/tmp/keyring-rB9csr USER=root .....
也可以和grep一起使用查看某一类的环境变量
# env | grep SH SSH_AGENT_PID=1768 SHELL=/bin/bash SSH_AUTH_SOCK=/tmp/keyring-rB9csr/ssh SHLVL=1
(3)set:查看本地定义的环境变量
[email protected]:~# set BASH=/bin/bash BASHOPTS=checkwinsize:cmdhist:expand_aliases:extquote:force_fignore:histappend:hostcomplete:interactive_comments:progcomp:promptvars:sourcepath BASH_ALIASES=() BASH_ARGC=() BASH_ARGV=() BASH_CMDS=() BASH_LINENO=() BASH_SOURCE=() BASH_VERSINFO=([0]="4" [1]="2" [2]="24" [3]="1" [4]="release" [5]="i686-pc-linux-gnu") BASH_VERSION=‘4.2.24(1)-release‘ .....
(4)export:设置一个新的环境变量 (临时的,重启后消失)
注意,一般环境变量都用大写加下划线来命名。
例如,新建环境变量HELLO
# export HELLO="hello" # echo $HELLO hello
(5)unset:清除环境变量
例如,清除环境变量HELLO,清除后再查看为空
# unset HELLO # echo $HELLO #
(6)readonly:设置只读环境变量
例如,将环境变量HELLO设为只读,再修改或者清除该环境变量时会提示为只读变量,不能再对它进行操作。但是因为它是一个临时性的环境变量,所以用户退出登录以后就会自动失效。
# export HELLO="hello" # readonly HELLO="hello" # export HELLO="hello" world bash: HELLO: 只读变量 # unset HELLO bash: unset: HELLO: 无法反设定: 只读 variable
Linux系统常用的环境变量
(1)PATH:决定了shell将到哪些目录中寻找命令或程序
# echo $PATH /usr/lib/lightdm/lightdm:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/home/tarena/workdir/toolchains/opt/S5PV210-crosstools/4.4.6/bin
我们可以看到在当前目录下,默认的PATH的值。它表示当我们在当前目录下执行一条命令时命令的搜索路径。每一个目录都是以冒号隔开的。例如:export PATH=$PATH:/opt/arm-2009q1-203/bin:
当我们执行一个可执行的程序时,系统就会到这些目录下面去找,在这些目录下找到的话才执行,否则不执行。
(2)HOME:指定用户的主目录(即用户登录到Linux系统时的默认目录)
环境变量是一个变量,会随着用户的不同,它的值也就不同。
普通用户下的主目录:
# echo $HOME /home/admin
root超级用户下的主目录:
# echo $HOME /root
(3)HISTSIZE:保存历史命令记录的条数
在linux中可以查找以前输入的命令,HISTSIZE这个环境的值就表示最多保存的记录的数目。
# echo $HISTSIZE 1000
可以看到,上面显示能够保存1000条。
(4)LOGNAME:显示当前用户的登录名 (同 指令logname)
echo $LOGNAME root
(5)HOSTNAME:显示主机的名字 (同 指令hostname)
echo $HOSTNAME ubuntu
(6)SHELL:指当前用户使用的shell类型
echo $SHELL /bin/bash
(7)LANG/LANGUGE:语言相关的环境变量,多语言可以修改此环境变量
echo $LANG zh_CN.UTF-8
(8)MAIL:当前用户的邮件存放的目录 (无邮件)
# echo $MAIL
(9)PS1:命令基本提示符,对root是 #,对普通用户是 $
# echo $PS1 \[\e]0;\[email protected]\h: \w\a\]${debian_chroot:+($debian_chroot)}\[email protected]\h:\w\$
(10)PS2:附属提示符,默认是 >
# echo $PS2 >
存放环境变量的文件
(1)/etc/profile (所有用户)
这个文件是每个用户登录时都会运行的环境变量设置,当用户第一次登陆时该文件被执行,并从/etc/profile.d目录的配置文件中搜索shell的设置。这个文件的作用就是当用户登录的时候用于获取系统的环境变量,只在登录的时候获取一次。
所以说,在/etc/profile文件中添加的变量,对所有用户永久的生效。
之前用到过,参看:Hi3516A开发--安装交叉编译器
gedit /etc/profile 添加 export PATH="/opt/hisi-linux/x86-arm/arm-hisiv300-linux/target/bin:$PATH" 执行 source /etc/profile
需要注意的是,修改完文件后想要马上生效还要运行 source /etc/profile 不然只能在下次重进此用户时生效。
(2)~/.bash_profile (单个用户)
每个用户都可以使用该文件输入自己专用的shell信息,当用户登录时,该文件仅仅执行一次。默认情况下,它设置一些环境变量,执行用户的.bashrc文件。单个用户对此文件的修改只会影响到它以后的每一次登录。
也就是说,在用户目录下的.bash_profile文件中增加变量,仅对当前用户永久生效,操作同 /etc/profile
(3)/etc/bashrc (所有用户)
在执行完 /etc/profile 内容后,如果用户运行 bash shell 的话,则就执行这个文件,当每次一个新的bash shell 被打开时,该文件被读取。所以,如果每打开一个bash都执行某些操作,就可以在这个文件里面设置。
(4)~/.bashrc (单个用户)
该文件只包含专用于你的bash信息,当你登录时以及每次打开新的shell时,该文件就会自动被读取。
操作参看:DM368开发攻略——开发环境搭建
gedit .bashrc 添加 export PATH=$PATH:/opt/arm-2009q1-203/bin: 执行 source .bashrc
(5)~/.bash_logout
每次在退出shell的时候会执行该文件,它提供了定制用户环境的功能,比如删除账号内的临时文件等命令就可以放在bash_logout 文件内,如果这个文件不存在的话则就执行其他的命令。
3、编程相关的环境变量
(1)CPATH/C_INCLUDE_PATH
C头文件的附加搜索路径,相当于gcc的 -I 选项
(2)CPLUS_INCLUDE_PATH
C++头文件的附加搜索路径
(3)LIBRARY_PATH
链接时查找静态库和共享库的路径,相当于 -I 选项
(4)LD_LIBRARY_PATH
运行时查找共享库的路径
参看:LIBRARY_PATH和LD_LIBRARY_PATH的区别
关于Linux gcc中的 LIBRARY_PATH 和 LD_LIBRARY_PATH 参数说明
下面摘取了两篇较权威的说明资料:
1、 GNU 上关于LIBRARY_PATH的说明:
LIBRARY_PATH The value of LIBRARY_PATH is a colon-separated list of directories, much like PATH. When configured as a native compiler, GCC tries the directories thus specified when searching for special linker files, if it can‘t find them using GCC_EXEC_PREFIX. Linking using GCC also uses these directories when searching for ordinary libraries for the -l option (but directories specified with -L come first).
2、 man7 上关于LD_LIBRARY_PATH的说明:
LD_LIBRARY_PATH A colon-separated list of directories in which to search for ELF libraries at execution-time. Similar to the PATH environment variable. Ignored in set-user-ID and set-group-ID programs.
后面发现 StackOverflow 上关于 LIBRARY_PATH 和 LD_LIBRARY_PATH 的解释更直白:
LIBRARY_PATH is used by gcc before compilation to search for directories containing libraries that need to be linked to your program. LD_LIBRARY_PATH is used by your program to search for directories containing the libraries after it has been successfully compiled and linked. EDIT: As pointed below, your libraries can be static or shared. If it is static then the code is copied over into your program and you don‘t need to search for the library after your program is compiled and linked. If your library is shared then it needs to be dynamically linked to your program and that‘s when LD_LIBRARY_PATH comes into play.
Linux gcc编译链接时的共享库搜索路径
GCC编译、链接生成可执行文件时,共享库的搜索路径顺序如下(注意不会递归性地在其子目录下搜索):
1、gcc编译、链接命令中的-L选项;
2、gcc的环境变量的LIBRARY_PATH(多个路径用冒号分割);
3、gcc默认共享库目录:/lib:/usr/lib:usr/lib64:/usr/local/lib。
执行二进制文件时的共享库搜索路径
链接生成二进制可执行文件后,在运行程序加载共享库文件时,搜索的路径顺序如下:
1、编译目标代码时指定的共享库搜索路径:用选项 -Wl,rpath 和 include 指定的共享库的搜索路径,比如 gcc -Wl,-rpath,include -L. -ldltest hello.c,在执行文件时会搜索路径 `./include`;
2、环境变量LD_LIBRARY_PATH(多个路径用冒号分割);
3、在 /etc/ld.so.conf.d/ 目录下的配置文件指定的共享库绝对路径(通过ldconfig生效,一般是非root用户时使用);
4、gcc默认共享库目录:/lib:/usr/lib:usr/lib64:/usr/local/lib等。
其中,Linux GCC默认的共享库搜索路径可以通过 ld --verbose 命令查看:
SEARCH_DIR("/usr/i686-linux-gnu/lib32"); SEARCH_DIR("=/usr/local/lib32"); SEARCH_DIR("=/lib32"); SEARCH_DIR("=/usr/lib32"); SEARCH_DIR("=/usr/local/lib/i386-linux-gnu"); SEARCH_DIR("=/usr/local/lib"); SEARCH_DIR("=/lib/i386-linux-gnu"); SEARCH_DIR("=/lib"); SEARCH_DIR("=/usr/lib/i386-linux-gnu"); SEARCH_DIR("=/usr/lib");
总结下来就是,共享库编译的时候与静态库一样依赖 LIBRARAY_PATH,运行的旪候依赖 LD_LIBRARY_PATH 。
这也就是我们为什么讲了这么长时间的环境变量。
4、环境列表
环境列表就是指环境变量的集合,而每个进程都拥有一张独立的环境列表,来保存和当前进程相关的环境变量信息。
环境列表,采用字符指针数组来存储所有的环境变量。
例如:
PATH 0X01 //运行程序所有路径
CPATH 0X02 //C语言路径
LIBRARY_PATH 0X03 //静态库
LD_LIBRARY_PATH 0X04 //共享库
char* arr[5] = {0x01, 0x02, 0x03, 0x04};
环境列表采用字符指针数组来存储所有的环境变量,使用全局变量char** environ来记录环境表的首地址,使用NULL 来代表环境表的结束。所以访问环境表则需要借助environ变量。
//示例一 #include <stdio.h> int main(void) { //声明全局变量 extern char** environ; //给环境表首地址指定替身 char** p = environ; while(*p != NULL) { printf("%s\n",*p); //指向下一个 p++; } return 0; } 功能等同指令env,可自行尝试
//示例二 #include <stdio.h> #include <string.h> int main(void) { //声明全局变量 extern char** environ; //给环境表首地址指定替身 char** p = environ; //练习:查找名字为SHELL的环境变量,获取SHELL的值存到buf的字符数组中,然后进行打印 char buf[20] = {0}; p = environ; while(*p != NULL) { //比较前6个字符是否相等 if(!strncmp(*p,"SHELL=",6)) { //跳过环境变量名= strcpy(buf,*p+6); break; } //比较下一个 p++; } printf("SHELL=%s\n",buf); return 0; } 输出结果: SHELL=/bin/bash 功能等同指令echo $SHELL,可自行尝试
5、环境变量函数
(1)getenv函数
#include <stdlib.h>
char *getenv (const char *name);
函数功能:
表示根据参数指定的环境变量名去环境表进行查找
返回该环境变量所对应的环境值,查找失败则返回NULL.
#include <stdio.h> #include <stdlib.h> int main (void) { char *pc = getenv ("SHELL"); if (NULL == pc) perror ("getenv"), exit (-1); printf ("SHELL = %s\n", pc); return 0; } 输出结果: SHELL = /bin/bash 使用echo指令 # echo $SHELL /bin/bash
(2)setenv函数
#include <stdlib.h>
int setenv (const char *name, const char *value, int overwrite);
第一个参数:环境变量名
第二个参数:环境变量值
第三个参数:是否修改
函数功能:
改变或增加环境变量,如果参数指定的环境变量不存在则增加。如果存在并且 overwrite 是非0,则修改环境名变量值,否则环境变量不变
成功返回 0,失败返回 -1
#include <stdio.h> #include <stdlib.h> int main (void) { char *pc = getenv ("SHELL"); if (NULL == pc) perror ("getenv"), exit (-1); printf ("SHELL = %s\n", pc); setenv ("SHELL", "ABC", 1); printf ("修改后的SHELL = %s\n", getenv ("SHELL")); return 0; } 输出结果: SHELL = /bin/bash 修改后的SHELL = ABC
(3)putenv函数
#include <stdlib.h>
int putenv (char *string);
函数功能:
表示按照参数的内容增加/修改环境变量,其中string的格式为:name=value,如果不存在则添加,存在则删除
成功返回0,失败返回-1
#include <stdio.h> #include <stdlib.h> int main (void) { char *pc = getenv ("SHELL"); if (NULL == pc) perror ("getenv"), exit (-1); printf ("SHELL = %s\n", pc); int res = putenv ("SHELL=abc"); if (res) perror("putenv"),exit(-1); printf ("修改后的SHELL = %s\n", getenv ("SHELL")); putenv("CSDN=666"); printf("增加的环境值是:%s\n",getenv("CSDN"));//123 return 0; } 输出结果: SHELL = /bin/bash 修改后的SHELL = abc 增加的环境值是:666
(4)unsetenv函数
#include <stdlib.h>
int unsetenv (const char *name);
函数功能:
表示根据参数指定的环境变量去环境表中进行删除
成功返回0,失败返回-1
#include <stdio.h> #include <stdlib.h> int main (void) { putenv("CSDN=666"); printf("增加的环境值是:%s\n",getenv("CSDN"));//123 unsetenv("CSDN"); printf("删除的结果是:%s\n",getenv("CSDN")); return 0; } 输出结果: 增加的环境值是:666 删除的结果是:(null)
(5)clearenv函数
#include <stdlib.h>
int clearenv (void);
函数功能:
表示清空整个环境表
成功返回0,失败返回-1
#include <stdio.h> #include <stdlib.h> int main (void) { int res; putenv("CSDN=666"); printf("增加的环境值是:%s\n",getenv("CSDN"));//123 res = clearenv (); if(res) perror("clearenv"), exit(-1); printf("清空整个环境表结束\n"); printf("CSDN = %s\n",getenv("CSDN")); return 0; } 输出结果: 增加的环境值是:666 清空整个环境表结束 CSDN = (null)
6、主函数的原型
我们之前将函数的时候提到过,参看:C语言再学习 -- 函数
main函数原型:
int main (int argc, char * argv[], char * envp[]) {....}
第一个参数:命令行参数的个数
第二个参数:命令行参数的地址信息
第三个参数:环境表的首地址
参数argc表示输入参数的个数(含命令名),如果有命令行参数,argc应不小于1;argv表示传入的参数的字符串,是一个字符串数组,argv[0]表示命令名,argv[1]指向第一个命令行参数;至于第三个参数env,它与全局变量environ相比也没有带来更多益处,所以POSIX.1也规定应使用environ而不使用第三个参数。通常用getenv和putenv函数来存取特定的环境变量,而不是直接使用environ变量。例如:
//main函数原型的使用 #include <stdio.h> int main(int argc,char* argv[],char* envp[]) { printf("argc=%d\n",argc); int i=0; for(i=0;i<argc;i++) { printf("感谢%s\n",argv[i]); } //声明全局变量 extern char** environ; printf("environ=%p,envp=%p\n",environ,envp);//直接访问环境表 return 0; } 输出结果为: argc=1 感谢./a.out environ=0xbfab74cc,envp=0xbfab74cc