#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 ...
我发现这些东西是不可能的,不考虑记忆中究竟是什么,以及它是如何组织的。
内存中的某处是一堆包含环境字符串的数据块 – “foo = bar”,“hello = world”等等。 假设为了争辩,“foo = bar”在地址100处,“hello = world”在地址200处。
内存中的其他地方是另一个数据块,它列出了这些数据块的地址(通常后面跟着一个零,所以我们知道列表实际上在哪里结束,如果我们不知道大小的话)。数据
{100,200,0}
如果我将这个数据定义为char *env[]
,这意味着在内存中有一个名为env
的地方,并且在那里是实际的数据块{100,200,0}。 也就是说, env
本身的数据将是数字100,这是环境字符串之一的地址。 env
之后的下一个位置将包含200,接下来的位置是0(好的,我简化了一下)。
如果我将数据块{100,200,0}定义为char **
,这意味着在内存中有一个名为env
的地方,其中包含数据块{100,200,0}的地址。 存储在env
的数据不会是“100”(字符串的地址)。 这将是一个地址,表示数据块{100,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 **
。
char **
和char *[]
之间的区别被削弱了,因此混淆了,因为C不允许将数组传递给函数。 函数的所有参数都是一个单一的数字 – 或者像一个整数或者浮点数的原语,或者某个东西的地址。 如果您尝试传递一个数组(即一个数据块),则会传递该块的开始地址。 这意味着在大多数代码中,实际上可以使用char **
和char *[]
,就好像它们是相同的东西。 在这个问题中,当数据以某种方式排列在内存中时,会遇到问题,除非程序员使用正确的类型声明来告诉编译器,否则编译器无法确定这种方式。
作为一个全局声明extern char *environ[];
声明一个指向char
的指针数组,这是一个与extern char **environ;
非常不同的野兽extern char **environ;
声明一个指向char
的指针。
可能令人困惑的是,这两种语法都可以互换使用,用于函数参数来声明指向char
的指针,如下所示:
int main(int argc, char *argv[]) {
和
int main(int argc, char **argv) {
一些程序员更喜欢第一种语法来强调这样一个事实,即argv
指向一个字符串数组,而不仅仅是一个字符串。 还要注意,也可以指定一些元素,但是编译器会完全忽略这些元素:
int main(int argc, char *argv[2]) { // 2 is ignored
Extern实际上给出了所有程序文件都可见的全局变量的引用。
指向指针的指针保留了函数调用之外的赋值或内存分配。 第一个指针用于存储第二个指针的地址,因为它被称为双指针。
extern char **environ
:表示环境列表(也就是双指针)。 extern char *environ[]
:表示指针数组。 char **environ
是一个指向地址的指针,它是一个指向char
的指针。 char *environ[]
是指向char
的指针数组。
下面是一个例子: char array[] = "foo"
=> array[1]
将获得array
的地址,并在char *pointer = "bar"
=> pointer[1]
后移动1个字符将得到pointer
的地址,然后移动1个字符
所以指针和数组密切相关,但不同。
检查http://c-faq.com/aryptr/index.html 。 关于指针有一个非常完整的解释,不仅仅是这个小小的答案。