int (*arr)[5]
表示 arr
是一个指向包含5个整数的数组的指针。那么这个指针到底是什么意思呢?
如果我声明 int arr[5]
,并且 arr
是指向第一个元素的指针,那么它是否与上面的例子相同?
这两个例子中的 arr
是否相同?如果不是,那么什么是指向数组的指针?
首先介绍一些理论(你可以跳到“答案”部分,但我建议你也阅读一下这部分):
int arr[5]
sizeof
的能力。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
它们都是指针,但区别在于它们的类型。
arr
与&arr[0]
完全等价,除了在应用sizeof
时的情况。然后我发现当按照这种方式思考时,存在许多令人讨厌的不一致和奇怪的行为。我喜欢这个答案!+1 - Eric在运行时,指针无论它所指向的内容如何,都只是“一个指针”,不同之处在于语义上的区别;指向数组的指针与指向元素的指针在编译器中传达的意义不同。
当使用指向数组的指针时,您指向的是一个指定大小的数组 - 编译器将确保您只能指向该大小的数组。
即,此代码将编译:
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个元素的数组。
int* anotherPtr = theArray;
不应该起作用。 - Potatoswatterint* anotherPtr = &theArray[0];
的简写语法;它们的作用完全相同。 - Ben Cottrell数组指针是指向某种类型的数组的指针。该类型包括元素类型和大小。您不能将不同类型的数组分配给它:
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
会衰减为指向其第一个元素的 int*
,使得赋值合法。
指向数组和指向数组第一个元素的指针是不同的。在 int (*arr)[5]
的情况下,arr
是指向 5
个 int
内存块的指针。对 arr
进行解引用将给出整个行。在 int arr[5]
的情况下,arr
衰减为指向第一个元素的指针。对 arr
进行解引用将给出第一个元素。
在两种情况下,起始地址相同,但两个指针的类型不同。
如果我声明
int arr[5]
并且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 */
}
这三个指针都可以定位到matrix[0][0]
中的0
,如果将这些指针转换为“字节地址”并在printf()
中使用%p
指示符打印出来,这三个指针很可能会产生相同的输出(在典型的现代计算机上)。但是,类型为int *
的指针p1
只指向一个单独的int
,如黑色圆圈所示。类型为int (*)[2]
的红色指针p2
指向两个int
,而指向整个矩阵的蓝色指针确实指向整个矩阵。
这些差异影响指针算术和一元*
(间接)运算符的结果。由于p1
指向单个int
,所以p1 + 1
向前移动一个int
。黑色圆圈1只有一个int
那么大,*(p1 + 1)
就是下一个int
,其值为1。同样,sizeof *p1
只是sizeof(int)
(可能是4)。
然而,由于p2
指向整个“int数组2”,所以p2 + 1
将向前移动一个这样的数组。结果将是一个指向绕过{2,3}
对的红色圆圈的指针。由于间接运算符的结果是一个对象,*(p2 + 1)
就是整个数组对象,这可能会受到规则的限制。如果它确实受到规则的限制,那么该对象将变成指向其第一个元素的指针,即当前保存2
的int
。如果它不受规则的限制 - 例如,在sizeof *(p2 + 1)
中,将对象置于对象上下文中 - 它将保持整个数组对象。这意味着sizeof *(p2 + 1)
(当然还有sizeof *p2
)是sizeof(int[2])
(可能是8)。
1以上内容摘自更多有关数组和指针的话题。
int arr[5]
,你会在栈上创建一个包含五个int
的数组。这将占用大小等于五个int大小的空间。int (*arr)[5]
,你会在栈上创建一个指向包含五个int
的数组的指针。这将占用大小等于指针大小的空间。