歷史上大多數UNIX系統支持main函數帶有三個參數:
int main(int argc, char *argv[], char *env[]);
其中第二個參數是參數表的地址,第三個參數就是環境表的地址,它們都是一個字符指針數組,每個指針指向一個以null結束的C字符串。
另外全局變量environ也包含了環境表指針數組的地址:
extern char **environ;
因爲ISO C規定main函數只有兩個參數,所以POSIX.1也規定應使用environ而不使用第三個參數。
下面的例子展示了main函數的參數:
include <stdio.h>
#include <stdlib.h>
#include <elf.h>
#include <assert.h>
extern char **environ;
int main(int argc, char *argv[], char *env[])
{
printf("sizeof pointer: %d\n", sizeof(void*));
assert(argc == *(int*)(argv-1));
printf("Argument count: %d\n", argc);
int i = 0;
for(; i<argc; ++i)
{
printf("Argument %d: %s\n", i, argv[i]);
}
assert(argv[argc] == NULL);
char **p = argv+argc+1;
printf("Environment address: %p %p %p\n", p, env, environ);
printf("Environment:\n");
while(*p)
{
printf(" %s\n", *p);
p++;
}
assert(*p++ == NULL);
printf("Auxiliary vectors:\n");
#ifdef __x86_64__
Elf64_auxv_t *aux = (Elf64_auxv_t*)p;
#else
Elf32_auxv_t *aux = (Elf32_auxv_t*)p;
#endif
while(aux->a_type != AT_NULL)
{
printf(" Type: %2d Value: %x\n", aux->a_type, aux->a_un.a_val);
++aux;
}
return 0;
}
C程序的典型存儲器安排是這樣的:
命令行參數和環境變量 高地址
------------------
棧
------------------
堆
------------------
未初始化的數據
------------------
初始化的數據
------------------
程序正文 低地址
環境表和環境字符串通常佔用的是進程地址空間頂部,它下面是棧空間,所以它不能向上(高地址)又不能向下(低地址)擴展。如果要增加一個環境變量,操作就比較複雜了。
如果是第一次增加一個新name,調用malloc爲新的環境表分配空間,然後將原來的環境表複製過來,並將指向新name=value字符串的指針放到環境表的尾部,再將一個空指針放在其後,最後更新environ使之指向新環境表。由於evnp是值傳遞的,它的值並不會進行更新。
如果不是第一次增加一個新name,只要調用realloc擴展空間,然後將指向新name=value字符串的指針放到環境表的尾部,再將一個空指針放在其後。此時environ的值可能變化也可能不變。
第一次增加新name時,複製原來的環境表這一步可能是通過environ去尋找舊的環境表,所以用戶程序最好不要修改environ的值。
有兩個函數可以增加一個新的環境變量:
#include <stdlib.h>
int putenv(chat *str);
int setenv(const char *name, const char *value, int rewrite);
setenv會分配存儲區,以便存放name=value字符串。putenv則可能直接把傳遞給它的字符串地址放入環境表尾部,如果這字符串是存放在棧中則可能會發生錯誤。
以下例程展示了增加一個環境變量的操作:
#include <stdio.h>
#include <stdlib.h>
extern char **environ;
int main(int argc, char *argv[])
{
printf("environ is %p\n",environ);
char **p = environ;
while(*p) ++p;
if(environ) printf("last element address is %p\n",p[-1]);
char buf[64]={"name=value"};
printf("putenv\nbuf address is p\n",buf);
putenv(buf);
printf("new environ is %p\n",environ);
p = environ;
while(*p) ++p;
if(environ)
{
printf("the one before last address is %p\n",p[-2]);
printf("last element address is %p\n",p[-1]);
}
return 0;
}
參考
程序員的自我修養
UNIX環境高級編程 7.6 C程序的存儲空間佈局
UNIX環境高級編程 7.6 環境變量