1

これは私のコードです:

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

input1:

abcdefghijklmnopqrstuvwxyza

output1:

abcdefghijklmnopqrstuvwxyza

(正常に動作していますが、理由はわかりません)

input2:

abcdefghijklmnopqrstuvwxyzab

output2:

abcdefghijklmnopqrstuvwxyzab_

(入力を要求する)

input3:

abcdefghijklmnopqrstuvwxyzabc

output3 :(実行時エラー)

output2が入力を要求し、input2を入力すると、出力は同じoutput2(再度入力を要求)になり、input1またはinput2を入力するとoutput1またはoutput2も表示されます。

誰かがこの現象を説明できますか?なぜそれが起こるのですか?

4

6 に答える 6

12

サイズ0の配列は無効です:

定数式(5.19)が存在する場合、それは積分定数式であり、その値はゼロより大きくなければなりません。

コンパイラがそれを受け入れる場合、それは単に非標準の拡張です。GCCはそれを受け入れますが、-pedanticオプションを追加すると診断を発行します。

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

それでも、サイズがゼロの非標準の配列を読み取ると、間違いなく未定義の動作が発生します。

于 2013-03-12T19:45:55.700 に答える
2

stfrabbitが指摘しているように、標準ではサイズ0の配列の宣言が明示的に禁止されています。しかし、私たちが入らない理由gccのために、この種のことを拡張として許可します。

では、何が起こっているのでしょうか。さて、許容できるオーバーロードを探すときが来たらoperator>>operator<<コンパイラはそれを処理char ch[0]char[]て、次に縮退しchar *ます。

のオーバーロードを見つけてchar *呼び出します。つまり、ランダムメモリをスラッシングしていることになります(アドレスchがwho-knows-whatから始まります)。以前に未定義の行動の土地にいなかった場合、現在は未定義の行動の土地にいます。

そして、あなたが未定義の行動の土地に入ると、何かが起こります:プログラムは宇宙を収縮させ始めるかもしれません、あるいはそれはあなたの銀行口座に魔法のように1...1億...百万...ドルを引き起こすかもしれません。

または、クラッシュする可能性があります。

于 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はその構造体の終了アドレスであるため、構造体がどのようにバイト整列/パディングされているかに関係なく、問題ではありません。

一部のWin32APIはこれを利用しています。

于 2013-03-12T22:32:53.013 に答える
0

入力値を格納できる配列が必要ですが、これは間違いなく処理できません。

char ch[0];

これは間違いなくSegmentation欠点です。

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

ch入力として必要な大きさを定義する必要があります。

于 2013-03-12T19:54:19.503 に答える
0

従来のバッファオーバーランの問題が発生しています。ゼロサイズの配列を定義することは準拠していませんが、何が起こっているのかというと、デフォルトの配置のサイズ(通常は4または8)で変数をスタックに取得しているということです。

したがって、その変数の読み取りを開始すると、データがスタックに配置され、スタックフレームのオーバーライドが開始されます。これはすぐには見えませんが、リターンアドレスを破壊します。

したがって、コードは読み取り(fine)を実行し、次に書き込み(これも問題ありません)を実行してから、戻りを試みます。差出人住所が破棄された場合、セグメンテーション違反が発生します。そうしないと、これが見過ごされる可能性があります。最後の例で出力が得られない理由は、バッファリングされた出力が原因です。プログラムは、終了する前にバッファをフラッシュするための変更を取得しません(メインから戻った後に実行されるため)。

コードがスタックデータをオーバーライドすることを確認する方法の例を次に示します。

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 に答える