11

以下の C コードを使用してうまく動作するのはなぜstrcpyですか? 私はそれを2つの方法で失敗させようとしました:

1)strcpy文字列リテラルから、小さすぎてそれを含めることができない割り当てられたメモリに入れようとしました。それはすべてをコピーし、文句を言いませんでした。

strcpy2) 終了していない配列から試しましたNULstrcpyと はうまくprintfいきました。a が見つかるまで sをstrcpyコピーしたと思っていましたが、何も存在せず、停止しました。charNUL

これらが失敗しないのはなぜですか?何らかの形で「運が良かった」だけですか、それともこの機能の仕組みを誤解していますか? 私のプラットフォーム (OS X Lion) に固有のものですか、それとも最近のほとんどのプラットフォームはこのように動作しますか?

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main() {
    char *src1 = "123456789";
    char *dst1 = (char *)malloc( 5 );

    char src2[5] = {'h','e','l','l','o'};
    char *dst2 = (char *)malloc( 6 );

    printf("src1: %s\n", src1);
    strcpy(dst1, src1);
    printf("dst1: %s\n", dst1);
    strcpy(dst2, src2);
    printf("src2: %s\n", src2);
    dst2[5] = '\0';
    printf("dst2: %s\n", dst2);

    return 0;
}

このコードの実行による出力は次のとおりです。

$ ./a.out   
src1: 123456789
dst1: 123456789
src2: hello 
dst2: hello
4

5 に答える 5

19

まず、小さすぎる配列にコピーします。

C には配列の境界を超えることに対する保護がないため、 に機密性の高いものが何もない場合はdst1[5..9]幸運であり、コピーは正当に所有していないメモリに入りますが、クラッシュすることもありません。ただし、そのメモリは変数に割り当てられていないため、安全ではありません。別の変数にそのメモリが割り当てられている可能性があり、後でそこに入れたデータを上書きして、後で文字列を破損する可能性があります。

次に、null で終了していない配列からコピーします。

通常、メモリは任意のデータでいっぱいであると教えられていますが、その巨大なチャンクはゼロになっています。に null-terminator を入れなかったとしても、たまたまそうなるsrc2可能性は十分にあります。これにより、コピーが成功します。これは保証されておらず、いつでも、どのプラットフォームでも、どの実行でも失敗する可能性があることに注意してください。しかし、今回は (そしておそらくほとんどの場合) 運が良かったので、うまくいきました。src[5]\0

于 2011-08-21T17:33:42.447 に答える
14

割り当てられたメモリの境界を超えて上書きすると、Undefined Behaviorが発生します。
ある意味では、あなたは幸運でした。

未定義の動作とは、言語のルールを定義する標準が動作を定義していないため、何かが発生する可能性があり、その動作を説明できないことを意味します。

編集:再考すると、プログラムが正常に動作し、クラッシュしないということは、ここでは本当に不運
だと思います。今効くからといって、ずっと効くわけではなく、実はカチカチと弾ける爆弾なのです。

マーフィーの法則によると、
うまくいかないものはすべてうまくいかない[「そして、おそらく最も不便な瞬間に」]

[ ]- 法律に対する私の編集です:)

于 2011-08-21T17:31:47.600 に答える
4

@Als が言ったように、これは未定義の動作です。これはクラッシュする可能性がありますが、クラッシュする必要はありません

多くのメモリ マネージャは、メモリの大きなチャンクを割り当ててから、おそらく 4 バイトまたは 8 バイトの複数の小さなチャンクで「ユーザー」に渡します。したがって、境界を越えた書き込みは、おそらく割り当てられた余分なバイトに書き込むだけです。または、他の変数の 1 つを上書きします。

于 2011-08-21T17:34:54.497 に答える
4

はい、あなたは非常に単純に幸運になっています。

通常、ヒープは連続しています。これは、malloced メモリを超えて書き込むと、次のメモリ ブロック、またはユーザー メモリ ブロック間に存在する内部データ構造が破損する可能性があることを意味します。このような破損は、多くの場合、問題のあるコードのずっと後に現れるため、この種のバグのデバッグが困難になります。

NULメモリがたまたまゼロで埋められているため、おそらく s を取得しています(これは保証されていません)。

于 2011-08-21T17:33:51.130 に答える
1

mallocそこに十分なバイトがありません。最初の文字列"123456789"は 10 バイト (ヌル ターミネータが存在する) で、{'h','e','l','l','o'}6 バイトです (ここでもヌル ターミネータのためのスペースを確保しています)。現在、そのコードでメモリを破壊しているため、未定義の (つまり、奇妙な) 動作が発生します。

于 2011-08-21T17:34:07.350 に答える