1

这是我的代码:

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

输入1:

abcdefghijklmnopqrstuvwxyza

输出1:

abcdefghijklmnopqrstuvwxyza

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

输入2:

abcdefghijklmnopqrstuvwxyzab

输出2:

abcdefghijklmnopqrstuvwxyzab_

(请求输入)

输入3:

abcdefghijklmnopqrstuvwxyzabc

输出3:(运行时错误)

当 output2 请求输入时,我们放入 input2,输出与 output2 相同(再次请求输入),当我们将 input1 或 input2 放入其中时,也会出现 output1 或 output2

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

4

6 回答 6

12

大小为 0 的数组无效:

如果常量表达式 (5.19) 存在,它应该是一个整数常量表达式并且它的值应该大于零。

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

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

尽管如此,读取零大小的非标准数组无疑会给您带来不确定的行为。

于 2013-03-12T19:45:55.700 回答
2

正如 stfrabbit 指出的那样,该标准明确禁止声明大小为零的数组。但是出于我们不会讨论的原因gcc,允许这种事情作为扩展。

那么发生了什么?那么,当是时候寻找可接受的重载operator>>并且operator<<编译器将其char ch[0]视为char[]然后退化为char *.

它找到一个重载char *并调用它。所以你现在正在破坏随机内存(从地址开始ch就是谁知道什么)。如果您以前没有处于未定义的行为领域,那么您现在处于未定义的行为领域。

一旦你处于未定义的行为领域,任何事情都会发生:该程序可能导致宇宙开始收缩,或者它可能导致一亿……百万……美元神奇地出现在你的银行账户中。

或者它可能会崩溃。

于 2013-03-12T20:44:26.707 回答
1

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

您必须对为什么会看到这种行为进行深入调查,但这完全是浪费时间。该行为未定义,无法保证它以任何方式可重复。

于 2013-03-12T19:59:02.127 回答
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 利用了这一点。

于 2013-03-12T22:32:53.013 回答
0

您遇到了经典的缓冲区溢出问题。尽管定义零大小的数组不符合要求,但发生的情况是,您在堆栈上获得了一个具有默认对齐大小的变量(通常为 4 或 8)。

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

所以代码执行读取(很好),然后写入(这也很好),然后尝试返回。如果返回地址已被破坏,则会出现分段错误。否则,这可能会继续被忽视。在最后一个示例中没有得到输出的原因是由于缓冲输出 - 程序在退出之前没有更改刷新缓冲区(因为它是在从 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;
} 
于 2013-03-13T12:01:02.570 回答
0

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

char ch[0];

这绝对是一个Segmentation错误。

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

您需要定义ch为输入所需的大小。

于 2013-03-12T19:54:19.503 回答