寻找关于指针使用差异的一些确认

Looking for some confirmations on pointer usage differences

我目前在大学学习编程 2。我们即将进行一项测试,只是想就我放在一起的一些事情寻求一些确认。考虑以下代码:

//CASE 1

int Awesome = 10;        //Line 1
int *P = &Awesome;       //Line 2

cout << Awesome << endl; //Line 3
cout << *P << endl;      //Line 4

//CASE 2

int X[3]={0,0,5};        //Line 1
int *A = X;              //Line 2

cout << X[2] << endl;    //Line 3
cout << A[2] << endl;    //Line 4

这是我的问题所在,但我想我有点 "stumbled" 我的问题的答案,但我想确保它是正确的。

在第 2 行中,我声明了指针 P 并使用引用运算符为其指定了 Awesome 的地址。在第二行 2 中,我声明了指针 A 并赋予它地址 X。我一直不明白为什么在将数组地址赋予指针的情况下我们不使用 & 运算符。想了想,我记得听说数组名只是一个常量指针,包含数组第一个下标的地址。如果那是正确的,那么为什么我们不需要使用 & 来获取数组的地址就有意义了,因为数组名称本身已经包含了地址。这是正确的吗?

我的第二个问题。在第 4 行的第一行,我使用解引用运算符 cout-ing 指针 P 指向的内容,但在第 4 行的第二行,我不需要使用*运算符获取值。在我的脑海中,我不确定为什么会有所不同,但我认为上一段中的概念使它更加清晰。数组名本身和指针基本上是一回事(我认为除了数组名是一个常量指针)。数组名是一个保存地址的常量指针,指针正在获取该地址。因此,我们使用新指针以相同的方式保存新地址。它与第一个实例完全不同。由于数组名和指针基本相同,我们不需要使用特殊的 * 运算符,只需像使用数组符号的普通数组一样使用它。这是正确的吗?

我一直在努力理解为什么上述两个概念不同,为什么在一个实例中 (int *P = &Address) 我们需要使用 & 运算符来给出地址以及为什么在另一个实例中(int *A=X) 我们没有。我一直在纠结为什么我们在一个实例 (cout << *P;) 中使用 * 而在另一个实例 (cout << A[2];) 中不使用。这些差异让我很困惑,但如果我的逻辑几乎是正确的,那么现在就完全有道理了。如果完全错误,谁能确认或更正?

你对数组的理解是正确的。数组变量可以视为指针。当你声明

int x[8];
int* y;

那么xy都兼容int*类型。主要区别是x指向栈上的一个地址范围,容量足够存放8个整数,而y没有初始化

此外,*xx[0] 是两种相同的解引用方式。

*x == x[0]
*(x+n) == x[n]

C/C++ 中数组和指针之间有一个重要区别。

给定:

int a[10];
int *p = a;

sizeof(a) 将 return 10*sizeof(int)(可能 == 40 或 80)。 sizeof(p) 将 return sizeof(int*)(可能 == 4 或 8)。

数组取消引用也适用于 C/C++ 中的指针。所以你可以写 p[1]*(p + 1).

如果有帮助还可以写:

int *p = &a[0];

这在 C++ 中特别有用,可用于编写适用于 std::vectorstd::array、普通 C/C++ 数组或任何其他以连续方式存储其元素的容器的模板代码内存块并提供​​数组接口。

编辑:我还要补充一点,由于别名,编译器应该能够比指针取消引用更好地优化数组访问。

数组和指针是同一类型不是形式上正确的。

这是使用重载函数的证明。第一个重载通过引用获取数组,第二个重载通过引用获取指针。它表明 int[8]int* 是不同的类型。

#include <iostream>

void f(int (&x) [8]) {
    std::cout << "array type\n";
}

void f(int*& y) {
    std::cout << "pointer type\n";
}

int main()
{
    int x[8];
    int* y;

    f(x);
    f(y);
}

事实上,数组可以很容易地转换为指向其第一个元素的指针。当初学者尝试按值将数组传递给函数时,通常会发生这种情况。必须有一个问题,为什么这在 Whosebug 不是每 2 或 3 天就起作用。这种转换意味着,例如,函数 void f(int x[8])void f(int* x) 将彼此相同(并且编译器会抱怨重新定义)。最重要的是,这当然意味着作为数组类型一部分的大小信息 ([8]) 会丢失。

这种转换 非正式地称为 "decay",它也发生在赋值或初始化时,如下所示:

int X[3]={0,0,5};        //Line 1
int *A = X;              //Line 2

这并不意味着 int[3]int* 相同,就像 intdouble 不相同只是因为您可以写 my_double = my_int; .

If thats correct, then it would make sense why we wouldn't need to use the & to obtain the address of the array, because the array name itself already contains the address. Is this correct?

没有。你需要用&来获取数组的地址。您不需要它来获取包含在其第一个元素中的值。

在你的例子中:

int X[3]={0,0,5};        //Line 1
int *A = X;              //Line 2

std::cout << &X; 会给你本地数组对象的地址。

Since an array name and a pointer are basically the same, we don't need to use the special * operator, just use it like a normal array with array notation. Is this correct?

好吧,这取决于你对狡猾的词的真正含义 "basically"。

毕竟,下标运算符只是指针运算的语法糖[*]t[n] 根据定义等于 *(t + n)(具有讽刺意味的是,它等同于 *(n + t),因此等同于 n[t])。

int X[3]={0,0,5};        //Line 1
int *A = X;              //Line 2

cout << X[2] << endl;    //Line 3
cout << A[2] << endl;    //Line 4

在第 3 行中,您执行 *(X + 2)。没关系。 X "decays" 指向指向其第一个元素的指针,然后将 2 添加到该指针。指针加法的结果被取消引用。

在第 4 行中,您执行 *(A + 2)。那也没关系。唯一的区别是没有 "decay" 发生; A已经是指针类型了。


顺便说一下...在您的问题和评论中,您一直提到 "array name itself is just holding an address"。这意味着比您想象的要少。毕竟,它只是陈述了一个非常简单的事实,即变量的名称表示一个存在于内存中的对象,因此必须有一个地址。你可以这样说一个简单的 int 变量。例如,在 int x = 123; 中,可以说 "the int name itself is just holding an address"。这在某种程度上是正确的,但我认为对于试图理解数组和指针的人来说,这不是一块有用的拼图。


[*] 这当然证明语法糖对于编写可维护的代码非常重要! :)