所以我读到在编译器转动的函数中传递数组时在幕后
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[]
(指向char
s 的指针数组)将调整为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/中指针和数组的区别