回答編集履歴
1
回答を修正
answer
CHANGED
@@ -1,6 +1,18 @@
|
|
1
|
-
経験的に言うと(具体的なアドレスなどはともかく)原因も違いも明確です。例外が発生するかしないかの違いは、コピーのソース側となっている
|
2
|
-
配列 char szString2[512]; の配置場所(メモリアドレス)に依ります。もう少し言うと、この512バイトのメモリに続く**残り512バイトのメモリ領域の、どこかがアクセスが許可されていない領域の先頭番地だった**という事です。
|
1
|
+
経験的に言うと(具体的なアドレスなどはともかく)原因も違いも明確です。例外が発生するかしないかの違いは、コピーのソース側となっている配列 char szString2[512]; の配置場所(メモリアドレス)に依ります。もう少し言うと、この512バイトのメモリに続く**残り512バイトのメモリ領域の中に、アクセス許可されている(=保護されていない)メモリと、許可されていない(=保護されている)メモリの境界があった**という事です。境界が残り512バイトのメモリ領域の中に無ければクラッシュしません。
|
3
2
|
|
3
|
+
> クラッシュするケースでは保護されているメモリ空間に不正にアクセスしたから、クラッシュした
|
4
|
+
|
5
|
+
は正しい理解です。念の為に繰り返しますが、アクセス許可されているメモリとは、保護されていないメモリ、ですよ。
|
6
|
+
|
7
|
+
書き込む先のメモリ領域にバッファオーバーランして書き込んだら、他の用途に使っているメモリ(他の変数)を上書きするので、その後、プログラムが異常動作する事は容易に想像できると思います。
|
8
|
+
一方、ご質問の状況は読み出し側のバッファオーバーランです。通常、コピー元のメモリ領域は、読みだす分にはオーバーランアクセスしたって(メモリを書き変えないのだから)問題を起こすことは無いものです。従って、問題なく動作することは少なくありません。というか、読み出しだけならオーバーランアクセスしても何も問題を起こさないでしょう。
|
9
|
+
|
10
|
+
ところがクラッシュするケースがある、それは例外(**メモリアクセス例外**、OSやCPUによって具体的な呼び名は違うかもしれない)が起こったことを意味します。そうなる理由はただひとつ、残り512バイトのメモリ領域の、ある番地から、アクセスが許可されていない・保護されたメモリだった、ということに尽きます。
|
11
|
+
|
12
|
+
プログラムが動作する時、動作に必要なメモリを、OSはアクセス可能なメモリ領域として割当てます。現在のCPUは[メモリ管理ユニット](https://ja.wikipedia.org/wiki/%E3%83%A1%E3%83%A2%E3%83%AA%E7%AE%A1%E7%90%86%E3%83%A6%E3%83%8B%E3%83%83%E3%83%88)、略してMMUを内蔵しており、OSはMMUを制御することで、各プロセスがアクセスできるメモリに制限をかけています。アクセス制限されたメモリ領域をアクセスしようとすれば(プログラムの異常動作であると見做して)例外を発生させ、プログラムを停止するようにしています。それがクラッシュするという事です。
|
13
|
+
|
14
|
+
そのアクセスを許可したメモリ領域の中に szString1[1024] も szString2[512]も配置されますが、szString2[512] に続く、残りの512バイトがアクセス可能なメモリである保証はありません。アクセスできたなら、それは運が良かったわけです。szString2[512]をメモリのどこに配置するか、なぜ境界の近くに配置されたのか、は**コンパイラと、OSの、両方の事情の合わせ技**のようなもので決まります。お使いのOSとコンパイラ次第なので具体的な説明はできかねますが。
|
15
|
+
|
4
16
|
memcpy() の中身はこんな感じです(愚直に書いてみた、ツッコミ無用笑)。
|
5
17
|
```C
|
6
18
|
void *memcpy(void *buf1, const void *buf2, size_t n)
|
@@ -16,16 +28,10 @@
|
|
16
28
|
}
|
17
29
|
return buf1;
|
18
30
|
}
|
19
|
-
|
20
31
|
```
|
21
|
-
|
32
|
+
「境界」を超えた時点で「temp = *s; // コピー元のメモリを読む」というメモリアクセス命令が
|
22
|
-
|
33
|
+
> 「保護されているメモリに読み取り(中略)操作を行おうとしました・・・」
|
23
34
|
|
24
|
-
ところがクラッシュするケースがある、それは例外(**メモリアクセス例外**?OSやCPUによって具体的な呼び名は違うかもしれない)が起こったことを意味します。そうなる理由はただひとつ、残り512バイトのメモリ領域の、ある番地から、アクセスが許可されていないメモリだった、ということに尽きます。
|
25
|
-
|
35
|
+
即ち不正アクセスになり、例外を起こしたのです。
|
26
36
|
|
27
|
-
|
28
|
-
|
29
|
-
そのアクセスを許可したメモリ領域の中に szString1[1024] も szString2[512]も配置されますが、szString2[512] に続く、残りの512バイトもアクセス可能なメモリである保証はありません。アクセスできたなら、それは運が良かったわけです。szString2[512]をメモリのどこに配置するかは、**コンパイラと、OSの、両方の事情の合わせ技**のようなもので決まります。
|
30
|
-
|
31
|
-
こういうことを実感するには、各変数、配列などの**メモリアドレスを確認する**などして、具体的なメモリ上の配置を調べてみるとよいです。さらに、荒っぽい話に聞こえるかもしれませんが、わざとバッファーオーバーランアクセスを起こしてして、どこで例外が起こるか試してみる、なんていうのも良い経験になります。
|
37
|
+
こうしたことを実感するには、各変数、配列などの**メモリアドレスを確認する**などして、具体的なメモリ上の配置を調べてみるとよいです。また変数の配置は、コンパイラ(実際はリンカ)の仕様を調べると共に、コンパイル時に**リンケージマップ情報を出力させて調べる**ことができます。さらに、荒っぽい話に聞こえるかもしれませんが、わざとバッファーオーバーランアクセスを起こしてみて、どこで例外が起こるか試してみる、なんていう調べ方もできます。
|