1. 程式人生 > >記憶體重疊之strcpy/strncpy/strcat/strncat和memcpy---我又踩到地雷了

記憶體重疊之strcpy/strncpy/strcat/strncat和memcpy---我又踩到地雷了

       前面, 我們說過, strcpy是一個非常不安全的函式, 如果大家在專案中還在用strcpy, 那是不是應該反思一下呢?  於是, 有了更好的strncpy.  

       但是, 有一次, 我在用strncpy的時候,我覺得很自然啊, 卻犯了一個非常隱蔽的錯誤, 只是在當時的情況下, 該錯誤沒有暴露出來。 在其他情況下, 肯定是有錯誤的。 原來實際是出現了記憶體重疊, 被某高人羨慕發現, 我心服口服啊。 

      看看有記憶體重疊的strcpy(為了簡便起見, 我就不說strncpy的記憶體重疊了)

#include <iostream>
using namespace std;

int main()
{
	char str[100] = {0};
	strncpy(str, "abcdefghi", 100 - 1);

	strcpy(str + 2, str); // 記憶體重疊了, bug!!!
	cout << str << endl;

	return 0;
}
      我們期待的結果是:ababcdefghi,  但實際的結果是:ababcdcdghgh (在vc++6.0環境下),原來, strcpy/strncpy是沒有考慮記憶體重疊的。 我不小心踩到了, 也算一種經歷吧,  做軟體開發就是這樣, 犯錯可以, 但不要犯相同的錯誤, 善於從錯誤中學習。

       那怎麼辦呢?

#include <iostream>
using namespace std;

int main()
{
	char str[100] = {0};
	strncpy(str, "abcdefghi", 100 - 1);
	cout << str << endl;

	char szTemp[100] = {0};
	strncpy(szTemp, str, sizeof(szTemp) - 1);

	strcpy(str + 2, szTemp);  // 這裡是示意, 防止越界啊
	cout << str << endl;

	return 0;
}
     我們期待的結果是:ababcdefghi,  實際結果也是這樣的。   

     微軟啊微軟, 你咋就沒有提前解決這個bug呢? 下面, 我來嘗試寫一個, 不一定100%穩健, 希望大家提出其中可能存在的bug微笑.

#include <iostream>
using namespace std;

char *myStrCpy(char *dst, const char *src)
{
	if(NULL == dst || NULL == src)
	{
		return NULL;
	}

	int len = strlen(src);
	char *p = (char *)malloc(len + 1);
	if(NULL == p)
	{
		return NULL;
	}

	char *q = p;
	p[len] = '\0'; // 有必要
	while(*p++ = *src++)
	{
		;
	}

	p = q;
	while(*dst++ = *p++)
	{
		;
	}

	free(q); // 不是free(p);
	return dst;
}

int main()
{
	char str[100] = {0};
	strncpy(str, "abcdefghi", 100 - 1);

	myStrCpy(str + 2, str);
	cout << str << endl;

	return 0;
}
      結果:ababcdefghi, 和預期一致。 有bug的話, 歡迎大家拍磚, 共同進步羨慕

      另外, 在記憶體拷貝時候, strcat/strncat/memcpy也是不允許記憶體重疊的, 總之,  一定要注意記憶體重疊問題。 memcpy替代的解決方法是memmove.  原型是:

void *memmove( void* dest, const void* src, size_t count );  至於其他函式(如 strcpy/strncpy/strcat/strncat/)的替代方法, 就相對迂迴了點, 需要自己多分配不重疊的記憶體去解決。

相關推薦

記憶體重疊strcpy/strncpy/strcat/strncatmemcpy---地雷

       前面, 我們說過, strcpy是一個非常不安全的函式, 如果大家在專案中還在用strcpy, 那是不是應該反思一下呢?  於是, 有了更好的strncpy.          但是, 有一次, 我在用strncpy的時候,我覺得很自然啊, 卻犯了一個非常隱蔽

記憶體溢位PermGen OOM深入分析解決方案

閱讀原文 *現在,網上關於討論PermGen OOM的資料很多,但是深入分析PermGen區域記憶體溢位原因的資料很少。本篇文章嘗試全面分析一下PermGen OOM的原因,其中涉及到了Java虛擬機器執行時資料區、型別裝載、型別解除安裝等,測試程式碼涉及到了JMX協議。

Linux記憶體管理三 頁的分配釋放

如上圖,Linux分配頁時,只能分配2^n個頁。核心維護MAX_ORDER個連結串列,每個連結串列記錄著連續的空閒頁。第一個連結串列中的每一項為1個空閒頁,第二個連結串列中的每一項為2個空閒頁,第三個連結串列中的每一項為4個空閒頁。。。,依次類推。分配頁時,從對應的連結串列上摘除空閒頁;釋放頁時,將對應的頁歸

C函式:strlen,strcat,strncat,strcmp,strncmp,strcpy,strncpy,strstr詳解

strlen() 原型:size_t strlen( const char *string ); 功能:計算給定字串的(unsigned int型)長度,不包括'\0'在內 說明:返回s的長度,不包括

Linux記憶體回收LRU連結串列第二次機會法

一  LRU回收演算法 記憶體回收的核心是圍繞LRU連結串列來進行操作,Linux核心實現了5種LRU連結串列型別 1. 不活躍匿名頁錶鏈表(LRU_INACTIVE_ANON)//shmem 2. 活躍匿名頁錶鏈表(LRU_ACTIVE_ANON)// 3. 不活躍檔案對

Python學習旅_01day:變數常量,變數輸入,縮排,if條件,記憶體回收機制

