声明元素个数为 0 的数组仍然可以存储值

Posted

技术标签:

【中文标题】声明元素个数为 0 的数组仍然可以存储值【英文标题】:Declaring an array with 0 number of elements can still store values 【发布时间】:2012-09-19 04:31:56 【问题描述】:

我知道使用负索引只是运气。但出于好奇,我尝试了这个。我知道你可以声明 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();

【问题讨论】:

我认为,你不能在那个数组中存储值 你也可以写array[1] = 5,值会被存储。只是你正在写入一个不是为你保留的内存位置。 访问负索引不是纯粹的运气,可以通过指针来实现。 如果您只是调试代码并跟踪您的内存,您的所有这些实验都可以很好地清除。 Visual Studio 在这些方面提供了很多选项。您可以监视您的变量、它们访问的寄存器以及它们写入的内存。无论如何,很好的实验;-) 你能想到多少种不同的方法来使用超出范围的索引,并被告知结果是未定义的行为,因为索引超出了范围,然后再次询问不同的方法超出范围的索引? ;-) 当然,在这个例子中,你引入了一个新的转折点,即数组定义是非法的,但即使它是合法的你也会越界访问它 【参考方案1】:

这样一个0 大小的数组在标准 C 中是违反约束的,您的编译器不应该在不给您诊断的情况下让您侥幸逃脱。如果它没有告诉您什么,那一定是您的编译器供应商添加到其 C 方言中的扩展。

不要依赖此类扩展。

无论您是否声明大小为0 的数组,C 都不会对数组或指针访问进行强制边界检查。但是,如果在编译时知道超出范围,一个好的现代编译器仍然应该给你一个警告。

【讨论】:

【参考方案2】:

您正在访问一个未定义或被其他东西使用的内存空间。这段代码最终会搞砸一些事情,因为在您使用 malloc 之前,您不知道数组存在的位置。您创建了一个大小为零的数组。无论你给它一个地址,C都会让你写任何你想写的东西。禁止操作系统干扰。 “array”是一个指向数组的指针,它有一些任意值。编译器不知道在使用时应该为数组的第零个元素保留内存空间。

【讨论】:

在他的示例中,他不能使用 malloc 为他的array 分配空间。他需要声明 int *array; 以便稍后为其分配空间(使用 malloc 或其他分配函数)。 @mah 是的,这是真的。然而,array 仍然是一个指针,只是不是指向编译器允许他使用 malloc 的地方,因为它是一个静态定义的数组。我的意思是,除非你有一个静态分配的数组,否则 malloc 是保证保留内存的唯一方法。【参考方案3】:

C 假设(在可能的情况下)您知道自己在做什么。零长度数组仍然有一个地址(但该地址可以很容易地与其他大小的东西共享)。当你索引到那个数组时,你只是在修改你正在使用的内存位置,而不用关心你最终得到的地址还有什么——通过写入,你很容易导致巨大的(并且难以调试)问题.

【讨论】:

标准 C 中根本不允许使用零大小的数组。 @Jens ideone.com/7ZT0K -- 编译器允许它并执行它。也许编译器只是在优化数组?我在尝试打印array 的地址时确实遇到了段错误,但如果标准不允许这样做,那就是它可以编译了。 必须给你一个诊断,所以这绝对是一个非标准的编译器。 当使用 gcc 版本 4.4.5 20101112 和 -Wall -Wextra 进行编译时,唯一的警告是变量 i 未使用。使用 -std=c99 和/或 -ansi 时会出现相同的结果。我需要向 gcc 提供什么选项以导致零长度数组无法编译?我希望能够编译到完全合规性,而不是依赖于不可移植的扩展。 您必须添加-pedantic。 Gcc 然后告诉我:“ISO C 禁止零大小数组”【参考方案4】:

就像 Vikdor 在评论中所说的那样,你正在写一个不是为你保留的内存位置。这可能会导致严重且难以调试的问题,因此我建议您永远不要这样做,但这就是它的工作原理:

当您声明一个大小为 0 int 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。数组的名字只是一个指针。

【讨论】:

【参考方案5】:
int array[0]; 

是无效的 C 代码。它不符合标准。您的代码可以干净地编译,因为它使用了 compiler specific extension

参考:C99 标准:6.7.5.2 数组声明符第 1 段:

除了可选的类型限定符和关键字 static,[ 和 ] 可以分隔 一个表达式或*。如果它们界定了一个表达式(它指定了一个数组的大小),则该表达式应为整数类型。 如果表达式是常量表达式,则它的值应大于零。元素类型不应是不完整类型或函数类型。可选的类型限定符和关键字 static 应仅出现在 声明具有数组类型的函数参数,然后仅在最外层 数组类型推导。


为什么这似乎有效?

假设您的编译器实现允许零长度数组:

array[0] = 5; 

仍然有效,因为此代码语句会导致 未定义行为。 它写入不属于数组的内存区域,从而覆盖已分配内存的边界。幸运的是,它可以工作,因为内存可能没有被其他实体使用。从技术上讲,它仍然是一种未定义的行为。

【讨论】:

以上是关于声明元素个数为 0 的数组仍然可以存储值的主要内容,如果未能解决你的问题,请参考以下文章

C语言中整型数组的每个元素在内存中是如何存放的

Scala数据结构

各个元素之和等于给定值的连续子数组的个数

编写程序将一个数组中的数按逆序重新存放并输出(程序中数组的元素个数自定,逆序

java基础之数组

QByteArray数组的单个元素值的判断