数组是指针? [重复]
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数组是指针? [重复]相关的知识,希望对你有一定的参考价值。
数组和指针在C和C ++中的实现方式是否不同?我遇到过这个问题,因为在这两种情况下我们都从元素的起始地址访问元素。所以,他们之间应该有密切的关系。请解释它们之间的确切关系。谢谢。
让我们首先得到重要的东西:数组不是指针。数组类型和指针类型是完全不同的东西,编译器会对它们进行不同的处理。
出现混淆的地方是C如何处理数组表达式。 N1570:
6.3.2.1 Lvalues, arrays, and function designators
...
3 Except when it is the operand of thesizeof
operator, the_Alignof
operator, or the unary&
operator, or is a string literal used to initialize an array, an expression that has type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points to the initial element of the array object and is not an lvalue. If the array object has register storage class, the behavior is undefined.
我们来看看以下声明:
int arr[10] = {0,1,2,3,4,5,6,7,8,9};
int *parr = arr;
arr
是一个10元素的int
阵列;它指的是一个连续的内存块,足以存储10个int
值。第二个声明中的表达式arr
是数组类型,但由于它不是&
或sizeof
的操作数而且它不是字符串文字,表达式的类型变为“指向int
的指针”,值为地址第一个元素,或&arr[0]
。
parr
是指向int的指针;它指的是一块足以容纳单个int
对象地址的内存块。如上所述,它被初始化为指向arr
中的第一个元素。
这是一个假设的存储器映射,显示了两者之间的关系(假设16位整数和32位地址):
Object Address 0x00 0x01 0x02 0x03 ------ ------- ---------------------- arr 0x10008000 0x00 0x00 0x00 0x01 0x10008004 0x00 0x02 0x00 0x03 0x10008008 0x00 0x04 0x00 0x05 0x1000800c 0x00 0x06 0x00 0x07 0x10008010 0x00 0x08 0x00 0x09 parr 0x10008014 0x10 0x00 0x80 0x00
这些类型对于像sizeof
和&
这样的东西很重要; sizeof arr == 10 * sizeof (int)
,在这种情况下是20,而sizeof parr == sizeof (int *)
,在这种情况下是4.同样,表达式&arr
的类型是int (*)[10]
,或指向int
的10元素阵列,而&parr
的类型是int **
,或指向int
指针的指针。
请注意,表达式arr
和&arr
将产生相同的值(arr
中第一个元素的地址),但表达式的类型不同(分别为int *
和int (*)[10]
)。这在使用指针运算时有所不同。例如,给定:
int arr[10] = {0,1,2,3,4,5,6,7,8,9};
int *p = arr;
int (*ap)[10] = &arr;
printf("before: arr = %p, p = %p, ap = %p
", (void *) arr, (void *) p, (void *) ap);
p++;
ap++;
printf("after: arr = %p, p = %p, ap = %p
", (void *) arr, (void *) p, (void *) ap);
“之前”行应该为所有三个表达式打印相同的值(在我们的假设图中,0x10008000
)。 “后”行应显示三个不同的值:0x10008000
,0x10008002
(base plus sizeof (int)
)和0x10008014
(base plus sizeof (int [10])
)。
现在让我们回到上面的第二段:在大多数情况下,数组表达式转换为指针类型。让我们看一下下标表达式arr[i]
。由于表达式arr
没有出现为sizeof
或&
的操作数,并且因为它不是用于初始化另一个数组的字符串文字,所以它的类型从“10个元素的int
数组”转换为“指向int
的指针” ,并将下标操作应用于此指针值。实际上,当您查看C语言定义时,您会看到以下语言:
6.5.2.1 Array subscripting
...
2 A postfix expression followed by an expression in square brackets [] is a subscripted designation of an element of an array object. The definition of the subscript operator [] is that E1[E2] is identical to (*((E1)+(E2))). Because of the conversion rules that apply to the binary + operator, if E1 is an array object (equivalently, a pointer to the initial element of an array object) and E2 is an integer, E1[E2] designates the E2-th element of E1 (counting from zero).
实际上,这意味着您可以将下标运算符应用于指针对象,就像它是一个数组一样。这就像代码一样
int foo(int *p, size_t size)
{
int sum = 0;
int i;
for (i = 0; i < size; i++)
{
sum += p[i];
}
return sum;
}
int main(void)
{
int arr[10] = {0,1,2,3,4,5,6,7,8,9};
int result = foo(arr, sizeof arr / sizeof arr[0]);
...
}
它的工作方式。 main
正在处理一个int
数组,而foo
正在处理指向int
的指针,但两者都能够使用下标运算符,就好像它们都处理数组类型一样。
这也意味着数组下标是可交换的:假设a
是一个数组表达式而i
是一个整数表达式,a[i]
和i[a]
都是有效的表达式,并且两者都将产生相同的值。
不了解C ++。对于C来说,c-faq的回答比以往任何时候都要好得多。
来自c-faq的小片段:
6.3那么C中“指针和数组的等价”是什么意思?
[...]
具体而言,等价的基石是这个关键定义:
对表达式中出现的array-of-T类型的对象的引用将(有三个例外)衰减为指向其第一个元素的指针;结果指针的类型是指向T的指针。
[...]
在C ++中根据C ++标准4.2:
可以将“N T数组”或“T的未知数组的数组”类型的左值或右值转换为“指向T的指针”的右值。结果是指向数组的第一个元素的指针。
不,它们的实现方式不同。两者都找到具有相同计算的元素:a[i]
位于地址a + i*sizeof(a[0])
,p[i]
位于地址p + i*sizeof(p[0])
。
但是,类型系统对它们的处理方式不同。 C ++在数组上有输入信息,可以通过sizeof operator
(如C),模板推理,函数重载,RTTI等查看。基本上语言中使用类型信息的任何地方,指针和数组都可能表现不同。
C ++中有许多例子,其中两种不同的语言概念具有相同的实现。只是几个:数组与指针,指针与引用,虚函数与函数指针,迭代器与指针,for循环与while循环,异常vs longjmp
在每种情况下,都有不同的语法和不同的思考方式,但最终会产生相同的机器代码。
在C ++中(我认为在C中),数组不是指针,可以通过以下方式证明。
#include <iostream>
int main()
{
char arr[1000];
std::cout << sizeof arr;
}
如果arr是一个指针,这个程序将打印sizeof(char *),通常为4.但它打印1000。
另一个证据:
template <class T>
void f(T& obj)
{
T x = obj; //this will fail to compile if T is an array type
}
int main()
{
int a[30] = {};
int* p = 0;
f(p); //OK
f(a); //results in compile error. Remember f takes by ref therefore needs lvalue and no conversion applies
}
形式上,一个数组被转换为指向lvalue-to-rvalue转换中第一个元素的指针,也就是说,当一个rvalue被预期时,在一个上下文中给出一个数组类型的左值,该数组被转换为指向它的第一个元件。
此外,声明为按值获取数组的函数等效于获取指针的函数,即
void f(int a[]);
void f(int a[10]);
void f(int* a);
是三个等同的声明。 HTH
数组和指针之间最大的混淆点来自于K&R决定将声明为数组类型的函数参数表现为就像它们被声明为指针一样。声明
void foo(int a[]);and
void foo(int *a);are equivalent, as is (so far as I can tell)
void foo(int a[5]);though I'm not positive a compiler would be required to accept a reference to a[6] within the latter function. In other contexts, an array declaration allocates space for the indicated number of elements. Note that given:
typedef int foo[1];
类型foo的任何声明都将为一个元素分配空间,但任何将foo作为函数参数传递的尝试都将传递该地址。我在研究va_list实现时学到了一些有用的技巧。
在C ++中,数组类型有一个“大小属性”,所以对于
T a[10];
T b[20];
a和b有不同的类型。
这允许使用这样的代码
template<typename T, size_t N>
void foo(T (&a)[N])
{
...
}
以上是关于数组是指针? [重复]的主要内容,如果未能解决你的问题,请参考以下文章