最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C語言安全的進行字符串拷貝的幾種方法

 更新時間:2026年06月16日 08:56:35   作者:碼農(nóng)愛學習  
在C語言編程中,字符串是通過定義char數(shù)組來保存,并通過指針以及字符串拷貝函數(shù)來實現(xiàn)字符串的拷貝,在字符串長度不太明確的情況下,可能處出現(xiàn)拷貝越界的情況,給程序引入了不安全的因素,本篇就來討論下,在C語言中,如何安全的使用strcpy這類函數(shù)進行字符串的拷貝

引言

在C語言編程中,字符串是通過定義char數(shù)組來保存,并通過指針以及字符串拷貝函數(shù)(如strcpy)來實現(xiàn)字符串的拷貝,無法方便的像C++中使用等號直接對std::string類型的字符串賦值。

使用strcpy這類接口,在字符串長度不太明確的情況下,可能處出現(xiàn)拷貝越界的情況,給程序引入了不安全的因素。本篇就來討論下,在C語言中,如何安全的使用strcpy這類函數(shù)進行字符串的拷貝。

1 strcpy

strcpy是C語言標準庫中的一個最基礎的字符串處理函數(shù),可以把源字符串復制到目標字符串。

1.1 函數(shù)原型

char *strcpy(char *dest, const char *src);

src 所指向的以空字符('\0')結尾的字符串復制到 dest 所指向的數(shù)組中,同時會復制終止空字符

參數(shù)

  • dest 是目標字符串的指針
  • src 是源字符串的指針

返回值

  • 返回目標字符串的指針

1.2 使用示例

一個基礎的strcpy使用示例,需要確保目標數(shù)組足夠大

// gcc strcpy1.c -o strcpy
#include <stdio.h>
#include <string.h>

int main() 
{
    char src[] = "Hello";
    char dest[10] = {0}; // 確保目標數(shù)組足夠大

    strcpy(dest, src);
    printf("復制后的字符串: %s\n", dest);
    
    for (int i=0; i<sizeof(dest); i++)
    {
        printf("dest[%d]:%x(%c)\n", i, dest[i], dest[i]);
    }

    return 0;
 }

運行結果如下:

注意事項

  1. 目標數(shù)組大小要足夠strcpy 不會檢查目標數(shù)組的大小,一旦目標數(shù)組的空間不足以容納源字符串,就會導致緩沖區(qū)溢出,這是非常危險的,可能會引發(fā)程序崩潰或者產(chǎn)生安全漏洞。
  2. 源字符串必須以空字符結尾:如果源字符串沒有以 '\0' 結尾,strcpy 會一直復制內(nèi)存中的數(shù)據(jù),直到遇到空字符為止,這會造成未定義行為。
  3. 避免自我復制:不要將字符串復制到自身,否則會導致數(shù)據(jù)被破壞。

1.3 拷貝越界情況舉例

測試一下,如果要拷貝的字符串長度大于目標存儲空間,會是什么結果。

// gcc strcpy2.c -o strcpy2
#include <stdio.h>
#include <string.h>

int main() 
{
    char src[] = "Hello, World"; //原字符串11個字符,再加上結尾'\0'則占12個字符
    char dest[10] = {0}; //目標存儲空間只有10個
    char other[10] = {0}; //隨后再定一個10個大小的other來驗證是否被越界拷貝了

    strcpy(dest, src);
    printf("復制后的字符串: %s\n", dest);
    
    printf("dest addr:%p\n", dest);
    for (int i=0; i<sizeof(dest); i++)
    {
        printf("dest[%d]:%x(%c)\n", i, dest[i], dest[i]);
    }
    
    printf("other addr:%p\n", other);
    size_t bytes = (char*)other - (char*)dest;
    printf("other (other-dest):%zu\n", bytes);
    for (int i=0; i<sizeof(other); i++)
    {
        printf("other[%d]:%x(%c)\n", i, other[i], other[i]);
    }

    return 0;
 }

運行結果如下:

需要注意的是,雖然dest字符串通過printf正常輸出了,但實際是拷貝字符時,越界拷貝,雖然這里暫時沒有問題,但other中的內(nèi)容被篡改,如果后續(xù)需要使用other中的內(nèi)容,可能就會出現(xiàn)不符合預期的結果。

1.4關于是否會加上結尾符的驗證

// gcc strcpy3.c -o strcpy3
#include <stdio.h>
#include <string.h>

