關於main函數的參數

歷史上大多數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 環境變量

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章