目录
[1.main 函数可以带参数吗?能带几个参数呢?](#1.main 函数可以带参数吗?能带几个参数呢?)
[1、通过 main 的第三个参数获取环境变量](#1、通过 main 的第三个参数获取环境变量)
[2、通过全局变量 environ 获取环境变量](#2、通过全局变量 environ 获取环境变量)
一、环境变量的基本概念
在我们学习JAVA,Python我们都要在windows上配置环境变量。
环境变量的概念
- 环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数如:我们在编写C/C++代码的时候,在链接的时候,从来不知道我们的所链接的动态静态库在哪里,但是照样可以链接成功,生成可执行程序,原因就是有相关环境变量帮助编译器进行查找。
- 环境变量通常具有某些特殊用途,还有在系统当中通常具有全局特性
1.常见环境变量
- PATH : 指定命令的搜索路径
- HOME : 指定用户的主工作目录(即用户登陆到Linux系统中时,默认的目录)
- SHELL : 当前Shell,它的值通常是/bin/bash。
2.查看环境变量方法
echo $NAME //NAME:你的环境变量名称
3.几个环境变量
环境变量:PATH
我们先先创建一个文件:test.c,并写入代码。
cpp
#include <stdio.h>
int main()
{
printf("打印\n");
return 0;
}
我们生成的可执行程序是不是一条命令呢?
是一条命令,每一个可执行文件都具有(X)执行权限。
像我们常说的命令,指令,可执行程序 都是同一个概念。
那竟然mytest是一条命令,那为什么会出现无法找到mytest这个指令呢?而是要./mytest呢?
我们上面说PATH环境变量是指令的搜索路径
这些都是绝对路径,如果在这些路径下找不到该指令的话,就会报错!
如果我们想直接用mytest让程序运行起来该怎么办呢?
我们可以将该可执行程序的路径添加到环境变量PATH中
这样我们就可以直接用mytest运行了。
环境变量:HOME
分别在 root 和普通用户下执行 cd ~ 和 pwd 查看家目录,分别是 /root 和 /home/xyl,为什么得到的结果不一样呢?
因为不同用户的家目录中的配置文件不一样,所以不同用户下的环境变量 HOME 也是不一样的。
环境变量:SHELL
- 我们在命令行上运行的大部分命令,它们的父进程都是 bash。
- bash 创建子进程,然后由子进程执行用户输入的命令。
二、和环境变量相关的命令
1. echo: 显示某个环境变量值
2. export: 设置一个新的环境变量
3. env: 显示所有环境变量
4. unset: 清除环境变量
5. set: 显示本地定义的shell变量和环境变量
三、库函数getenv,setenv
getenv:char *getenv(const char *name); 获取环境变量。
setenv:int setenv(const char *name, const char *value, int overwrite);更改或添加环境变量。
getenv() 和setenv()
cpp
1 #include <stdio.h>
2 #include <stdlib.h>
3 int main()
4 {
5 printf("%s\n",getenv("PATH"));
6 char* env;
7 env=getenv("HOME");
8 printf("HOME:%s\n",getenv("HOME"));
9 setenv("HOME","haha",1);
10
11 printf("HOME:%s\n",getenv("HOME"));
12 return 0;
13 }
setenv()用来改变或增加环境变量的内容。参数name为环境变量名称字符串。参数 value则为变量内容,参数overwrite用来决定是否要改变已存在的环境变量。如果没有此环境变量则无论overwrite为何值均添加此环境变量。若环境变量存在,当overwrite不为0时,原内容会被改为参数value所指的变量内容;当overwrite为0时,则参数value会被忽略。返回值 执行成功则返回0,有错误发生时返回-1。
四、环境变量和本地变量
上面我们说环境变量具有全局属性,也就是说环境变量可以被子进程继承。
cpp
1 #include <stdio.h>
2 #include <stdlib.h>
3 int main()
4 {
5 char* env=getenv("MYENV");
6 if(env)
7 {
8 printf("%s",env);
9 }
10 return 0;
11 }
运行发现没有结果,因为我自己创建的MYENV不是环境变量。
当我们用export导出环境变量时,发现程序运行并打印了。
这说明环境变量是可以被子进程继承下去的。
- 本地变量(只能在当前 shell 命令行解释器内被访问,不可以被子进程继承)
这说明本地变量不能被子进程继承
我们该如何查看本地变量呢?
其实和环境变量是一样的。
上面说到,我们在命令行上运行的大部分命令,都是 bash 创建子进程来执行的,而本地变量不能被子进程继承,那为什么使用 echo 命令,却可以访问本地变量呢?
因为echo是一个内建命令,是shell程序内部的一个函数,可以直接访问shell内定义的本地变量,这并没有fork创建子进程。
五、命令行参数
main函数,我们很少见到像这样的类型。
cpp
int main(int argc, char* argv[])。
1.main 函数可以带参数吗?能带几个参数呢?
main 函数可以带参,但大部分都是缺省。
- argc:命令行参数的个数。
- argv:字符指针数组(指向各个命令行参数的字符指针所构成的数组)。
cpp
int main(int argc, char* argv[]) // 接收命令行参数
{
for (int i = 0; i < argc; i++)
{
printf("argv[%d]: %s\n", i, argv[i]); // 遍历字符指针数组argv
}
return 0;
}
运行结果:字符数组中只有一个元素,就是我们输入的命令。
如果我们多输入几个参数
实际上我们输入的命令行参数,就是一个个的 C 字符串: "./proc"、"arg1"、"arg2"、"arg3",传给了 main 函数:
2.命令行参数的意义
为什么要存在命令行参数呢?
帮助我们能够给同一个程序,设计出不同的业务功能。
举个小例子,比如我想要实现这样一个计算器:
- 如果输入 ./cal,则会提示该程序的正确用法:Usage:./cal -[a|s] x y;
- 输入 ./cal -a 1 2,cal 程序可以输出 1 + 2 的结果;
- 输入 ./cal -s 4 2,cal 程序可以输出 4 - 2 的结果。
cpp
#include <stdio.h>
#include <stdlib.h> // atoi -- 函数原型:int atoi(const char *nptr); // 将C字符串转换成整数
#include <string.h> // strcmp
// cal命令的用法手册
void Usage(const char* cal)
{
printf("Usage: %s -[a|s] x y\n", cal);
}
int main(int argc, char* argv[]) // 接收命令行参数
{
// 输入的参数个数不为4
if (argc != 4)
{
Usage(argv[0]);
return 1; // 退出程序
}
// 保存第3个和第4个参数
int x = atoi(argv[2]);
int y = atoi(argv[3]);
// 根据不同参数,执行不同功能,然后输出结果
if (strcmp(argv[1], "-a") == 0)
{
printf("%d + %d = %d\n", x, y, x + y);
}
else if (strcmp(argv[1], "-s") == 0)
{
printf("%d - %d = %d\n", x, y, x - y);
}
else
{
Usage(argv[0]);
return 1; // 退出程序
}
return 0;
}
命令行参数可以让同一个命令,通过带上不同的选项表现出不同的功能和作用。
比如:ls -l、ls -l -a、ls -l -a -i。这就是命令行参数的意义。
总结:我们运行的进程,都是子进程,bash本身在启动时,会从操作系统的配置文件读取环境变量信息,子进程会继承父进程的环境变量
可以通过 main 函数的参数,可以传递命令行参数和环境变量
六、环境变量的组织方式
main 函数除了可以传递两个和命令行参数相关的参数 argc 和 argv 以外,还可以传递第 3 个参数 env:
cpp
int main(int argc, char* argv[], char* env[]);
这也是 main 函数获取环境变量的方式。
通过给 main 函数第三个参数传参,把一个个环境变量传递给当前程序,当前程序运行起来变成进程,就意味着当前这个进程获取到了这些环境变量。
每个被 bash 创建的子进程都会接收到一张环境表,环境表是一个字符指针数组,每个指针指向一个以 '\0' 结尾的环境字符串(环境变量)。
1、通过 main 的第三个参数获取环境变量
cpp
// proc.c
#include <stdio.h>
#include <string.h>
int main(int argc, char* argv[], char* env[]) // 通过第三个参数接收环境变量
{
for (int i = 0; env[i]; i++) // 循环结束条件为env[i],遍历到NULL停止
{
printf("env[%d]: %s\n", i, env[i]); // 遍历字符指针数组env
}
return 0;
}
我们获取了bash进程的所有环境变量,因为子进程mytest继承了bash进程的环境变量。
2、通过全局变量 environ 获取环境变量
C/C++ 提供了一个全局二级指针变量 char** environ,指向存放环境变量地址的字符指针数组 char* env[ ]。
cpp
#include <stdio.h>
int main()
{
extern char** environ;
for (int i = 0; environ[i]; i++)
{
printf("%s\n", environ[i]); // 等价于 *(environ + i)
}
return 0;
}
因为 libc 中定义的全局变量 environ 指向环境变量表,environ 没有包含在任何头文件中,所以在使用时要用 extern 声明。