int main() 
{
    char src[] = "Hello";
    char dest[10] = {0}; // 確保目標數(shù)組足夠大

    strcpy(dest, "12345abcd");
    printf("復制后的字符串: %s\n", dest);
    
    strcpy(dest, src);
    printf("復制后的字符串: %s\n", dest);
    
    for (int i=0; i<sizeof(dest); i++)
    {
        printf("dest[%d]:%x(%c)\n", i, dest[i], dest[i]);
    }
    
    //再測試一下char數(shù)組逐個賦值后的拷貝(確保src2當做字符串時最后沒有‘\0')
    char src2[2];
    src2[0] = 'm';
    src2[1] = 'n';
    strcpy(dest, src2);
    printf("復制后的字符串: %s\n", dest);
    for (int i=0; i<sizeof(dest); i++)
    {
        printf("dest[%d]:%x(%c)\n", i, dest[i], dest[i]);
    }

    return 0;
 }

運行結果如下:

可以看到:

  • 拷貝src字符串時,"Hello"被正??截?,后面一個’\0’也拷貝了,dest中剩下的則保持原樣
  • 拷貝char src2[2]時,由于src2沒有自動被賦予的’\0’字符串結尾符,在strcpy賦值時,就只是復制了2個char數(shù)據(jù)本身,所有在printf打印時,就和后面的數(shù)據(jù)一起打印出來了,直到遇到了’\0’字符。
  • 另外有點特殊的是,dest原有的內(nèi)容,被整體向后移動了。

2 strncpy

strncpy是 strcpy的安全版本,用于復制指定長度的字符串。

2.1 函數(shù)原型

char *strncpy(char *dest, const char *src, size_t n);

src 的前 n 個字符復制到 dest,不自動添加終止符 '\0'(除非 src 的長度小于 n)。

參數(shù)

  • dest:目標字符串指針(需提前分配足夠空間)。
  • src:源字符串指針(必須以 '\0' 結尾)。
  • n:最多復制的字符數(shù)。

返回值

  • 返回目標字符串的指針,也就是dest。

它會復制最多 n 個字符,能夠防止緩沖區(qū)溢出。

不過要注意,如果源字符串的長度超過 n,dest 數(shù)組將不會以空字符結尾。

2.2 使用示例

// gcc strncpy1.c -o strncpy1
#include <stdio.h>
#include <string.h>

int main() 
{
    char src[] = "Hello";
    char dest[10] = {0}; // 確保目標數(shù)組足夠大

    int destSize = sizeof(dest);
    strncpy(dest, src, destSize-1); //最多只能復制destSize-1, 因為要加上結尾符
    dest[destSize] = '\0'; //手動加上結尾符
    printf("復制后的字符串: %s\n", dest);
    
    for (int i=0; i<sizeof(dest); i++)
    {
        printf("dest[%d]:%x(%c)\n", i, dest[i], dest[i]);
    }

    return 0;
 }

運行結果如下:

關鍵注意事項

是否自動添加終止符:

  • 如果 src 的長度 小于 n,strncpy 會在復制完 src 后,在 dest 中填充 '\0' 直到 n 個字符。
  • 如果 src 的長度 大于等于 n,dest 不會自動添加終止符,此時需要手動添加 dest[n-1] = '\0',否則可能導致字符串處理異常。

避免緩沖區(qū)溢出

n 應不超過 dest 的大小(包括終止符空間)。

例如:char dest[5]; strncpy(dest, src, 5); 可能導致溢出(因為 dest 的有效空間只有 4 個字符 + 1 個終止符)

2.3 拷貝越界被截斷的情況舉例

// gcc strncpy2.c -o strncpy2
#include <stdio.h>
#include <string.h>

int main() 
{
    char src[] = "Hello, World"; //原字符串11個字符,再加上結尾'\0'則占12個字符
    char dest[10] = {0}; //目標存儲空間只有10個
    char other[10] = {0}; //隨后再定一個10個大小的other來驗證是否被越界拷貝了

    int destSize = sizeof(dest);
    strncpy(dest, src, destSize-1);
    dest[destSize] = '\0';
    printf("復制后的字符串: %s\n", dest);
    
    printf("dest addr:%p\n", dest);
    for (int i=0; i<sizeof(dest); i++)
    {
        printf("dest[%d]:%x(%c)\n", i, dest[i], dest[i]);
    }
    
    printf("other addr:%p\n", other);
    size_t bytes = (char*)other - (char*)dest;
    printf("other (other-dest):%zu\n", bytes);
    for (int i=0; i<sizeof(other); i++)
    {
        printf("other[%d]:%x(%c)\n", i, other[i], other[i]);
    }

    return 0;
 }

