int (*arr)[5]
表示arr
是一个包含5个整数的指针数组。那究竟是什么指针?
如果我声明int arr[5]
arr
是指向第一个元素的指针,它是否相同?
两个例子中的arr
是否相同?如果没有,那么究竟什么是指向数组的指针?
答案 0 :(得分:8)
首先关闭一些理论(您可以跳到"答案"部分,但我建议您也阅读此内容):
int arr[5]
这是一个阵列" arr"是不指向数组第一个元素的指针。 在特定情况下(即将它们作为左值传递给函数)它们会衰减成指针:您将失去在其上调用sizeof
的能力。
在正常情况下,数组是一个数组,指针是一个指针,它们是两个完全不同的东西。
当处理衰减的指针和指向你所编写的数组的指针时,它们的行为完全一样,但有一个警告:T类型的数组可以衰变为类型为T的指针,但只有一次(或者一层深的)。新创建的腐朽类型不能再进一步腐烂。
这意味着像
这样的二维数组int array1[2][2] = {{0, 1}, {2, 3}};
无法传递给
void function1(int **a);
因为它意味着两级衰变并且不允许(你丢失了数组元素的布局)。以下内容将起作用:
void function1(int a[][2]);
void function1(int a[2][2]);
如果将一维数组作为左值传递给函数,则可以将其衰减为一个简单的指针,在这种情况下,可以像使用任何其他指针一样使用它。
回答你的问题:
int (*arr)[5]
这是一个指向数组的指针,您可以将"视为一个由5个整数组成的数组"作为它的类型,即你不能用它来指向3个整数的数组。
int arr[5]
这是一个数组,除了将它作为左值传递
外,它总是表现为一个数组int* ptrToArr = arr;
在这种情况下,数组会衰减(我引用上面的所有例外情况)并且你得到一个指针,你可以根据需要使用它。
并且:不,他们并不相等,否则会允许这样的事情
int (*arr)[5]
int* ptrToArr = arr; // NOT ALLOWED
Error cannot convert ‘int (*)[5]’ to ‘int*’ in initialization
他们都是指针,但区别在于他们的类型。
答案 1 :(得分:6)
指向数组的指针是指向某种类型数组的指针。类型包括元素的类型以及大小。您无法为其分配不同类型的数组:
int (*arr)[5];
int a[5];
arr = &a; // OK
int b[42];
arr = &b; // ERROR: b is not of type int[5].
指向数组第一个元素的指针可以指向具有正确元素类型的任何数组的开头(事实上,它可以指向数组中的任何元素):
int* arr;
int a[5];
arr = &a[0]; // OK
int b[42];
arr = &b[0]; // OK
arr = &b[9]; // OK
请注意,在C和C ++中,数组会在某些上下文中衰减为指向其元素类型的指针。这就是为什么可以这样做的原因:
int* arr;
int a[5];
arr = a; // OK, a decays to int*, points to &a[0]
此处,arr
(int*
)的类型与a
(int[5]
)的类型不同,但a
衰减为{ {1}}指向其第一个元素,使作业合法化。
答案 2 :(得分:4)
在运行时,无论指向什么指针,指针都是“只是一个指针”,差异是语义指针;指向数组的指针与指向元素的指针
相比,传达了与编译器不同的含义当处理指向数组的指针时,您指向一个指定大小的数组 - 编译器将确保您只能指向该大小的数组。
即。这段代码将编译
int theArray[5];
int (*ptrToArray)[5];
ptrToArray = &theArray; // OK
但这会破裂:
int anotherArray[10];
int (*ptrToArray)[5];
ptrToArray = &anotherArray; // ERROR!
在处理指向元素的指针时,您可以指向内存中具有匹配类型的任何对象。 (它甚至不一定需要在数组中;编译器不会做出任何假设或以任何方式限制你)
即
int theArray[5];
int* ptrToElement = &theArray[0]; // OK - Pointer-to element 0
和..
int anotherArray[10];
int* ptrToElement = &anotherArray[0]; // Also OK!
总之,数据类型int*
并不意味着任何数组知识,但数据类型int (*)[5]
意味着一个数组,它必须包含5个元素。
答案 3 :(得分:2)
整个数组的地址和第一个元素的地址被定义为相同,因为除了组成对象之外,C ++(和C)中的数组没有内部填充。
但是,这些指针的类型是不同的。在您执行某种类型转换之前,将int *
与int (*)[5]
进行比较的是苹果与橙子。
如果您声明arr[5]
,那么arr
不是指向第一个元素的指针。它是数组对象。您可以观察到这一点,因为sizeof( arr )
将等于5 * sizeof (int)
。数组对象隐式地将转换为指向其第一个元素的指针。
指向数组的指针不会隐式转换为任何内容,这可能是造成混淆的另一个原因。
答案 4 :(得分:2)
指向数组的指针和指向数组的第一个元素的指针都是不同的。如果是int (*arr)[5]
,则arr
指向5
int
的内存块。取消引用arr
将给出整行。在int arr[5]
的情况下,arr
衰减指向第一个元素。解除引用arr
将给出第一个元素
在这两种情况下,起始地址相同但指针都是不同类型。
如果我声明
int arr[5]
arr
是指向第一个元素的指针,它是否相同?这两个例子的arr
是一样的吗?如果没有,那究竟什么是指向数组的指针?
没有。要理解这一点,请参阅函数 1 :
的图表void f(void) {
int matrix[4][2] = { {0,1}, {2,3}, {4,5}, {6,7} };
char s[] = "abc";
int i = 123;
int *p1 = &matrix[0][0];
int (*p2)[2] = &matrix[0];
int (*p3)[4][2] = &matrix;
/* code goes here */
}
所有三个指针当然允许您在0
中找到matrix[0][0]
,如果您将这些指针转换为'字节地址'并使用{{打印出来' 1}} %p
中的指令,三者很可能产生相同的输出(在典型的现代计算机上)。但printf()
指针int *
仅指向一个p1
,以黑色圈出。红色指针int
,其类型为p2
,指向两个int (*)[2]
s,蓝色指针 - 指向整个矩阵的指针 - 确实指向整个矩阵。
这些差异会影响指针运算和一元int
(间接)运算符的结果。由于*
指向一个p1
, int
向前移动一个p1 + 1
。黑圈1只有一个int
,而int
只是下一个int,其值为1.同样,*(p1 + 1)
只是sizeof *p1
(可能是4)。
由于sizeof(int)
指向整个'int'数组2', p2
将向前推进一个此类数组。结果将是指向围绕p2 + 1
对的红色圆圈的指针。由于间接运算符的结果是一个对象,{2,3}
是整个数组对象,它可能属于规则。如果确实属于规则,则对象将成为指向其第一个元素的指针,即当前持有*(p2 + 1)
的{{1}}。如果它不属于规则 - 例如,在int
中,它将对象置于对象上下文中 - 它将保留整个数组对象。这意味着2
(当然还有sizeof *(p2 + 1)
)是sizeof *(p2 + 1)
(可能是8)。
1 以上内容取自More Words about Arrays and Pointers。
答案 5 :(得分:0)
如果您编写int arr[5]
,则表示您正在堆栈中创建五个int
的数组。这占用的大小等于五个整数的大小。
如果您编写int (*arr)[5]
,则表示正在为堆栈上的五个int
数组创建指针。这占用的大小等于指针的大小。
如果从上面不清楚,指针与数组有分开存储,并且可以指向任何内容,但是不能将数组名称指定为指向其他内容。
有关详细信息,请参阅我的回答here。