I have been programming c/c++ for many years, but todays accidental discovery made me somewhat curious... Why does both outputs produce the same result in the code below? (arr
is of course the address of arr[0]
, i.e. a pointer to arr[0]
. I would have expected &arr
to be the adress of that pointer, but it has the same value as arr
)
多年来我一直在编程c/c++,但今天偶然的发现让我有点好奇……为什么两个输出在下面的代码中产生相同的结果?(arr当然是arr[0]的地址,即指向arr[0]的指针。我本以为&arr是那个指针的地址,但它的值和arr是一样的)
int arr[3];
cout << arr << endl;
cout << &arr << endl;
Remark: This question was closed, but now it is opened again. (Thanks ?)
注:这个问题已经结束了,但现在又重新开始了。(谢谢)
I know that &arr[0]
and arr
evaluates to the same number, but that is not my question! The question is why &arr
and arr
evaluates to the same number. If arr
is a literal (not stored anyware), then the compiler should complain and say that arr
is not an lvalue. If the address of the arr
is stored somewhere then &arr
should give me the address of that location. (but this is not the case)
我知道&arr[0]和arr的计算结果是相同的,但那不是我的问题!问题是为什么&arr和arr的值是相同的。如果arr是一个文字(不存储任何软件),那么编译器应该抱怨说arr不是一个lvalue。如果arr的地址被存储在某处,那么arr应该给我那个位置的地址。(但事实并非如此)
if I write
如果我写
const int* arr2 = arr;
const int* arr2 = arr;
then arr2[i]==arr[i]
for any integer i
, but &arr2 != arr
.
那么arr2[i]= arr[i]对于任何整数i,但是&arr2 != arr。
6 个解决方案
#1
13
They're not the same. They just are at the same memory location. For example, you can write arr+2
to get the address of arr[2]
, but not (&arr)+2
to do the same.
他们是不一样的。它们只是在相同的内存位置。例如,您可以编写arr+2来获取arr[2]的地址,但不能使用(&arr)+2。
Also, sizeof arr
and sizeof &arr
are different.
而且,arr的尺寸和sizeof &arr是不同的。
#2
17
#include <cassert>
struct foo {
int x;
int y;
};
int main() {
foo f;
void* a = &f.x;
void* b = &f;
assert(a == b);
}
For the same reason the two addresses a
and b
above are the same. The address of an object is the same as the address of its first member (Their types however, are different).
出于同样的原因,上面的两个地址a和b是相同的。对象的地址与它的第一个成员的地址相同(但是它们的类型不同)。
arr
_______^_______
/ \
| [0] [1] [2] |
--------------------+-----+-----+-----+--------------------------
some memory | | | | more memory
--------------------+-----+-----+-----+--------------------------
^
|
the pointers point here
As you can see in this diagram, the first element of the array is at the same address as the array itself.
如图所示,数组的第一个元素与数组本身的地址相同。
#3
6
The two have the same value but different types.
这两个值相同,但类型不同。
When it's used by itself (not the operand of &
or sizeof
), arr
evaluates to a pointer to int
holding the address of the first int
in the array. &arr
evaluates to a pointer to array of three int
s, holding the address of the array. Since the first int
in the array has to be at the very beginning of the array, those addresses must be equal.
当它自己使用时(不是&或sizeof的操作数),arr计算为一个指向int的指针,该指针包含数组中第一个int的地址。&arr计算一个指针,指向三个ints的数组,包含数组的地址。因为数组中的第一个int数必须在数组的开头,所以这些地址必须是相等的。
The difference between the two becomes apparent if you do some math on the results:
如果你对结果做一些计算,两者之间的差别就变得明显了:
arr+1
will be equal to arr + sizeof(int)
.
arr+1等于arr+ sizeof(int)
((&arr) + 1)
will be equal to arr + sizeof(arr)
== arr + sizeof(int) * 3
(&arr) + 1) = arr + sizeof(arr) == arr + sizeof(int) * 3。
Edit: As to how/why this happens, the answer is fairly simple: because the standard says so. In particular, it says (§6.3.2.1/3):
编辑:至于发生这种情况的原因,答案相当简单:因为标准是这么说的。特别是,它说(§6.3.2.1/3):
Except when it is the operand of the sizeof 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.
除非sizeof运算符的操作数或一元&操作符,或者是一个字符串文字用来初始化一个数组,一个表达式的类型数组类型的”转化为一种表达“指针式”型指向数组的初始元素对象并不是一个左值。
[note: this particular quote is from the C99 standard, but I believe there's equivalent language in all versions of both the C and C++ standards].
[注意:这个特别的引用来自C99标准,但是我相信在C和c++标准的所有版本中都有相同的语言]。
In the first case (arr
by itself), arr
is not being used as the operand of sizeof, unary &, etc., so it is converted (not promoted) to the type "pointer to type" (in this case, "pointer to int").
在第一种情况(arr本身)中,arr不被用作sizeof、unary &等的操作数,因此它被转换为类型“指针指向类型”(在本例中,是指向int的指针)。
In the second case (&arr
), the name obviously is being used as the operand of the unary &
operator -- so that conversion does not take place.
在第二种情况(&arr)中,名称显然被用作一元和运算符的操作数——因此转换不会发生。
#4
5
The address is the same but both expressions are different. They just start at the same memory location. The types of both expressions are different.
地址相同,但两个表达式都不同。它们只是从相同的内存位置开始。这两个表达式的类型都不同。
The value of arr
is of type int *
and the value of &arr
is of type int (*)[3]
.
arr的值为int *类型,而arr的值为int(*)[3]。
&
is the address operator and the address of an object is a pointer to that object. The pointer to an object of type int [3]
is of type int (*)[3]
&是地址操作符,对象的地址是指向该对象的指针。指向int[3]类型对象的指针是int(*)[3]类型
#5
4
They are not the same.
他们不一样。
A bit more strict explanation:
更严格的解释是:
arr
is an lvalue of type int [3]
. An attempt to use arr
in some expressions like cout << arr
will result in lvalue-to-rvalue conversion which, as there are no rvalues of array type, will convert it to an rvalue of type int *
and with the value equal to &arr[0]
. This is what you can display.
arr是类型为int[3]的lvalue。尝试在一些表达式中使用arr,如cout < arr,将导致lvalue-to-rvalue的转换,因为没有数组类型的rvalue,因此会将其转换为int *类型的rvalue,值为&arr[0]。这是你可以显示的。
&arr
is an rvalue of type int (*)[3]
, pointing to the array object itself. No magic here :-) This pointer points to the same address as &arr[0]
because the array object and its first member start in the exact same place in the memory. That's why you have the same result when printing them.
arr是类型int(*)[3]的一个rvalue,指向数组对象本身。这个指针指向与&arr[0]相同的地址,因为数组对象和它的第一个成员从内存中相同的位置开始。这就是为什么打印出来的结果是一样的。
An easy way to confirm that they are different is comparing *(arr)
and *(&arr)
: the first is an lvalue of type int
and the second is an lvalue of type int[3]
.
确认它们不同的一个简单方法是比较*(arr)和*(&arr):第一个是int类型的lvalue,第二个是int类型[3]的lvalue。
#6
2
Pointers and arrays can often be treated identically, but there are differences. A pointer does have a memory location, so you can take the address of a pointer. But an array has nothing pointing to it, at runtime. So taking the address of an array is, to the compiler, syntactically defined to be the same as the address of the first element. Which makes sense, reading that sentence aloud.
指针和数组通常可以被处理为相同的,但是它们是不同的。指针确实有一个内存位置,所以你可以取指针的地址。但是在运行时,数组没有指向它的任何东西。因此,对编译器来说,取数组的地址,语法上定义为与第一个元素的地址相同。大声读这句话是有道理的。
#1
13
They're not the same. They just are at the same memory location. For example, you can write arr+2
to get the address of arr[2]
, but not (&arr)+2
to do the same.
他们是不一样的。它们只是在相同的内存位置。例如,您可以编写arr+2来获取arr[2]的地址,但不能使用(&arr)+2。
Also, sizeof arr
and sizeof &arr
are different.
而且,arr的尺寸和sizeof &arr是不同的。
#2
17
#include <cassert>
struct foo {
int x;
int y;
};
int main() {
foo f;
void* a = &f.x;
void* b = &f;
assert(a == b);
}
For the same reason the two addresses a
and b
above are the same. The address of an object is the same as the address of its first member (Their types however, are different).
出于同样的原因,上面的两个地址a和b是相同的。对象的地址与它的第一个成员的地址相同(但是它们的类型不同)。
arr
_______^_______
/ \
| [0] [1] [2] |
--------------------+-----+-----+-----+--------------------------
some memory | | | | more memory
--------------------+-----+-----+-----+--------------------------
^
|
the pointers point here
As you can see in this diagram, the first element of the array is at the same address as the array itself.
如图所示,数组的第一个元素与数组本身的地址相同。
#3
6
The two have the same value but different types.
这两个值相同,但类型不同。
When it's used by itself (not the operand of &
or sizeof
), arr
evaluates to a pointer to int
holding the address of the first int
in the array. &arr
evaluates to a pointer to array of three int
s, holding the address of the array. Since the first int
in the array has to be at the very beginning of the array, those addresses must be equal.
当它自己使用时(不是&或sizeof的操作数),arr计算为一个指向int的指针,该指针包含数组中第一个int的地址。&arr计算一个指针,指向三个ints的数组,包含数组的地址。因为数组中的第一个int数必须在数组的开头,所以这些地址必须是相等的。
The difference between the two becomes apparent if you do some math on the results:
如果你对结果做一些计算,两者之间的差别就变得明显了:
arr+1
will be equal to arr + sizeof(int)
.
arr+1等于arr+ sizeof(int)
((&arr) + 1)
will be equal to arr + sizeof(arr)
== arr + sizeof(int) * 3
(&arr) + 1) = arr + sizeof(arr) == arr + sizeof(int) * 3。
Edit: As to how/why this happens, the answer is fairly simple: because the standard says so. In particular, it says (§6.3.2.1/3):
编辑:至于发生这种情况的原因,答案相当简单:因为标准是这么说的。特别是,它说(§6.3.2.1/3):
Except when it is the operand of the sizeof 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.
除非sizeof运算符的操作数或一元&操作符,或者是一个字符串文字用来初始化一个数组,一个表达式的类型数组类型的”转化为一种表达“指针式”型指向数组的初始元素对象并不是一个左值。
[note: this particular quote is from the C99 standard, but I believe there's equivalent language in all versions of both the C and C++ standards].
[注意:这个特别的引用来自C99标准,但是我相信在C和c++标准的所有版本中都有相同的语言]。
In the first case (arr
by itself), arr
is not being used as the operand of sizeof, unary &, etc., so it is converted (not promoted) to the type "pointer to type" (in this case, "pointer to int").
在第一种情况(arr本身)中,arr不被用作sizeof、unary &等的操作数,因此它被转换为类型“指针指向类型”(在本例中,是指向int的指针)。
In the second case (&arr
), the name obviously is being used as the operand of the unary &
operator -- so that conversion does not take place.
在第二种情况(&arr)中,名称显然被用作一元和运算符的操作数——因此转换不会发生。
#4
5
The address is the same but both expressions are different. They just start at the same memory location. The types of both expressions are different.
地址相同,但两个表达式都不同。它们只是从相同的内存位置开始。这两个表达式的类型都不同。
The value of arr
is of type int *
and the value of &arr
is of type int (*)[3]
.
arr的值为int *类型,而arr的值为int(*)[3]。
&
is the address operator and the address of an object is a pointer to that object. The pointer to an object of type int [3]
is of type int (*)[3]
&是地址操作符,对象的地址是指向该对象的指针。指向int[3]类型对象的指针是int(*)[3]类型
#5
4
They are not the same.
他们不一样。
A bit more strict explanation:
更严格的解释是:
arr
is an lvalue of type int [3]
. An attempt to use arr
in some expressions like cout << arr
will result in lvalue-to-rvalue conversion which, as there are no rvalues of array type, will convert it to an rvalue of type int *
and with the value equal to &arr[0]
. This is what you can display.
arr是类型为int[3]的lvalue。尝试在一些表达式中使用arr,如cout < arr,将导致lvalue-to-rvalue的转换,因为没有数组类型的rvalue,因此会将其转换为int *类型的rvalue,值为&arr[0]。这是你可以显示的。
&arr
is an rvalue of type int (*)[3]
, pointing to the array object itself. No magic here :-) This pointer points to the same address as &arr[0]
because the array object and its first member start in the exact same place in the memory. That's why you have the same result when printing them.
arr是类型int(*)[3]的一个rvalue,指向数组对象本身。这个指针指向与&arr[0]相同的地址,因为数组对象和它的第一个成员从内存中相同的位置开始。这就是为什么打印出来的结果是一样的。
An easy way to confirm that they are different is comparing *(arr)
and *(&arr)
: the first is an lvalue of type int
and the second is an lvalue of type int[3]
.
确认它们不同的一个简单方法是比较*(arr)和*(&arr):第一个是int类型的lvalue,第二个是int类型[3]的lvalue。
#6
2
Pointers and arrays can often be treated identically, but there are differences. A pointer does have a memory location, so you can take the address of a pointer. But an array has nothing pointing to it, at runtime. So taking the address of an array is, to the compiler, syntactically defined to be the same as the address of the first element. Which makes sense, reading that sentence aloud.
指针和数组通常可以被处理为相同的,但是它们是不同的。指针确实有一个内存位置,所以你可以取指针的地址。但是在运行时,数组没有指向它的任何东西。因此,对编译器来说,取数组的地址,语法上定义为与第一个元素的地址相同。大声读这句话是有道理的。