C++中字符串拷貝函數(shù)strcpy的實現(xiàn)與優(yōu)化指南
C++中,有個常見的字符串拷貝函數(shù):char* strcpy(char* dst, const char* src), 作用是將1個字符串src,拷貝到另外一個字符串dst。需要考慮的邊界條件如下:
- 空指針:dst或src為NULL時會觸發(fā)非法內(nèi)存訪問。
- 內(nèi)存重疊:dst和src的地址范圍存在重疊,從前向后拷貝會覆蓋尚未讀取的src內(nèi)容。
- 空字符串:src僅包含’\0’,需保證拷貝后dst仍然是合法空串。
- 緩沖區(qū)溢出:dst空間不足導(dǎo)致拷貝越界,是嚴重的安全漏洞。
- 結(jié)束符丟失:必須保證拷貝后的dst以’\0’結(jié)尾,否則后續(xù)strlen/printf等字符串操作會越界。
1 基本版本
#include <cassert>
// 對齊標準strcpy簽名,src加const保證常量正確性
char* my_strcpy(char* dst, const char* src) {
// 調(diào)試期斷言快速定位空指針問題,發(fā)布模式可關(guān)閉斷言不影響性能
assert(dst != nullptr && src != nullptr);
// 保存目標地址首指針,后續(xù)會移動dst指針
char* ret = dst;
// 拷貝邏輯:賦值表達式返回賦值后的值,遇到'\0'時循環(huán)終止,自動包含結(jié)束符
while ((*dst++ = *src++) != '\0');
return ret;
}
1.1 優(yōu)點
實現(xiàn)了核心功能,適合調(diào)用方能保證參數(shù)合法性的高性能場景。
1.2 缺點
- 若發(fā)布模式關(guān)閉斷言,則傳入空指針,會觸發(fā)未定義行為。
- 沒有處理內(nèi)存重疊場景,若dst落在src的地址范圍內(nèi)且dst > src,會導(dǎo)致拷貝內(nèi)容錯誤。
- 沒有檢查dst緩沖區(qū)大小,極易出現(xiàn)溢出。
2 進階版(支持內(nèi)存重疊)
參考memmove的語義,處理重疊場景,特別存在地址重疊的情況(如字符串內(nèi)部移動)
#include <cstddef> // size_t定義
char* my_strcpy(char* dst, const char* src) {
// 發(fā)布期也保留空指針檢查,避免程序崩潰
if (dst == nullptr || src == nullptr) {
return nullptr;
}
char* ret = dst;
// 第一步:計算src總長度(包括末尾'\0')
size_t len = 0;
while (src[len] != '\0') len++;
len += 1; // 包含結(jié)束符的總拷貝字節(jié)數(shù)
// 第二步:判斷內(nèi)存重疊,選擇拷貝方向
if (dst < src || dst >= src + len) {
// 無重疊 / dst在src前面,從前向后拷貝性能更高
for (size_t i = 0; i < len; i++) {
dst[i] = src[i];
}
} else {
// 重疊且dst在src后面,從后向前拷貝避免覆蓋未讀取的src內(nèi)容
// 注意:size_t是無符號類型,禁止寫i>=0(永遠為真),改為從len倒序到1
for (size_t i = len; i > 0; i--) {
dst[i-1] = src[i-1];
}
}
return ret;
}
2.1 特點
- 即使參數(shù)為空也返回nullptr,避免程序崩潰。
- 正確處理所有內(nèi)存重疊場景,例如dst = src + 2這類后向重疊場景,也能得到正確結(jié)果。
- 仍然依賴調(diào)用方保證src以’\0’結(jié)尾、dst緩沖區(qū)足夠大。
3 高級版(緩存區(qū)防護)
在實際開發(fā)中,禁止使用無長度限制的strcpy,推薦實現(xiàn)帶緩沖區(qū)長度的安全版本,徹底避免溢出:
#include <cstddef>
char* my_strcpy_s(char* dst, size_t dst_size, const char* src) {
if (dst == nullptr || src == nullptr || dst_size == 0) {
return nullptr;
}
size_t i = 0;
// 最多拷貝dst_size-1個字符,預(yù)留1個位置給'\0'
while (src[i] != '\0' && i < dst_size - 1) {
dst[i] = src[i];
i++;
}
// 強制加結(jié)束符,保證dst永遠是合法C字符串(解決strncpy不自動加'\0'的缺陷)
dst[i] = '\0';
return dst;
}
3.1 特點
- 完全避免緩沖區(qū)溢出,即使src遠長于dst的空間,也只會截斷到緩沖區(qū)可容納的最大長度。
- 始終保證dst以’\0’結(jié)尾,不會出現(xiàn)非法字符串。
4 測試案例
| 測試場景 | 用例代碼 | 預(yù)期結(jié)果 |
|---|---|---|
| 普通字符串拷貝 | char dst[20]; my_strcpy(dst, “hello world”); | dst內(nèi)容為"hello world" |
| 空字符串拷貝 | char dst[10]; my_strcpy(dst, “”); | dst[0] == ‘\0’ |
| 自拷貝 | char buf[] = “test”; my_strcpy(buf, buf); | buf內(nèi)容不變,仍為"test" |
| 后向內(nèi)存重疊 | char buf[20] = “abcdefgh”; my_strcpy(buf+2, buf); | buf內(nèi)容為"ababcdefgh"(基礎(chǔ)版會得到"abababab…"錯誤結(jié)果) |
| 空參數(shù)傳入 | my_strcpy(nullptr, “test”); | 返回nullptr,程序不崩潰 |
| 緩沖區(qū)不足(安全版) | char dst[5]; my_strcpy_s(dst, 5, “hello world”); | dst內(nèi)容為"hell",自動截斷并加結(jié)束符 |
5 使用場景
- const 正確性:src參數(shù)必須加const修飾,既可以傳入const字符串,也能避免函數(shù)內(nèi)部誤修改源字符串。
- 指針溢出:嵌入式 / 內(nèi)核開發(fā)中,若src地址接近地址空間上限,src + len可能出現(xiàn)指針溢出,可轉(zhuǎn)為uintptr_t整數(shù)類型后再做范圍比較。
- 性能取舍:如果業(yè)務(wù)不存在重疊場景,基礎(chǔ)版的性能更高;如果優(yōu)先保證安全性,直接使用帶長度的安全版本。
到此這篇關(guān)于C++中字符串拷貝函數(shù)strcpy的實現(xiàn)與優(yōu)化指南的文章就介紹到這了,更多相關(guān)C++字符串拷貝函數(shù)strcpy內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Visual Studio C++指針靠前靠后的問題全面解析
這篇文章主要介紹了Visual Studio C++指針靠前靠后的問題全面解析,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-04-04

