为什么我可以使用以0大小启动的数组:Char ch [0];

时间:2013-03-12 19:44:06

标签: c++ arrays char

这是我的代码:

#include<iostream>
using namespace std;
int main(){
    char ch[0];
    cin >> ch;
    cout << ch;
    return 0;
}

INPUT1:

abcdefghijklmnopqrstuvwxyza

OUTPUT1:

abcdefghijklmnopqrstuvwxyza

(工作正常,但我不知道为什么)

输入2:

abcdefghijklmnopqrstuvwxyzab

OUTPUT2:

abcdefghijklmnopqrstuvwxyzab_

(请求输入)

输入3:

abcdefghijklmnopqrstuvwxyzabc

OUTPUT3: (运行时错误)

当output2请求输入时,我们输入input2, 输出是相同的output2(再次请求输入), 当我们将input1或input2放在

时,output1或output2也会出现

有人可以解释这种现象吗?为什么会这样?

7 个答案:

答案 0 :(得分:12)

大小为0的数组无效:

  

如果存在常量表达式(5.19),则它应为整数常量表达式,其值应大于零。

如果您的编译器接受它,它只是一个非标准扩展。 GCC接受它,但如果您添加-pedantic选项,则会发出诊断信息:

warning: ISO C++ forbids zero-size array ‘arr’ [-pedantic]

尽管如此,读入零大小的非标准数组无疑会给你不明确的行为。

答案 1 :(得分:2)

正如stfrabbit指出标准显式禁止声明大小为零的数组。但gcc允许此类事件作为我们不会进入的reasons的扩展。

那发生了什么事?好吧,当需要查找operator>>operator<<的可接受重载时,编译器会将char ch[0]视为char[],然后退化为char *

它找到char *的重载并调用它。所以你现在正在颠簸随机记忆(从ch的地址开始,这是谁知道什么)。如果你之前没有处于未定义的行为状态,那么你现在处于未定义的行为状态。

一旦你处于未定义的行为状态,任何就会发生:该程序可能导致Universe开始收缩,或者它可能导致一百......百万...... 。美元神奇地出现在您的银行帐户中。

或者它可能会崩溃。

答案 2 :(得分:1)

基本上char ch[0];是内存中定义的地址。 你把它传递给Cin它开始从输入到它的字符写。 然后,您传递相同的地址,并尝试理解它。 基本上打印每个字符,直到它运行到0。

你必须深入调查你为什么会看到这种行为,但这完全是浪费时间。行为是不确定的,不能保证它在任何方面都是可重复的。

答案 3 :(得分:1)

大多数编译器只是将数组设置为具有真正的起始地址,但不会占用任何空间,因此堆栈或结构中的后续变量将从与0大小的数组不存在时相同的地址开始一点都不这经常被用作结构的最后一个成员,当由n个字节分配时,结构被填充。然后可以索引作为最后一个成员的数组,以便在没有指针数学的情况下访问该填充。

例如

struct foo
{
    int a;
    int b;
    char c[0];
};

foo* f = malloc(sizeof(foo) + 50);

for (int i = 0; i < 50; ++i)
    f->c[i] = 57;

sizeof foo很可能是8但无关紧要,因为c是该结构的结束地址,无论结构是如何字节对齐/填充的。

一些Win32 API利用了它。

答案 4 :(得分:0)

ISO/ANSI Standard for C++, 1996ISO/IEC 14882:2011 Programming Language C++ (draft)

  

语法提供了空的初始化列表,但仍然是C ++   没有零长度数组。

所以,这是非法的。

答案 5 :(得分:0)

你需要有可以存储输入值的数组,这绝对无法处理。

char ch[0];

这肯定是Segmentation错误。

$./a.out 
12345678901234567890
Segmentation fault (core dumped)

您需要将ch定义为您输入所需的最大值。

答案 6 :(得分:0)

你遇到了经典的缓冲区溢出问题。虽然它不符合定义零大小的数组,但正在发生的是,你在堆栈上得到一个带有默认对齐大小的变量(通常是4或8)。

因此,当您开始读取该变量时,您将数据放入堆栈,并开始覆盖您的堆栈帧。这不会立即显现,但它会破坏你的返回地址。

所以代码执行read(fine),然后write(也没关系),然后尝试返回。如果返回地址已被破坏,则会出现分段错误。否则,这可能会继续被忽视。你在上一个例子中没有得到输出的原因是缓冲输出 - 程序在退出之前没有得到更改以刷新缓冲区(因为从main返回后完成)。

以下是一个示例,您可以看到代码如何覆盖堆栈数据:

int main(int argc, const char *argv[])
{
    int dummy1 = 0xCDCDCDCDCDCDCDCD;
    int dummy2 = 0xCDCDCDCDCDCDCDCD;

    char badvar[0];

    cin >> badvar;
    cout << badvar << endl;
    cout << dummy1 << endl;
    cout << dummy2 << endl;
    cout << flush;

    return 0;
}