为什么 char** argv 与 char* argv[] 相同



所以我读到在编译器转动的函数中传递数组时在幕后

int myArray(int arr[])

int myArray(int *arr)

此外,数组大多数时候会衰减到指针,例如

arr[0]

(arr +  0)

(如果我错了,请纠正我)

但是当涉及到char *argv它变得混乱时,char *argv[]转换为字符串数组。

例如:

argv[2] = "Hello"
argv[3] = "World"

但是**argv怎么和*argv[]一样,因为**argv是指向指针的指针,**argv怎么能包含 10 个不同的值 因为它是指向指针的指针? 我想我误解了什么。

此外,数组大多数时候会衰减到指针,例如 arr[0] 与 (arr + 0) 相同

arr[0]的评估就像*( arr + 0 )一样,与*arr相同。

具有数组类型的函数参数由编译器调整为指向数组元素类型的指针。

另一方面,用作参数表达式的数组被隐式转换为指向其第一个元素的指针。

例如这些函数声明

void f( char * s[100] );
void f( char * s[10] );
void f( char * s[] );

等效并声明与

void f( char **s );

为了清楚起见,只需引入一个 typedef 名称。例如

typedef char *T;

那么你有

void f( T s[] );

所以函数参数由编译器调整

void f( T *s );

现在将 typedef 别名更改为其原始类型,您将获得

void f( char * *s );

请注意,指针s不知道数组用作函数参数的元素数量。

因此,例如函数 main 被声明为

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

也就是说,它还有一个参数argc,允许确定传递给函数的字符串数组中的元素数量。虽然如果要告诉 main,那么一般来说参数argc是多余的,因为字符串数组始终包含哨兵值NULL.也就是说argv[argc]等于NULL

但一般来说,您还必须传递数组中用作函数参数的元素数量。

但是**argv和*argv[]有什么相同,因为**argv是指向指针的指针

因为,引用 n1570 6.7.6.3p7(强调我的):

将参数声明为"类型数组">

应调整为">限定指向类型的指针",其中类型限定符(如果有)是在数组类型派生的 [ 和 ] 中指定的限定符。如果关键字 static 也出现在数组类型派生的 [ 和 ] 中,则对于对函数的每次调用,相应的实际参数的值应提供对数组第一个元素的访问,该数组的第一个元素至少具有与 size 表达式指定的元素一样多的元素。

char *argv[]的每个元素都有类型char *,它是指向char的指针。

因此,根据 6.7.6.3p7,char *数组将调整为指向char *的指针;即char *argv[](指向chars 的指针数组)将调整为char **argv(指向指向char的指针)。

**argv 如何包含 10 个不同的值,因为它是指向指针的指针?

因为它没有。仅仅因为它是指向指针的指针,并不能使它与任何其他指针不同(好吧,除了它们的大小、表示形式和对齐要求可能不同)。

下图可能会帮助您了解实际情况:

argv (points to the beginning of the array of pointer to chars.)
------------------+---------+---------+
             |         |         |
 argv[0]    |argv[1]  |argv[2]  | argv[3]
           |         |         |
          |         |         |
V  char * V  char * V char *  V char *
+---------+---------+---------+---------+
|  0xf00  |  0xf0C  |  0xf13  |   NULL  |   (0xf0C and 0xf13 are the addresses of the first element of the strings passed as parameters to your program.)
+---------+---------+---------+---------+
|           |        |
|           |        |
|           |        |
V           V        V
"my_        "hello"  "world"            ("hello" and "world" are the parameters passed to your program.)
program"

"**" 仅指指向指针的指针。数组本身用作指针。因此,我们可以说 int **argv == *argv[] = argv[][0]。

char *argv[]转换为字符串数组

如果您考虑此代码段

char const *arr[] = {"one", "two", "three"};

arr声明为指向char的指针数组,并使用初始值设定项列表进行初始化

。C 标准(参见C11 草案 6.7.6.2 数组声明符1)说(强调我的):

1除了可选的类型限定符和关键字 static 之外,[ 和 ] 还可以分隔表达式或 *。如果它们分隔表达式(指定数组的大小),则表达式应具有整数类型。如果表达式是常量表达式,则其值应大于零。元素类型不应是不完整的或函数类型.
[...]
4如果大小不存在,则数组类型不完整

稍后,在6.7.9初始化2

8初始值设定项指定存储在对象中的初始值.
[...]
22如果初始化了未知大小的数组,则其大小由具有显式初始值设定项的最大索引元素确定。数组类型在其初始值设定项列表的末尾完成。


问题是关于argv,但是,main函数的第二个函数参数

正如 JASLP 的回答中所指出的,我们需要看看6.7.6.3 函数声明符 3

6参数类型列表指定函数参数的类型,并可以声明函数参数的标识符.
7参数声明为"类型数组"应调整为"限定指向类型的指针">,其中类型限定符(如果有)是在数组类型派生的 [ 和 ] 中指定的那些。如果关键字 static 也出现在数组类型派生的 [ 和 ] 中,则对于对函数的每次调用,相应的实际参数的值应提供对数组第一个元素的访问,该数组的第一个元素至少具有与 size 表达式指定的元素一样多的元素。

<小时 />

1) http://port70.net/%7Ensz/c/c11/n1570.html#6.7.6.2

2) http://port70.net/%7Ensz/c/c11/n1570.html#6.7.9

3)http://port70.net/%7Ensz/c/c11/n1570.html#6.7.6.3

C 语言中的数组和指针有着非常强的关系。通常,指针是包含其他一些变量地址的变量,对于数组,指针存储数组的起始地址。数组名称本身充当指向数组第一个元素的指针,如果指针变量存储数组的基址,那么我们可以仅使用指针变量操作所有数组元素。

C:https://www.geeksforgeeks.org/difference-pointer-array-c/中指针和数组的区别

最新更新