目录
获取环境变量的后两种方法
环境变量具有全局属性
内建命令
和环境变量相关的命令
c语言访问地址
重新理解地址
地址空间
获取环境变量的后两种方法
main函数的第三个参数 :char* env[ ]
也是一个指针数组,我们可以把它的内容打印出来看看。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
E>int main(int argc,char* argv,char* env[])
{
int i=0;
for(; env[i];i++)
{
E> printf("env[%d]:%s\n",i,env[i]);
}
return 0;
}
我们再把pid也打印出来看看:
输入命令:
!man getpid
我们把这两个头文件加上
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
E>#include<unistd.h>
E>int main(int argc,char* argv,char* env[])
{
int i=0;
for(; env[i];i++)
{
E> printf("pid:%d,env[%d]:%s\n",getpid(),i,env[i]);
}
return 0;
}
就可以把我们的此程序的环境变量打印出来:
这个获取环境变量的方法和我们输入"env"命令获取的环境变量是一样的:
我们之前说过如果我们把PATH置空,很多系统命令都用不了了:
但是我们重启xshell之后就又可以用了。这是因为我们置空的是加载进内存的PATH。
我们重启之后系统解释器会重新读取环境变量表,形成新的环境变量,环境变量是脚本配置文件的形式存在的的。
在家目录下有个隐藏文件,bash_profile.
环境变量表就在这个文件里面。
假设我们自己写一个环境变量,
然后用env调用环境变量,通过管道只打印我们自己写的环境变量,显示找不到:
这是因为我们写的环境变量并没有被加载进环境变量表。
我们可以通过 "export"命令把我们写的环境变量加载进环境变量表里面,再通过env打印就可以打印出来了。
但是我们重启xshell之后我们配置的这个环境变量仍然会消失不见。只有当我们去家目录下面的bash_profile文件下写入我们的环境变量才能再次重启之后仍然会存在。
vim ~/.bash_profile
此刻我们就可以把我们自定义的变量打印出来看看:
假如我们不想给main函数传参呢?
有个外部变量叫 environ
它指向了char * env[ ]
我们把environ打印出来,main函数不带参照样可以把环境变量打印出来。
int main()
{
extern char** environ;
int i=0;
for(;environ[i];i++)
{
printf("%d,%s",i,environ[i]);
}
return 0;
}
到目前为止,我们获取环境变量的方法有:
main函数传参 getenv[ ] char** environ[ ]
环境变量具有全局属性
我们在我们将才的进程里再写个子进程,看它能获取我们子进程的环境变量吗
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<unistd.h>
int main()
{
pid_t id=fork();
if(id==0)
{
extern char** environ;
int i=0;
for(;environ[i];i++)
{
printf("%d,%s",i,environ[i]);
}
}
sleep(3);
return 0;
}
照样可以,这也证明了环境变量具有全局属性。
我们自定一个本地变量
OUR_ENV=333
然后我们再把这个本地变量打印出来
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<unistd.h>
int main()
{
printf("OUR_ENV_ENV:%S\n",getenv("OUR_ENV"));
return 0;
}
注意:因为是子进程,所以我们没export的时候子进程获取不到我们自定义的本地变量,会显示为null
当我们export把我们定义的环境变量载入bash之后,让它成为环境变量,环境变量具有全局性,子进程就可以获取到了:
内建命令
我们再写几个本地变量:
我们发现,a,b都为本地变量,echo是一个子进程,但是可以直接打印本地变量 a,b,这是为什么?
再比如,我们把PATH置空,这时候ls,touch这种命令都用不了了,但是echo还可以用
这是因为echo是shell的内置函数,这种命令叫做linux的内建命令。
内建命令不创建子进程。
和环境变量相关的命令
set:
把本地变量和环境变量全部打印出来:
set | grep+本地变量名
功能:打印本地变量
c语言访问地址
程序的地址空间遵守的就是下面这张图:
我们把这各个区的存的值的地址打印出来看看:
int usa;
int bbb=100;
int main ()
{
printf("公共代码区:%p\n",main);
const char* str="helo djwd";
//常量区
printf("常量区:%p\n",str);
printf("初始化全局数据区:%p\n",&bbb);
W>printf("未初始化全局数据区:%p\n",usa);
char* heap=(char*)malloc(100);
printf("堆区:%p\n",heap);
printf("栈区:%p\n",&str);
观察打印出来的地址可以发现一个问题,从公共代码区到常量区,再往下走,地址都是呈递增状态。
当到了堆区之后,从堆区到栈区,中间宽度变的特别大。
按照这个结果我们可以有这种推论:
得出结论:堆和栈相对而生。
验证
把堆区地址打印看一下:
char* heap1=(char*)malloc(100);
char* heap2=(char*)malloc(100);
char* heap3=(char*)malloc(100);
char* heap4=(char*)malloc(100);
printf("堆区:%p\n",heap);
printf("堆区:%p\n",heap1);
printf("堆区:%p\n",heap2);
printf("堆区:%p\n",heap3);
printf("堆区:%p\n",heap4);
可以发现堆区越来越大,这也证明堆区向上增长
把栈区地址打印一下看一下:
char* str="hello";
char* heap1=(char*)malloc(100);
char* heap2=(char*)malloc(100);
char* heap3=(char*)malloc(100);
char* heap4=(char*)malloc(100);
printf("栈区:%p\n",&str);
printf("栈区:%p\n",&heap);
printf("栈区:%p\n",&heap1);
printf("栈区:%p\n",&heap2);
printf("栈区:%p\n",&heap3);
printf("栈区:%p\n",&heap4);
观察图我们发现栈区越往下越小
如果定义一个结构体object ,里面有三个成员变量 int a,b,c,abc谁最大?
struct d
{
int a;
int b;
int c;
}object;
printf("%p\n",&object.a);
printf("%p\n",&object.b);
printf("%p\n",&object.c);
很明显,c最大
这是因为虽然栈区向下增长,但整体是向上增长的:
假设我们定义一个 int b,一个int类型有4个字节,那就应该有4个地址,但是我们打印b的地址,显示出来只有一个,这个地址就是最小的地址。
然后向上按照int类型访问4个字节,访问到最大字节:
类型的本质叫做偏移量
c语言中就是以起始位置+偏移量访问任何地址。
定义一个int a,打印a的地址。
a因为是常量,所以在栈区,又因为后开辟,所有地址偏小。
现在我们a重定义为 static int a,此刻再重新打印a。我们发现a的地址变量,它和全局变量的地址是一个样子,也就是此时a就是一个全局变量:
所以我们可以说已初始化全局变量区就是静态区:
在栈区之上还有一个环境变量区和命令参数区:
重新理解地址
地址空间
写一个子进程,写一个父进程
pid_t id=fork();