阅读有关C中指针和数组的一些细节后,我有些困惑。
一方面,数组可以被视为数据类型。另一方面,数组往往是一个不可修改的lvalue。我想象编译器会做类似的事情,用常量地址和运行时计算给定索引位置的表达式来替换数组的标识符。
myArray[3] -(compiler)-> AE8349F + 3 * sizeof(<type>)
当说一个数组是一种数据类型时,这意味着什么?我希望你能帮我澄清我对数组的真正理解以及编译器如何处理它。
auto V[10];
编译器分配了11个单元;10个连续单元用于数组本身,另一个单元绑定到V,其中包含第一个单元的位置:
+----+
V: | | -----+
+----+ |
... |
+----+ |
| | <----+
+----+
| |
+----+
| |
+----+
| |
+----+
...
当Ritchie在将struct
类型添加到C语言时,他意识到这种安排会带来一些问题。例如,他想创建一个结构体类型来表示文件或目录表中的条目:
当Ritchie在为C语言添加struct
类型时,他意识到这样的安排会导致一些问题。例如,他想创建一个结构体类型来表示文件或目录表中的条目:
struct {
int inumber;
char name[14];
};
他希望这个结构不仅以抽象方式描述条目,而且还要表示实际文件表条目中的位,该条目没有额外的单元或字来存储数组中第一个元素的位置。所以他放弃了它-他没有设置单独的位置来存储第一个元素的地址,而是编写了C代码,使得在计算数组表达式时,第一个元素的地址将被计算出来。
这就是为什么你不能做像这样的事情。int a[N], b[N];
a = b;
因为在这种情况下,a
和b
都被评估为指针值; 这等效于编写3 = 4
。实际上没有任何存储数组中第一个元素的地址的内存; 编译器只是在翻译阶段计算它。
如果您想了解更多细节,请阅读此答案。
编辑:为了更清晰;可修改l-value,不可修改l-value和r-value之间的区别(简述);
这些表达式之间的区别如下:
- 可修改的l-value是可寻址的(可以是一元&的操作数)并且可赋值的(可以是=的左操作数)。
- 不可修改的l-value是可寻址的,但不可赋值。
- r-value既不可寻址也不可赋值。
<type> foo[7]; myArray = foo;
那么你会在第二行 (myArray = foo;
) 得到一个编译器错误。这就是他们所说的数组是不可修改的左值*。当然,你可以为 myArray[i]
(对于任何常量或变量 i
)赋值,但你不能直接赋值给 myArray
。想一想它被编译成什么...根据你的例子,myArray = foo;
将会是 0xAE8349F = 0xDEADBEEF;
(假设 foo
位于地址 0xDEADBEEF)。对于任何赋值运算符,常量都不能是左值。 - Dave Lillethun2 + 3
,既不是2
也不是3
是lvalue。在C标准中,“lvalue”的确切定义在每个版本中都有所改变,但基本上它是一个(潜在地)指定对象的表达式。 - Keith Thompson数组是一块连续的内存区域。这意味着它在内存中是按顺序排列的。假设我们定义了如下的一个数组:
int x[4];
当 sizeof(int) == 32
位时。
这将在内存中以这种方式布局(选择一个任意的起始地址,比如说 0x00000001
)
0x00000001 - 0x00000004
[element 0]
0x00000005 - 0x00000008
[element 1]
0x00000009 - 0x0000000C
[element 2]
0x0000000D - 0x00000010
[element 3]
你说得对,编译器会替换标识符。记住(如果你已经学过的话,否则你正在学习新知识!)数组本质上是一个指针。在C/C++中,数组名是指向数组第一个元素的指针(或者在我们的例子中指向地址0x00000001
的指针)。通过这样做:
std::cout << x[2];
int i = 2;
std::cout << x[i];
int i = 2;
std::cout << x + (i * sizeof(int));
它基本上将数据类型的大小乘以给定的索引,然后将其添加到数组的基地址中。编译器基本上采用索引运算符[]
并将其转换为指针加法。
如果你真的想让自己头晕,考虑一下这段代码:
std::cout << 2[x];
这是完全有效的。如果你能弄清楚为什么,那么你就掌握了这个概念。
x[2]
并不会将2添加到x
的地址上。其次,数组在功能上只是一块连续的内存块,但这并不意味着元素之间没有间隙(考虑对齐)。 - user268396int arr[100]; sizeof arr
并不能给你一个指针的大小。在大多数情况下,数组名会被 转换 成指向第一个元素的指针。请参考 comp.lang.c FAQ 第6节。 - Keith Thompson
foo
元素的数组的大小为N*sizeof(foo)
。另一方面,数组表达式的行为很奇怪,这在comp.lang.c FAQ第6节中得到了详细解释,H2CO3已经在上面提供了链接。 - Keith Thompson