運行結果如下:

可以看到,使用strnpy,通過指定拷貝的長度:

  • 數(shù)據(jù)如果超出長度,則被截斷,并且有結尾符
  • 確保了其它數(shù)據(jù)不被越界訪問

2.4 不規(guī)范的使用舉例

// gcc strncpy3.c -o strncpy3
#include <stdio.h>
#include <string.h>

int main() 
{
    char src[] = "Hello";
    char dest[10] = {0}; //拷貝的目標位置
    char other[10] = "xy"; //隨后一個位置用于測試
    printf("other[0]: %c\n", other[0]);
    
    printf("dest addr:%p\n", dest);
    printf("other addr:%p\n", other);
    size_t bytes = (char*)other - (char*)dest;
    printf("other (other-dest):%zu\n", bytes); //確認other就是在dest之后

    int destSize = sizeof(dest);
    //不規(guī)范1:這里的原字符串長度大于destSize,并且沒有手動添加字'\0'結尾符
    strncpy(dest, "12345abcdefg", destSize);
    printf("復制后的字符串: %s\n", dest); //dest與后面的other連成一個字符串了!
    printf("other[0]: %c\n", other[0]); //由于strncpy拷貝了destSize,后面的other沒有影響
    
    //不規(guī)范2:這次拷貝的字符串長度小于destSize, 并且沒有手動添加字'\0'結尾符
    strncpy(dest, src, 5);
    printf("復制后的字符串: %s\n", dest);
    
    for (int i=0; i<sizeof(dest); i++)
    {
        printf("dest[%d]:%x(%c)\n", i, dest[i], dest[i]);
    }
    
    int srcSize = sizeof(src); //注意src的sizeof是包含了之后的'\0'結尾符的,所以是6
    int srcLen = strlen(src); 
    printf("srcSize:%d, srcLen:%d\n", srcSize, srcLen);
    strncpy(dest, src, srcSize);
    printf("復制后的字符串: %s\n", dest);
    
    for (int i=0; i<sizeof(dest); i++)
    {
        printf("dest[%d]:%x(%c)\n", i, dest[i], dest[i]);
    }

    return 0;
 }

運行結果如下:

strncpy的安全性只是在于拷貝的長度可控,避免越界訪問。

但當要拷貝的數(shù)據(jù)長度大于目標空間時,數(shù)據(jù)被截斷,但沒有提示,如果直接使用截斷的字符串,也會出現(xiàn)意想不到的的問題。

3 strlcpy

strlcpy是一個更安全的字符串復制函數(shù),并可以返回實際拷貝的長度。

不過需要注意的是,strlcpy 并非標準 C 庫函數(shù),而是在 BSD 系統(tǒng)(如 macOS)中存在,

Linux 系統(tǒng)需要通過 #include <bsd/string.h> 引入,并額外鏈接 libbsd,另外也要安裝一下這個庫:

sudo apt-get install libbsd-dev

3.1 函數(shù)原型

size_t strlcpy(char *dest, const char *src, size_t size);

src 的內(nèi)容復制到 dest,確保 dest'\0' 結尾,并返回 src 的原始長度(不包含終止符)。

參數(shù)

  • dest:目標字符串指針(需提前分配空間)。
  • src:源字符串指針(必須以 '\0' 結尾)。
  • sizedest 的最大容量(包括終止符 '\0')。

返回值

  • src 的實際長度(不包含 '\0')。若返回值 ≥ size,說明復制時發(fā)生了截斷。

3.2 使用示例

// gcc strlcpy.c -o strlcpy -lbsd
#include <stdio.h>
#include <string.h>
#include <bsd/string.h>  // Linux需要顯式包含

int main() 
{
    char src[] = "Hello, World";
    char dest[10] = {0}; 
    char other[10] = {0};

    size_t len = strlcpy(dest, src, sizeof(dest));
    printf("strlcpy ret:%zu\n", len);
    if (len > sizeof(dest))
    {
        printf("copy oversize!\n");
    }
    printf("復制后的字符串: %s\n", dest);
    
    printf("dest addr:%p\n", dest);
    for (int i=0; i<sizeof(dest); i++)
    {
        printf("dest[%d]:%x(%c)\n", i, dest[i], dest[i]);
    }
    
    printf("other addr:%p\n", other);
    size_t bytes = (char*)other - (char*)dest;
    printf("other (other-dest):%zu\n", bytes);
    for (int i=0; i<sizeof(other); i++)
    {
        printf("other[%d]:%x(%c)\n", i, other[i], other[i]);
    }

    return 0;
 }

