当处理C或C++中的指针和数组时,将它们视为非常不同的构造真的有助于理解(我认为最好的解释这种区别的书籍之一是一本叫做“深入C秘密”的书)。让我感到困惑的是,事实上从数组名称到指针允许进行单向无声转换(这是语言处理变量名称的一致性不足)-但是非常重要的是,不要将存在这种衰变现象解释为暗示等价性。
为了帮助我们思考这个问题,让我们引入“内存单元”的概念。我们将“内存单元”建模为具有两个属性:
a) value
b) address
我们可以将一个简单的C++变量建模为具有两个属性(在这个抽象层次上我们不需要类型):
c) name
d) memory cell
像大多数模型一样,它存在一些缺陷(无法处理具有多个元素的数组,但对于我们的目的而言已足够)。
因此例如:
int i = 3;
int *p = &i;
int a[1] = { 4 };
int (*b)[1] = &a;
int *s = &a[0];
int *t = a;
现在,一个数组变量和一个非数组(指针)C++变量之间的主要区别在于:当C++中的变量名被评估时,它总是评估为其内存单元的值,但有一个例外:如果变量名是数组变量,则它会评估为内存单元的地址。以上两行值得再读一遍。
以下是一些示例,以帮助澄清其含义(请参考上述变量):
int k = i; // the 'i' name evaluates to the value of its cell, so 'k' is set to 3
int *q = p; // 'p' evaluates to the value of its cell, so 'q' is set to 0x0A
int *r = a; // 'a' evaluates to the *address* of its cell, so 'r' is set to 0x0C
int (*c)[1] = b; // 'c' is set to 0x0D
这绝不意味着数组变量与指针变量是相同的。它们具有本质上不同的类型,任何试图将它们视为相同的尝试(即在一个转换单元中将变量名定义为数组,在另一个转换单元中将其定义为指针)都会导致糟糕的事情发生。
因此,例如不要这样做:
// myproj_file1.cpp
int array[100] = { 0 }; // 这里 'array' 求值为第一个内存单元的*地址*
// myproj_file2.cpp
extern int* array; // 这里 'array' 求值为第一个内存单元的*值*
// 假设链接器将两个文件链接起来
// 如果你查看汇编代码,它做的事情类似于:
// extern int* array = (int*) array[0];
// 但它可以执行任何操作,因为行为是未定义的
希望这可以帮到你。
如果你仍然觉得需要进一步解释,请提出跟进问题,并且不要犹豫去获取那本“深入C语言秘密”的书籍 :)
--
p.s. 对于大多数本文内容来说,函数类型及其名称和衰减都是无关紧要的
p.s. 我还有意地省略了当数组绑定到引用类型时,不会发生数组到指针的转换