10

これは、一部の人にとっては非常に基本的な質問かもしれません。strcpyが実際に舞台裏でどのように機能するかを理解しようとしていました。たとえば、このコードでは

#include <stdio.h>
#include <string.h>
int main ()
{
  char s[6] = "Hello";
  char a[20] = "world isnsadsdas";
  strcpy(s,a);

  printf("%s\n",s);
  printf("%d\n", sizeof(s));
  return 0;
}

As I am declaring s to be a static array with size less than that of source. I thought it wont print the whole word, but it did print world isnsadsdas .. So, I thought that this strcpy function might be allocating new size if destination is less than the source. But now, when I check sizeof(s), it is still 6, but it is printing out more than that. Hows that working actually?

4

8 に答える 8

16

未定義の動作を引き起こしたばかりなので、何でも起こり得ます。あなたの場合、あなたは幸運になり、クラッシュしていませんが、その出来事に頼るべきではありません。これは単純化されたstrcpy実装です (ただし、多くの実際の実装からそれほど離れていません)。

char *strcpy(char *d, const char *s)
{
   char *saved = d;
   while (*s)
   {
       *d++ = *s++;
   }
   *d = 0;
   return saved;
}

sizeofコンパイル時から配列のサイズを返すだけです。を使用するstrlenと、期待どおりになると思います。しかし、上で述べたように、未定義の動作に依存することは悪い考えです。

于 2013-02-06T07:04:26.910 に答える
5

http://natashenka.ca/wp-content/uploads/2014/01/strcpy8x11.png

strcpy は、あなたが示しているような理由で危険であると考えられています。作成した 2 つのバッファーは、関数のスタック フレームに格納されたローカル変数です。スタック フレームの大まかな外観は次のとおりです

参考までに、スタックの一番上に配置されます。つまり、メモリを逆方向に拡張します (これは、メモリ内の変数が逆方向に読み取られるという意味ではなく、新しい変数が古い変数の「後ろ」に配置されるという意味です)。つまり、関数のスタック フレームの locals セクションに十分に書き込むと、コピー先の変数の後に他のすべてのスタック変数に順方向に書き込み、他のセクションに割り込んで、最終的にリターン ポインターを上書きします。その結果、賢ければ、関数が返す場所を完全に制御できます。あなたはそれを本当に何でもさせることができますが、心配しているのはあなたではありません.

5 文字の文字列に対して最初のバッファを 6 文字にすることでわかるように、C 文字列は null バイト \x00 で終わります。strcpy 関数は、コピー元のバイトが 0 になるまでバイトをコピーしますが、コピー先がその長さであることを確認しません。これが、配列の境界を越えてコピーできる理由です。これは、印刷がそのサイズを超えてバッファを読み取っている理由でもあり、\x00 まで読み取ります。興味深いことに、コンパイラがスタックに与えた順序に応じて、strcpy が s のデータに書き込んだ可能性があるため、a を出力して、「snsadsdas」のような結果が得られるかどうかを確認するのも楽しい練習になるかもしれませんが、できませんs を汚染している場合でも、さまざまな理由でスタックエントリ間にバイトが存在することがあるため、どのように見えるかを確認してください)。

このバッファに、ハッシュ関数を使用してコードをチェックインするためのパスワードが保持されていて、それを取得した場所 (サーバーの場合はネットワーク パケット、テキスト ボックスなど) からスタック内のバッファにコピーすると、非常にうまくいきます。宛先バッファが保持できるよりも多くのデータをソースからコピーし、パケットを送信したりパスワードを試行したりできたユーザーにプログラムの制御を返す可能性があります。正しい文字数を入力してから、RAM のどこかにジャンプするアドレスを表す正しい文字を入力するだけです。

境界をチェックしてソース文字列をトリミングする場合は strcpy を使用できますが、これは悪い習慣と見なされます。http://www.cplusplus.com/reference/cstring/strncpy/のように最大長を取る最新の関数があります。

最後に、これはすべてバッファ オーバーフローと呼ばれます。一部のコンパイラは、OS によってランダムに選択されたバイトの素敵な小さな塊を、すべてのスタック エントリの前後に追加します。コピーのたびに、OS はこれらのバイトをそのコピーと照合してチェックし、異なる場合はプログラムを終了します。これにより、多くのセキュリティ問題が解決されますが、バイトが変更されたときに何が起こるかを処理する関数へのポインターを上書きするのに十分な距離までバイトをスタックにコピーして、同じことを行うことができます。正しく行うのが非常に難しくなります。

于 2014-12-17T19:26:37.353 に答える
2

C では、配列の境界チェックはありません。これは、自分自身を撃つリスクを冒してパフォーマンスを向上させるためのトレードオフです。

strcpy()ターゲットバッファが十分に大きいかどうかは気にしないため、コピーしすぎると未定義の動作が発生します。

これが、ターゲット バッファ サイズを指定できる新しいバージョンの strcpy が導入された理由の 1 つです。strcpy_s()

于 2013-02-06T07:12:18.803 に答える
1

sizeof(s)は実行時に決定されることに注意してください。strlen()を使用して、占有されている文字数を検索します。strcpy()を実行すると、ソース文字列が宛先文字列に置き換えられるため、出力は「Helloworldisnsadsdas」にはなりません。

#include <stdio.h>
#include <string.h>
main ()
{
  char s[6] = "Hello";
  char a[20] = "world isnsadsdas";
  strcpy(s,a);

  printf("%s\n",s);
  printf("%d\n", strlen(s));
}
于 2013-02-06T07:27:47.657 に答える
1

コードがたまたま機能する場所に 2 つの配列を配置するためにコンパイラが選択したのと同じくらい、未定義の動作に依存しています。これは将来的に機能しない可能性があります。

演算子に関してsizeofは、これはコンパイル時に計算されます。

適切な配列サイズを使用したらstrlen、文字列の長さを取得するために使用する必要があります。

于 2013-02-06T07:05:03.587 に答える
0

strcpy が舞台裏でどのように機能するかを理解する最良の方法は...そのソース コードを読むことです! GLibC のソースを読むことができます: http://fossies.org/dox/glibc-2.17/strcpy_8c_source.html。それが役立つことを願っています!

于 2013-02-06T07:04:15.970 に答える
0

より良い解決策は

char *strcpy(char *p,char const *q)
{
   char *saved=p;

   while(*p++=*q++);

   return saved;
}
于 2014-09-09T11:02:07.413 に答える
0

すべての文字列/文字配列null terminator character '\0'の終わりには、文字列/文字配列の終わりを示す があります。

strcpy()'\0' 文字が表示されるまでタスクを実行します。

printf()また、「\0」文字が表示されるまでタスクを実行します。

sizeof()一方、配列の内容には関心がなく、割り当てられたサイズ (想定される大きさ) のみに関心があるため、文字列/文字配列が実際にどこで終了するか (実際の大きさ) は考慮されません。

sizeof() とは対照的に、文字列が実際にどのくらいの長さであるか (本来の長さではなく) に関心strlen()があるため、最後 ('\0' 文字) に到達するまでの文字数をカウントします。停止します(「\0」文字は含まれません) .

于 2016-07-05T09:50:39.370 に答える