記憶體重疊是指在記憶體中存在兩個或多個區域,它們的地址範圍有交叉部分。在 C++ 中,記憶體重疊可能會導致程式出現不可預期的行為,因此我們需要瞭解它的原因和如何避免。 記憶體重疊的原因 記憶體重疊的主要原因是指針的使用。當我們使用指針訪問記憶體時,如果指針指向的記憶體區域與另一個區域有交叉部分,就會產生記憶體重疊。 ...
記憶體重疊是指在記憶體中存在兩個或多個區域,它們的地址範圍有交叉部分。在 C++ 中,記憶體重疊可能會導致程式出現不可預期的行為,因此我們需要瞭解它的原因和如何避免。
記憶體重疊的原因
記憶體重疊的主要原因是指針的使用。當我們使用指針訪問記憶體時,如果指針指向的記憶體區域與另一個區域有交叉部分,就會產生記憶體重疊。
如下圖,記憶體拷貝的兩種情況:
第一種情況下,拷貝重疊的區域不會出現問題,內容均可以正確地被拷貝。
第二種情況下,問題出現在右邊的兩個位元組,這兩個位元組的原來的內容首先就被覆蓋了,而且沒有保存。所以接下來拷貝的時候,拷貝的是已經被覆蓋的內容,顯然這是有問題的。
舉個代碼例子,下麵的代碼片段就會導致記憶體重疊:
char str[] = "Hello World";
char* p = str + 1;
memcpy(p, str, 11);
在上面的代碼中,我們定義了一個字元數組 str
,並使用指針 p
指向 str
中的第二個字元。接著,我們使用 memcpy
函數將 str
中的 11 個字元複製到了 p
指向的區域。由於 p
指向的區域與 str
有交叉部分,因此就產生了記憶體重疊。因為 memcpy
是直接按位複製拷貝,代碼如下,所以會遇到情況二,拷貝的是已經被覆蓋的內容。
void *memcpy(void *dest, const void *src, size_t count)
{
char *tmp = dest;
const char *s = src;
while (count--)
*tmp++ = *s++;
return dest;
}
如何避免記憶體重疊
為了避免記憶體重疊,我們需要註意以下幾點:
- 儘量避免使用指針,尤其是指針運算和類型轉換;
- 在使用指針時,確保指針指向的記憶體區域與其他區域沒有交叉部分;
- 使用安全的記憶體操作函數,如
memcpy_s
、memmove
等,這些函數可以確保在複製記憶體時不會產生記憶體重疊。
那 memmove
是怎麼避免記憶體重疊的影響呢,我們看看 memmove
的代碼:
void *memmove(void *dest, const void *src, size_t n)
{
char *d = dest;
const char *s = src;
if (d < s) {
while (n--)
*d++ = *s++;
} else {
// 採用倒序拷貝
char *lasts = (char *)s + (n - 1);
char *lastd = d + (n - 1);
while (n--)
*lastd-- = *lasts--;
}
return dest;
}
memmove
判斷如果 dest
≥src
的時候(也就是前面圖片的情況2),採用倒序拷貝,避免了內容被覆蓋導致拷貝不完整的問題。
其原理圖如下:
memcpy 與 strcpy 都沒有記憶體重疊的問題,實際可以根據需要使用 memmove 。
總結
本文介紹了 C++ 中的記憶體重疊問題,指出了指針的使用是記憶體重疊的主要原因,並提供了避免記憶體重疊的方法,如儘量避免使用指針,確保指針指向的記憶體區域與其他區域沒有交叉部分,使用安全的記憶體操作函數等。此外,還介紹了 memmove
函數如何避免記憶體重疊的影響。