extern char ** environ和extern char * environ []之间有什么区别

时间:2017-10-19 13:08:19

标签: c linux

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

extern char *environ[];
int main(int argc, char *argv[])
{
    int index = 0;
    char **env = environ;
    printf("Environment variables:\n");
    index = 0;
    while (env[index])
    {
        printf("envp[%d]: %s\n", index, env[index]);
        ++index;
    }
    return 0;
}

输出:

Environment variables:  
envp[0]: GH#þ 

我想要打印所有环境,但它无法正常工作 我将extern char *environ[]更改为extern char **environ,它可以打印所有环境 更改代码输出后:

Environment variables:
envp[0]: XDG_SESSION_ID=8
envp[1]: TERM=xterm
envp[2]: SHELL=/bin/bash
envp[3]: SSH_CLIENT=192.168.1.224 1085 22
envp[4]: SSH_TTY=/dev/pts/0
...

4 个答案:

答案 0 :(得分:5)

我发现这些东西无法在不考虑记忆中的内容以及它是如何组织的情况下遵循。

内存中的某个地方是一堆包含环境字符串的数据块 - &#34; foo = bar&#34;,&#34; hello = world&#34;等等。让我们为了论证而假设&#34; foo = bar&#34;在地址100,&#34;你好=世界&#34;地址是200。

内存中的其他位置是另一个数据块,它列出了这些数据块的地址(通常后跟零,所以我们知道列表实际结束的位置,如果我们事先不知道大小}。所以这个块包含数据

{100,200,0}

如果我将此数据定义为char *env[],这意味着内存中有一个名为env的位置,并且该位置是实际数据块{100,200,0}。也就是说,env本身的数据将是数字100,它是其中一个环境字符串的地址。 env之后的下一个位置将包含200,然后是0之后的下一个位置(好的,我简化了一点)。

如果我将数据块{100,200,0}定义为char **,则意味着内存中有一个名为env的位置,其中包含数据块的地址{1​​00,200,0} 。存储在env的数据是&#34; 100&#34; (字符串的地址)。它将是一个指示数据块开始的地址{1​​00,200,0}。

在C程序中,环境实际上是char **,也就是说,environ之类的东西不是字符串地址列表的开头,它是地址字符串地址列表。要查看它,您可以错误地定义它,然后更正它,就像在原始代码的这个修改版本中一样:

extern char *environ[];
int main(int argc, char *argv[])
{
    int index = 0;
    char **env = (char **)environ[0];
    printf("Environment variables:\n");
    index = 0;
    while (env[index])
    {
        printf("envp[%d]: %s\n", index, env[index]);
        ++index;
    }
    return 0;
}

使用char *environ[]告诉编译器(错误地)名为environ的位置开始一个指向字符串的指针列表,从那一点开始在内存中一个接一个地排列。实际上,只有确切位置environ的数据才有意义。我们可以将该数据视为environ[0],然后将其转换为实际数据类型,即char **

由于C不允许将数组传递给函数,因此char **char *[]之间的差异会受到侵蚀,从而导致混淆。函数的所有参数都是单个数字 - 可以是整数或浮点数的基元,也可以是某个地址。如果尝试传递数组(即数据块),则会传递块开头的地址。这意味着在大多数代码中,您实际上可以使用char **char *[],就好像它们是相同的一样。你会遇到问题 - 就像在这个问题中一样 - 当数据以某种方式排列在内存中时,除非程序员通过使用正确的类型声明告诉它,否则编译器无法解决这种问题。

答案 1 :(得分:4)

作为全局声明extern char *environ[];声明了一个指向char的指针数组,这是一个与extern char **environ;非常不同的野兽,声明指向char的指针。< / p>

可能令人困惑的是,两个语法可以互换地用于函数参数,以声明指向char的指针,如下所示:

int main(int argc, char *argv[]) {

int main(int argc, char **argv) {

一些程序员更喜欢第一种语法来强调argv指向字符串数组而不仅仅是单个字符串的事实。另请注意,也可以指定元素计数,但编译器将完全忽略这些元素:

int main(int argc, char *argv[2]) { // 2 is ignored

答案 2 :(得分:2)

Extern实际上提供了对所有程序文件可见的全局变量的引用。

指向指针的指针,它保留函数调用之外的赋值或内存分配。第一个指针用于存储第二个指针的地址,因为它被称为双指针。

  1. extern char **environ:表示环境列表。(换句话说,双指针)。
  2. extern char *environ[]:表示指针数组。

答案 3 :(得分:1)

char **environ是指向地址的指针,该地址是char的指针。 char *environ[]是指向char的指针数组。

这是一个例子: char array[] = "foo" =&gt; array[1]将获得array的地址并在之后移动1个字符 char *pointer = "bar" =&gt; pointer[1]将获取 pointer指向的地址,并在

后移动1个字符

所以指针和数组密切相关,但不同。

检查http://c-faq.com/aryptr/index.html。对指针有一个非常完整的解释,远不止这个小答案。