Linux C编程:环境变量与库链接实战
1. 环境变量的使用
环境变量是与shell进行通信以及配置程序的一种方式。默认情况下,系统已经设置了许多环境变量,这些变量包含了关于用户和设置的各种信息,例如用户名、使用的终端类型、之前提到的路径变量、首选编辑器、首选区域和语言等。了解如何读取这些变量,能让程序更好地适应用户环境。
1.1 准备工作
编写读取环境变量的程序,几乎可以使用任何shell,还需要GCC编译器。
1.2 具体步骤
- 编写代码 :将以下代码保存为
env-var.c文件,也可从 链接 下载完整程序。该程序使用getenv()函数读取shell中的一些常见环境变量,奇怪的数字序列(\033[0;31)用于给输出着色。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
/* Using getenv() to fetch env. variables */
printf("Your username is %s\n", getenv("USER"));
printf("Your home directory is %s\n", getenv("HOME"));
printf("Your preferred editor is %s\n", getenv("EDITOR"));
printf("Your shell is %s\n", getenv("SHELL"));
/* Check if the current terminal support colors*/
if ( strstr(getenv("TERM"), "256color") )
{
/* Color the output with \033 + colorcode */
printf("\033[0;31mYour \033[0;32mterminal "
"\033[0;35msupport "
"\033[0;33mcolors\033[0m\n");
}
else
{
printf("Your terminal doesn't support colors\n");
}
return 0;
}
- 编译程序 :使用GCC编译程序。
$> gcc env-var.c -o env-var
- 运行程序 :运行编译后的程序,输出的信息会因用户而异。如果终端支持颜色,最后一行会显示彩色信息;否则,会提示终端不支持颜色。
$> ./env-var
Your username is jake
Your home directory is /home/jake
Your preferred editor is vim
Your shell is /bin/bash
Your terminal support colors
- 查看环境变量 :使用
echo命令查看使用的环境变量,注意$符号表示要打印变量的值,而不是变量名。
$> echo $USER
jake
$> echo $HOME
/home/jake
$> echo $EDITOR
vim
$> echo $SHELL
/bin/bash
$> echo $TERM
screen-256color
- 更改环境变量 :将
$TERM变量更改为不支持颜色的xterm,程序会输出不同的结果。
$> export TERM=xterm
$> ./env-var
Your username is jake
Your home directory is /home/jake
Your preferred editor is vim
Your shell is /bin/bash
Your terminal doesn't support colors
- 恢复环境变量 :在继续操作之前,将
$TERM变量恢复到之前的值。
$> export TERM=screen-256color
- 临时设置环境变量 :可以在运行程序时临时设置环境变量,程序结束后,变量恢复为原来的值。
$> echo $TERM
xterm-256color
$> TERM=xterm ./env-var
Your username is jake
Your home directory is /home/jake
Your preferred editor is vim
Your shell is /bin/bash
Your terminal doesn't support colors
$> echo $TERM
xterm-256colo
- 查看所有环境变量 :使用
env命令可以打印出所有环境变量的完整列表,这些变量都可以使用C语言的getenv()函数访问。
$> env
1.3 工作原理
程序使用 getenv() 函数从shell的环境变量中获取值,并将这些变量打印到屏幕上。在程序末尾,通过 strstr() 函数检查 $TERM 变量是否包含 256color 子字符串,以判断当前终端是否支持颜色。
所有这些变量都是shell的环境变量,可以使用 echo 命令打印,也可以在shell中设置自己的环境变量,例如 export FULLNAME=Jack-Benny ,还可以通过覆盖现有变量来更改它们。使用 FULLNAME=Jack-Benny 语法设置的普通变量仅在当前shell中可用,称为局部变量;使用 export 命令设置的变量成为全局变量或环境变量,可用于子shell和子进程。
1.4 更多操作
在C程序中,可以使用 setenv() 函数更改环境变量或创建新的环境变量,但这些变量在启动程序的shell中不可用,因为程序是shell的子进程,无法更改父进程的变量。以下是一个使用 setenv() 的简单示例:
#define _POSIX_C_SOURCE 200112L
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
setenv("FULLNAME", "Jack-Benny", 1);
printf("Your full name is %s\n", getenv("FULLNAME"));
return 0;
}
编译并运行该程序后,尝试从shell中读取 $FULLNAME 变量,会发现它并不存在。
$> gcc env-var-set.c -o env-var-set
$> ./env-var-set
Your full name is Jack-Benny
$> echo $FULLNAME
2. 使用GCC进行库链接
学会将程序链接到外部库,包括系统级安装的库和位于主目录的库,能让我们利用各种现成的函数,节省开发时间。
2.1 准备工作
需要Make工具和GCC编译器,最好通过元包或组安装方式安装。所有源代码可在 链接 获取。
2.2 链接数学库
编写一个计算银行账户复利的小程序,需要使用数学库中的 pow() 函数。
1. 编写代码 :将以下代码保存为 interest.c 文件。
#include <stdio.h>
#include <math.h>
int main(void)
{
int years = 15; /* The number of years you will keep the money in the bank account */
int savings = 99000; /* The inital amount */
float interest = 1.5; /* The interest in % */
printf("The total savings after %d years is %.2f\n", years, savings * pow(1+(interest/100), years));
return 0;
}
- 编译链接程序 :使用
-l选项链接数学库,库名为m。
$> gcc -lm interest.c -o interest
- 运行程序 :
$> ./interest
The total savings after 15 years is 123772.95
2.3 创建自己的库
创建一个用于判断数字是否为质数的共享库。
1. 创建头文件 :将以下内容保存为 prime.h 文件。
int isprime(long int number);
- 编写函数代码 :将以下代码保存为
prime.c文件。
int isprime(long int number)
{
long int j;
int prime = 1;
/* Test if the number is divisible, starting from 2 */
for(j=2; j<number; j++)
{
/* Use the modulo operator to test if the number is evenly divisible, i.e., a prime number */
if(number%j == 0)
{
prime = 0;
}
}
if(prime == 1)
{
return 1;
}
else
{
return 0;
}
}
- 编译为目标文件 :使用以下命令将
prime.c编译为prime.o目标文件,需要生成位置无关代码(PIC)。
$> gcc -Wall -Wextra -pedantic -fPIC -c prime.c
$> ls -l prime.o
-rw-r--r-- 1 jake jake 1296 nov 28 19:18 prime.o
- 打包为共享库 :使用
-shared选项创建共享库,-Wl,-soname,libprime.so指定共享库名,-o指定输出文件名。
$> gcc -shared -Wl,-soname,libprime.so -o libprime.so prime.o
2.4 链接主目录中的库
使用刚刚创建的 libprime.so 库编写一个判断数字是否为质数的程序。
1. 编写代码 :将以下代码保存为 is-it-a-prime.c 文件。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "prime.h"
int main(int argc, char *argv[])
{
long int num;
/* Only one argument is accepted */
if (argc != 2)
{
fprintf(stderr, "Usage: %s number\n", argv[0]);
return 1;
}
/* Only numbers 0-9 are accepted */
if ( strspn(argv[1], "0123456789") != strlen(argv[1]) )
{
fprintf(stderr, "Only numeric values are accepted\n");
return 1;
}
num = atol(argv[1]); /* String to long */
if (isprime(num)) /* Check if num is a prime */
{
printf("%ld is a prime\n", num);
}
else
{
printf("%ld is not a prime\n", num);
}
return 0;
}
- 编译链接程序 :使用
-L选项指定库的路径,-l选项指定库名。
$> gcc -L${PWD} -lprime is-it-a-prime.c -o is-it-a-prime
- 设置环境变量 :设置
$LD_LIBRARY_PATH环境变量,以便程序能找到动态链接的库。
$> export LD_LIBRARY_PATH=${PWD}:${LD_LIBRARY_PATH}
- 运行程序 :测试不同的数字,判断其是否为质数。
$> ./is-it-a-prime 11
11 is a prime
$> ./is-it-a-prime 13
13 is a prime
$> ./is-it-a-prime 15
15 is not a prime
$> ./is-it-a-prime 1000024073
1000024073 is a prime
$> ./is-it-a-prime 1000024075
1000024075 is not a prime
使用 ldd 程序可以查看程序依赖的库。
$> ldd is-it-a-prime
linux-vdso.so.1 (0x00007ffc3c9f2000)
libprime.so => /home/jake/libprime.so (0x00007fd8b1e48000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fd8b1c4c000)
/lib64/ld-linux-x86-64.so.2 (0x00007fd8b1e54000)
2.5 工作原理
pow() 函数需要链接数学库 libm.so ,由于该库位于系统默认库位置,使用 -l 选项指定库名 m 即可。数学库与标准C库 libc.so 是分开的,标准C库默认会被链接。
在 is-it-a-prime.c 程序中,需要包含 prime.h 头文件,实际的 isprime() 函数包含在 libprime.so 库中。当链接不在系统默认位置的库时,需要使用 -L 选项指定库的路径,并设置 $LD_LIBRARY_PATH 环境变量,以便程序能找到库。
以下是环境变量操作和库链接操作的流程总结:
|操作类型|步骤|
| ---- | ---- |
|环境变量操作|编写代码 -> 编译代码 -> 运行程序 -> 查看环境变量 -> 更改环境变量 -> 恢复环境变量 -> 临时设置环境变量 -> 查看所有环境变量|
|库链接操作|链接数学库(编写代码 -> 编译链接 -> 运行程序) -> 创建自己的库(创建头文件 -> 编写函数代码 -> 编译为目标文件 -> 打包为共享库) -> 链接主目录中的库(编写代码 -> 编译链接 -> 设置环境变量 -> 运行程序)|
mermaid流程图如下:
graph LR
classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px
classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px
A([开始]):::startend --> B(环境变量操作):::process
B --> B1(编写代码):::process
B1 --> B2(编译代码):::process
B2 --> B3(运行程序):::process
B3 --> B4(查看环境变量):::process
B4 --> B5(更改环境变量):::process
B5 --> B6(恢复环境变量):::process
B6 --> B7(临时设置环境变量):::process
B7 --> B8(查看所有环境变量):::process
A --> C(库链接操作):::process
C --> C1(链接数学库):::process
C1 --> C11(编写代码):::process
C11 --> C12(编译链接):::process
C12 --> C13(运行程序):::process
C --> C2(创建自己的库):::process
C2 --> C21(创建头文件):::process
C21 --> C22(编写函数代码):::process
C22 --> C23(编译为目标文件):::process
C23 --> C24(打包为共享库):::process
C --> C3(链接主目录中的库):::process
C3 --> C31(编写代码):::process
C31 --> C32(编译链接):::process
C32 --> C33(设置环境变量):::process
C33 --> C34(运行程序):::process
B8 --> D([结束]):::startend
C13 --> D
C24 --> D
C34 --> D
Linux C编程:环境变量与库链接实战
3. 环境变量与库链接的应用场景及注意事项
3.1 环境变量的应用场景
- 个性化配置 :通过读取环境变量,程序可以根据用户的偏好进行个性化设置。例如,根据
$EDITOR变量确定用户首选的编辑器,方便在程序中调用该编辑器进行文件编辑。 - 系统兼容性 :不同的系统或终端可能有不同的特性,通过检查环境变量可以让程序更好地适应这些差异。如检查
$TERM变量判断终端是否支持颜色,从而决定是否输出彩色信息。
3.2 库链接的应用场景
- 代码复用 :使用现有的库可以避免重复编写代码,提高开发效率。例如,使用数学库中的
pow()函数来计算复利,而不需要自己实现幂运算。 - 模块化开发 :将不同的功能封装在不同的库中,便于代码的管理和维护。如创建一个判断质数的库,在需要判断质数的程序中直接链接该库使用。
3.3 注意事项
- 环境变量的作用域 :使用
export命令设置的环境变量是全局的,可在子shell和子进程中使用;而普通变量仅在当前shell中可用。在编写程序时,要清楚变量的作用域,避免出现变量未定义的错误。 - 库的路径问题 :当链接不在系统默认位置的库时,需要使用
-L选项指定库的路径,并设置$LD_LIBRARY_PATH环境变量。如果路径设置不正确,程序运行时会找不到库,导致错误。
4. 常见问题及解决方案
4.1 环境变量相关问题
- 变量未定义 :如果在程序中使用
getenv()函数获取环境变量的值时返回NULL,可能是该变量未设置。可以在shell中使用export命令设置该变量,或者在程序中进行错误处理。 - 变量作用域问题 :如前所述,普通变量和环境变量的作用域不同。如果需要在子进程中使用某个变量,要确保使用
export命令将其设置为环境变量。
4.2 库链接相关问题
- 找不到库 :如果在编译或运行程序时出现找不到库的错误,可能是库的路径设置不正确。检查
-L选项指定的路径和$LD_LIBRARY_PATH环境变量是否正确。 - 版本不兼容 :不同版本的库可能有不同的接口和功能。如果程序链接的库版本与开发时使用的版本不一致,可能会导致程序运行出错。在使用库时,要确保使用的库版本兼容。
以下是常见问题及解决方案的总结表格:
|问题类型|问题描述|解决方案|
| ---- | ---- | ---- |
|环境变量问题|变量未定义|在shell中使用 export 命令设置变量,或在程序中进行错误处理|
|环境变量问题|变量作用域问题|使用 export 命令将变量设置为环境变量|
|库链接问题|找不到库|检查 -L 选项指定的路径和 $LD_LIBRARY_PATH 环境变量是否正确|
|库链接问题|版本不兼容|确保使用的库版本兼容|
mermaid流程图如下:
graph LR
classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px
classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px
classDef decision fill:#FFF6CC,stroke:#FFBC52,stroke-width:2px
A([开始]):::startend --> B{遇到问题?}:::decision
B -->|是| C{问题类型}:::decision
C -->|环境变量问题| D(变量未定义?):::decision
D -->|是| E(在shell中设置变量或程序中错误处理):::process
D -->|否| F(变量作用域问题?):::decision
F -->|是| G(使用export设置为环境变量):::process
C -->|库链接问题| H(找不到库?):::decision
H -->|是| I(检查路径和环境变量):::process
H -->|否| J(版本不兼容?):::decision
J -->|是| K(确保库版本兼容):::process
B -->|否| L([结束]):::startend
E --> L
G --> L
I --> L
K --> L
5. 总结
通过本文的介绍,我们学习了如何在Linux环境下使用C语言处理环境变量和进行库链接。环境变量可以让程序更好地适应用户环境,而库链接则可以提高代码的复用性和开发效率。在实际开发中,要注意环境变量的作用域和库的路径设置,遇到问题时可以根据常见问题及解决方案进行排查和解决。
希望本文对你在Linux C编程方面有所帮助,让你能够更加熟练地使用环境变量和库链接,开发出更加高效、稳定的程序。
1038



被折叠的 条评论
为什么被折叠?



