C++指针与数组符号的区别

7
当我像这样声明一个新数组时:

int foo[5]

foo是否真的是指向数组的第一个元素的指针?我能这样做吗:

*(foo+2)

如何访问数组的第三个元素?假设我正在创建一个二维数组:

int foo[3][4]

foo现在是int **类型吗?


11
“foo”是否真的指向数组的第一个元素? - 不是,它是一个数组。“foo”现在是“int**”吗? - 不是,它是一个二维数组。请参阅arrays - chris
3
请看这里:https://dev59.com/8m445IYBdhLWcg3wia2V?lq=1。 - Stefan
@Stefan,那是一个很棒的常见问题解答 - 谢谢你。 - user623990
2
@maxmackie - 你可以使用不同的 语法,但实际类型是有区别的。数组不是指针,指针也不是数组。是的,你可以使用数组/指针的 语法 来访问各个元素,但它们的类型并不相同。 - PaulMcKenzie
在这种情况下,C++与C的工作方式相同:https://dev59.com/THI-5IYBdhLWcg3w18d3#1641963 - Thomas Padron-McCarthy
显示剩余4条评论
2个回答

6
不,'foo'在两种情况下都是数组类型,但当表达式需要指针时,它会被隐式转换为指向数组第一个元素的指针。所有数组都具有这种行为。在此情况下,由于可以通过指针类型进行加法运算,但不能使用数组进行加法运算,因此'foo'被转换为'int *'。
*(foo+2) // 'foo' is implicitly converted into 'int *', pointing to 'foo' first element

foo + 1 //same as above

现在可能会问,'array'类型的属性是什么,为什么我们应该使用它,而不是隐式指向第一个元素的指针。事实是,它们并不多。您可以像这样告诉具有数组类型的对象的大小:

sizeof(foo) //returns the size which array 'foo' occupies 

通过使用'&'操作符来获取它的地址:
&foo // '&foo' has type of 'int (*)[5]'

您还可以创建带有“数组”引用(或指针)类型参数的函数,以便仅接受具有指定大小的数组(如果它们只是指针并且期望传递的数组衰减为这样,则不可能)。例如:

void func(int (&)[5]);

void func1(int (*arg)[5]); // should be accessed by '*arg', allow the use of null-pointers

void func2(int *); //same as the misleading 'void func2(int [5])' or 'void func2(int [6])' etc.

int foo[5];

int foo1[6];

func(foo); // 'foo' type is ('int [5]') - ok

func1(&foo); // '&foo' type is ('int (*)[5]') - ok

func(foo1); // 'foo1' type is ('int [6]') - not allowed, param type is 'int (&)[5]' !

func1(&foo1); // '&foo1' type is ('int (*)[6]') - not allowed, param type is 'int (*)[5]' !

func2(foo); // 'foo' is implicitly converted to 'int *' - ok

func2(foo1); // 'foo1' is implicitly converted to 'int *' - ok

在第二种情况下,当数组是2D时-应用相同的属性。它的声明意味着:'一个由3个元素组成的数组,类型为4个元素的int数组',所以实际上只是一个数组的数组,没有其他东西。它的隐式指针转换并不是'int **'类型,而是'int (*) [4]',因为它的每个元素都是另一个数组。
声明也可以这样写:
int (foo[3])[4];

还需要注意的是,'数组'不能被赋值,因此它们不能按值传递或从函数中返回。我的意思是:

int funcReturningArray()[2]; //not allowed

int funcAcceptingArray(int [2]); //just converted into pointer

int funcAcceptingArray(int *); //same as above

尽管由于历史原因(或其他原因?),数组参数在语法上被接受,但它们的真正含义从未被容忍,它们只是被“调整”为指针。

注意:将数组类型隐式转换为其第一个元素的指针有时称为“数组指针衰减”。


一般来说,答案是正确的,但还有改进的空间:1. 你应该说数组衰变成指针,而不是“它被隐式转换为一个”。单词“转换”通常意味着程序员迫使编译器做一些违背其更好知识的事情,而不是在幕后隐式轻松地更改类型。你还可以通过大幅缩短注释来显著提高大型代码块的可读性。例如://error: wrong type//ok: foo decays into int* - cmaster - reinstate monica
decay是关键字。应该把它放在开头。 - SwiftMango

-3

数组不是指针,但在表达式中它们会被转换为指向其第一个元素的rvalue指针。因此,在这个表达式中

*(foo+2)

首先,foo被转换为rvalue指针,然后进行指针算术运算。
对于这个数组声明,
int foo[3][4];

在表达式中使用的名称foo被转换为类型为int(*)[4]的rvalue指针。

rvalue这个词的意思是例如你不能写++foo

也就是编译器为在表达式中使用的数组名称创建一个临时对象,该对象是指向数组第一个元素的指针。

请注意,如果你使用例如操作符sizeof与数组名称一起使用,则最后一个不会被转换为指针。所以对于你的最后一个数组定义

sizeof( foo )

将等同于

3 * 4 * sizeof( int )

sizeof( int ( * )[4] )

将返回指针本身的大小。

最后,如果您将运算符&应用于数组名称,则会得到指向数组本身的指针。例如

int foo[3][4];

int ( *ptr_to_foo )[3][4] = &foo;

我真的不明白谁会踩这个答案,它完全正确! - cmaster - reinstate monica
编译器在表达式中使用的数组名称会创建一个临时对象,该对象是指向数组第一个元素的指针。语法错误... - Lightness Races in Orbit

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接