運行結果:

strlcpy的特性

自動處理終止符

  • strlcpy 會確保 dest'\0' 結尾,即使 src 被截斷。
  • 最多復制 size - 1 個字符到 dest,并在末尾添加 '\0'

安全避免溢出

  • 無論 src 多長,dest 都不會溢出。
  • src 長度 ≥ size,函數(shù)會截斷 src,僅復制 size - 1 個字符。

返回值的用途

  • 返回src的實際長度,可用于檢測截斷情況:
if (strlcpy(dest, src, size) >= size) {
    printf("警告:源字符串被截斷!\n");
}

4 上述3種函數(shù)對比

函數(shù)是否自動添加終止符溢出風險截斷處理
strcpy??不截斷,可能溢出
strncpy??截斷但不補終止符
strlcpy??截斷并補終止符
  • strcpy使用簡單,在數(shù)據(jù)長度不確定的情況下使用會有風險
  • strnpy指定了拷貝長度嗎,但仍有數(shù)據(jù)被截斷的風險
  • strlcpy可以通過返回值檢測是否被截斷,但需要額外庫的支持

5 自定義strlcpy

基于上述分析,可以對strnpy進行自定義封裝改造,實現(xiàn)類似strlcpy,這樣也不需要額外庫的支持。

5.1 對strnpy進行封裝實現(xiàn)自定義檢查

int my_strlcpy(char *dst, const char *src, size_t dstSize) 
{
    size_t srcLen = strlen(src);
    
    if (NULL == dst || NULL == src || 0 == dstSize)
    {
        return -1; //參數(shù)錯誤
    }
    
    size_t maxCopyLen = dstSize - 1;
    strnpy(dst, src, maxCopyLen);
    dst[len] = '\0';
    
    if (srclen > maxCopyLen)
    {
        printf("%d > %d, copy oversize! only copy:%s\n", srclen, maxCopyLen, dst);
        return -2; //數(shù)據(jù)被截斷
    }
    
    retun 0; //正??截?
}

5.2 測試驗證

// gcc my_strlcpy.c -o my_strlcpy
#include <stdio.h>
#include <string.h>

int my_strlcpy(char *dst, const char *src, size_t dstSize) 
{
    size_t srcLen = strlen(src);
    
    if (NULL == dst || NULL == src || 0 == dstSize)
    {
        return -1; //參數(shù)錯誤
    }
    
    size_t maxCopyLen = dstSize - 1;
    strncpy(dst, src, maxCopyLen);
    dst[maxCopyLen] = '\0';
    
    if (srcLen > maxCopyLen)
    {
        printf("%zu > %zu, copy oversize! only copy:%s\n", srcLen, maxCopyLen, dst);
        return -2; //數(shù)據(jù)被截斷
    }
    
    return 0; //正??截?
}

int main() 
{
    char src[] = "Hello, World";
    char dest[10] = {0}; 
    char other[10] = {0};

    int ret = my_strlcpy(dest, src, sizeof(dest));
    if (ret)
    {
        printf("err! my_strlcpy ret:%d\n", ret);
    }
    printf("復制后的字符串: %s\n", dest);
    
    printf("dest addr:%p\n", dest);
    for (int i=0; i<sizeof(dest); i++)
    {
        printf("dest[%d]:%x(%c)\n", i, dest[i], dest[i]);
    }
    
    printf("other addr:%p\n", other);
    size_t bytes = (char*)other - (char*)dest;
    printf("other (other-dest):%zu\n", bytes);
    for (int i=0; i<sizeof(other); i++)
    {
        printf("other[%d]:%x(%c)\n", i, other[i], other[i]);
    }

    return 0;
 }

運行結果:

6 總結

本篇介紹了C語言中如何安全的進行字符串拷貝,首先測試了在使用strcpy、strncpy、strlcpy進行字符串拷貝時可能遇到的問題,然后對比這兩種方式的基礎差別,最后通過自定義封裝strncpy來實現(xiàn)安全拷貝字符串的功能。

