在处理 C 或 C++ 中的指针和数组时,将它们识别为非常不同的结构确实很有帮助(如果我没记错的话,我认为解释这种区别的最好的书之一是一本名为“Deep C Secrets”的书)。搅浑水的是这样一个事实,即允许从数组名称到指针的单向静默转换(语言对变量名称的处理不一致) - 但重要的是不要将这种衰减现象的存在解释为暗示等价。
为了帮助我们对此进行推理,让我们介绍一下“记忆细胞”的概念。我们将“记忆单元”建模为具有两个属性:
a) value
b) address
然后我们可以将一个简单的 C++ 变量建模为具有两个属性(我们不需要这种低抽象级别的类型):
c) name
d) memory cell
和大多数模型一样,它也有一些不足(不能处理包含多个元素的数组,但对于我们的目的来说已经足够了)。
例如:
// non-array variable: name 'i', and memory cell: value=3, address=0x0A
int i = 3;
// non-array variable: name 'p', and memory cell: value=0x0A, address=0x0B
int *p = &i;
// array variable: name 'a', and memory cell: vale=4, address=0x0C
int a[1] = { 4 };
// non-array variable: name 'b', and memory cell: value=0x0C, address = 0x0D
int (*b)[1] = &a;
// non-array variable: name 's', and memory cell: value=0x0C, address = 0x0E
int *s = &a[0];
// non-array variable: name 't', and memory cell: value=0x0C, address = 0x0F
int *t = a; // Here is the key difference! read on...
现在这里是数组变量和非数组(指针)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
整数数组[100] = { 0 }; // 这里 'array' 计算为第一个内存单元的 *address*
// myproj_file2.cpp
外部 int* 数组; // 这里 'array' 计算为第一个内存单元的 *value*
// 假设链接器链接了两者
// 如果你阅读程序集,它的作用是这样的:
// extern int* array = (int*) array[0];
// 但它不是必须的,它可以做任何事情,因为行为是未定义的
我希望这会有所帮助。
如果您仍然认为进一步澄清可能会有所帮助,请提出后续问题,并毫不犹豫地获取“Deep C Secrets”一书的副本(库?):)
--
p.s.函数类型及其名称及其衰减与这篇文章的大部分内容无关
p.s.我还故意省略了当数组绑定到引用类型时不会发生数组到指针的转换