1.變數和常量1.1 變數是為了儲存程式運算過程中的一些中間結果,為了方便日後呼叫1.2 變數存在一定的描述性,讓大眾一看就知道該變數的用途 1.3書寫方式 變數的命名規則 1. 要具有描述性 2. 變數名只能_,數字,字母組成,不可以是空格或特殊字元(#?<.,¥$*!~) 3. 不能以中文為變數名

Nginx學習路(六)NginX中的記憶體管理---Nginx中的記憶體對齊記憶體分頁

Nginx由於極高的效能受到大家的追捧,而Nginx的高效能與它優秀的記憶體管理方式是分不開的,今天就來聊一聊Nginx中的記憶體對齊和記憶體分頁。先說下Nginx中的記憶體對齊,Nginx中的記憶體對齊機制是它高效能的關鍵因素之一,先說點基礎的東西,什麼是記憶體對齊呢? 記

記憶體管理棧的區別

不知道誰寫的,很詳細,對了解程式資料儲存有一定幫助,轉載過來自己學習同時與眾分享。 一、預備知識―程式的記憶體分配 一個由C/C++編譯的程式佔用的記憶體分為以下幾個部分 1、棧區(stack)― 由編譯器自動分配釋放 ,存放函式的引數值,區域性變數的值等。其操作方式類似於資料結構中的棧。 2、堆區

C指標動態記憶體分配(編寫calloc函式,函式內部使用malloc函式來獲取記憶體)

1、問題 編寫calloc函式,函式內部使用malloc函式來獲取記憶體 2、程式碼實現 #include <stdio.h> #include <stdlib.h&

memcpy memmove區別實現(如何處理記憶體重疊問題)

memcpy與memmove的目的都是將N個位元組的源記憶體地址的內容拷貝到目標記憶體地址中。 但當源記憶體和目標記憶體存在重疊時,memcpy會出現錯誤,而memmove能正確地實施拷貝,但這也增加了一點點開銷。 memmove的處理措施: (1)當源記憶體的首地址等於目標記憶體的首地址時,不進行任何拷貝

記憶體管理

關於程式的執行,不得不提到記憶體方面的內容,那麼首先就對一個程序虛擬地址空間的佈局用一張圖來看清楚 這張圖基於32位Linux系統,即起始地址為0x08048000,可以看到順序為只讀段(程式碼段等)、讀寫段(資料段、bss段等)、堆(向上即高地址擴充套件)、用於堆擴充套件的未使用空間、動態庫的對

程式的記憶體分配棧的區別

堆疊概述   在計算機領域,堆疊是一個不容忽視的概念,堆疊是兩種資料結構。堆疊都是一種資料項按序排列的資料結構,只能在一端(稱為棧頂(top))對資料項進行插入和刪除。在微控制器應用中,堆疊是個特殊的儲存區,主要功能是暫時存放資料和地址,通常用來保護斷點和現場

C語言模擬實現strncpystrncpystrncat、strstrstrrstr函式實現

以下是我用C語言模擬實現的部分字串函式: 1、strncpy函式的實現 #include<stdio.h> #include<assert.h> #include<

作業系統記憶體管理 ---堆棧的區別

一、預備知識—程式的記憶體分配 一個由C/C++編譯的程式佔用的記憶體分為以下幾個部分 (從上到下,從記憶體高地址到記憶體低地址) 1、棧區(stack) — 由編譯器自動分配釋放 ,存放函式的引數值,區域性變數的值等。其操作方式類似於資料結構中的棧。

strcpystrncpy、strncpy_ssnprintf

1、strcpy 原型宣告: extern char *strcpy(char* dest, const char *src); 依據源串的\0作為結束判斷的,不會檢查需要拷貝的緩衝區的大小,如果目標空間不夠,就有溢位問題。 2、strncpy 原型 char*

Linux下程序記憶體管理mallocsbrk

之前自己突發興趣想寫一下malloc函式,順便了解一下程序的記憶體管理。在寫的過程中發現其實malloc只不過是通過呼叫Linux下的sbrk函式來實現記憶體的分配,只是在sbrk之上加了一層對所分配的記憶體的管理罷了,而sbrk以及brk是實現從虛擬記憶體到記憶體的對映的

記憶體管理malloc、free、callocrealloc

記憶體區域可以分為棧,堆,靜態儲存區和常量儲存區。區域性變數,函式形參,臨時變數都是在棧上獲得記憶體的,它們獲取的方式都是由編譯器自動執行的。 C 標準函式庫提供了許多函式來實現對堆上記憶體管理,其中包括:malloc函式,free函式,calloc函式和realloc函式

資料結構預備知識指標,結構體動態記憶體的分配與釋放

資料結構的整體框架: 資料結構只解決儲存問題,演算法解決操作問題。演算法依附於儲存結構,儲存不同,演算法不同。 衡量演算法的標準: 時間複雜度:執行的次數而非時間空間複雜度:佔用的記憶體難易程度健壯性 1.預備知識之指標 記憶體是CPU唯一可以直接訪問的大容量儲存區域,

mem系列函式(memset memcpy memmove) str系列函式(strlen strcpy strcmp strcat strstr strtok)

 void *memset(void *s, int ch, size_t n);  函式解釋:將s中前n個位元組 (typedef unsigned int size_t )用 ch 替換並返回 s 。  memset:作用是在一段記憶體塊中填充某個給定的值,它是對較大的

strcpy(字串複製)memcpy記憶體複製)

strcpy和memcpy都是標準C庫函式,它們有下面的特點。 strcpy提供了字串的複製。即strcpy只用於字串複製,並且它不僅複製字串內容之外,還會複製字串的結束符。 已知strcpy函式的原型是:char* strcpy(char* dest, const