以上就是C語言安全的進行字符串拷貝的幾種方法的詳細內(nèi)容,更多關于C語言進行字符串拷貝的資料請關注腳本之家其它相關文章!

相關文章

  • C++設計模式之迭代器模式

    C++設計模式之迭代器模式

    這篇文章主要介紹了C++設計模式之迭代器模式,本文講解了什么是迭代器模式、迭代器模式的代碼實例等內(nèi)容,需要的朋友可以參考下
    2014-10-10
  • Qt實現(xiàn)驗證碼相關功能的代碼示例

    Qt實現(xiàn)驗證碼相關功能的代碼示例

    驗證碼的原理基于人類視覺和計算機視覺的差異性,通過給用戶顯示一些難以被機器識別的圖形或文字,讓用戶進行人機交互,確認自己的身份,這樣可以有效保護網(wǎng)站安全,所以本給大家介紹了Qt實現(xiàn)驗證碼相關功能的代碼示例,感興趣的朋友可以參考下
    2024-01-01
  • C程序結構的入門

    C程序結構的入門

    在我們學習 C 語言的基本構建塊之前,讓我們先來看看一個最小的 C 程序結構,在接下來的章節(jié)中可以以此作為參考
    2021-06-06
  • C語言數(shù)組和指針,內(nèi)存之間的關系

    C語言數(shù)組和指針,內(nèi)存之間的關系

    這篇文章主要介紹了C語言數(shù)組和指針,內(nèi)存之間的關系,首先論證一維數(shù)組和一級指針之前的關系,我們常常使用一級指針指針的方式訪問一維數(shù)組,只有對內(nèi)存的理解到位才能理解它們直接的關系。需要的小伙伴可以參考一下
    2022-02-02
  • C++鏈表實現(xiàn)通訊錄設計

    C++鏈表實現(xiàn)通訊錄設計

    這篇文章主要為大家詳細介紹了C++鏈表實現(xiàn)通訊錄設計,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-06-06
  • C++和java設計模式之單例模式

    C++和java設計模式之單例模式

    這篇文章主要為大家詳細介紹了C++和java設計模式之單例模式的相關資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-12-12
  • C++ vector從入門到模擬實現(xiàn)過程

    C++ vector從入門到模擬實現(xiàn)過程

    這段文章詳細講解了C++ STL中的vector容器,從其優(yōu)勢、基礎刪查改操作到迭代器失效等,并提供了模擬實現(xiàn)和常見陷阱的解析,幫助開發(fā)者全面掌握vector的用法,重點強調(diào)了reserve的用法、迭代器失效的處理和深拷貝、淺拷貝的區(qū)別,感興趣的朋友一起看看吧
    2026-06-06
  • c++11中關于std::thread的join的詳解

    c++11中關于std::thread的join的詳解

    這篇文章主要介紹了c++11中關于std::thread的join詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-03-03
  • 關于VS2019 C++項目同時出現(xiàn)LNK2005 和LNK1169 error 的解決辦法

    關于VS2019 C++項目同時出現(xiàn)LNK2005 和LNK1169 error 的解決辦法

    這篇文章主要介紹了關于VS2019 C++項目同時出現(xiàn)LNK2005 和LNK1169 error 的解決辦法,本文給大家介紹的非常詳細,對大家的學習工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-04-04
  • Matlab實現(xiàn)將圖像序列合并為視頻的方法詳解

    Matlab實現(xiàn)將圖像序列合并為視頻的方法詳解

    MATLAB是一種高性能語言,用于操縱矩陣、執(zhí)行技術計算、繪圖等。它代表矩陣實驗室。借助這個軟件,我們可以從圖像中創(chuàng)建視頻。這篇文章主要介紹了Matlab實現(xiàn)將圖像序列合并為視頻的四個方法,希望對大家有所幫助
    2023-03-03

最新評論

贵南县| 齐齐哈尔市| 鹿邑县| 建湖县| 大连市| 大连市| 天峨县| 平原县| 泰来县| 三原县| 双辽市| 鹿邑县| 九寨沟县| 永靖县| 深圳市| 信阳市| 集贤县| 商水县| 资源县| 黑龙江省| 贵州省| 会理县| 门源| 武夷山市| 宁城县| 阿拉善盟| 桦甸市| 黎城县| 砚山县| 紫金县| 峨眉山市| 营口市| 榆林市| 乾安县| 富平县| 蒙阴县| 原阳县| 邳州市| 英山县| 新余市| 吉首市|