我知道使用负索引只是运气。但出于好奇,我尝试了这个。我知道你可以声明 array[0]; 就像 malloc(0); 是合法的。但是我怎么能在数组[0]中存储一个值呢?
#include <stdio.h>
#include <conio.h>
int main(void)
{
int i;
int array[0];
array[0] = 5;
printf("%d\n",array[0]);
getch();
}
如此0
大小的数组在标准 C 中是违反约束的,您的编译器不应该在不给您诊断的情况下让您侥幸逃脱。如果它没有告诉您什么,那一定是您的编译器供应商添加到其 C 方言中的扩展。
不要依赖这样的扩展。
无论您是否使用 size 声明数组0
,C 都没有对数组或指针访问进行强制边界检查。但是,如果在编译时知道超出范围,一个好的现代编译器仍然应该给你一个警告。
您正在访问一个未定义或被其他东西使用的内存空间。这段代码最终会搞砸一些事情,因为在您使用 malloc 之前,您不知道数组存在的位置。您创建了一个大小为零的数组。无论你给它一个地址,C都会让你写任何你想写的东西。禁止操作系统干扰。“array”是一个指向数组的指针,它有一些任意值。编译器不知道在使用它时应该为数组的第零个元素保留内存空间。
C 假设(在可能的情况下)您知道自己在做什么。零长度数组仍然有一个地址(但该地址可以很容易地与其他大小的东西共享)。当你索引到那个数组时,你只是在修改你正在使用的内存位置,而不用关心你最终得到的地址还有什么使用——并且通过写入,你很容易导致巨大的(并且难以调试)问题.
就像 Vikdor 在评论中所说的那样,你正在写一个不是为你保留的内存位置。这可能会导致严重且难以调试的问题,因此我建议您永远不要这样做,但这就是它的工作原理:
当您声明一个大小为 0int array[0]
的数组时,编译器会将名称“数组”与内存位置相关联。对于这个例子,假设为 100。但是由于数组的大小为 0,所以没有字节属于数组,因此字节 100、101 等也可能分配给其他变量。
当您说array[0] = 5
您将数字 5 写入字节 100、101、102 和 103 时,因为 int 的长度为 4 个字节。然后您可以使用从位置 100 开始读取 4 个字节的数组 [0] 读取该数字。
当从 100 开始的空间分配给其他变量时会出现问题,因为它可能会覆盖 array[0] 并且看起来好像 array[0] 无缘无故地改变了(这是你将花费大量令人沮丧的时间的地方调试)
请记住,int array[0] 就像 int *array。数组的名称只是一个指针。
int array[0];
是无效的 C 代码。它不符合标准。您的代码可以干净地编译,因为它使用了特定于编译器的扩展。
参考:
C99 标准:6.7.5.2 数组声明器
第 1 段:
除了可选的类型限定符和关键字 static 之外,[ 和 ] 可以分隔表达式或
*
. 如果它们分隔了一个表达式(它指定了一个数组的大小),则该表达式应为整数类型。如果表达式是常量表达式,则它的值应大于零。元素类型不应是不完整类型或函数类型。可选类型限定符和关键字 static 应仅出现在具有数组类型的函数参数的声明中,然后仅出现在最外层的数组类型派生中。
为什么这似乎有效?
假设您的编译器实现允许零长度数组:
array[0] = 5;
仍然有效,因为此代码语句会导致Undefined Behavior。
它写入不属于数组的内存区域,从而覆盖已分配内存的边界。幸运的是,它可以工作,因为内存可能没有被其他实体使用。从技术上讲,它仍然是一种未定义的行为。