C標准庫學習


前言

C標准庫源碼可通過下列兩個網站進行查看:The GNU C LibraryWelcome to uClibc-ng! - Embedded C library

以下學習記錄也是以這兩個網站提供的庫函數源碼進行學習的。

字符串相關

strcpy()函數

頭文件:#include <string.h>

函數原型:char *strcpy(char *dest, const char *src);

函數描述:將src指向的字符串拷貝到dest,包括結束符'\0'。字符串不能重疊,並且dest有足夠的空間接收拷貝的字符串。

返回值:返回指向dest存放字符串的指針。

函數原型:

char *strcpy(char *dest, const char *src)
{
	char *dst = dest;

	while ((*dst = *src) != '\0') {
		src++;
		dst++;
	}

	return dest;
}

可以看出,函數中並不會檢查dest的空間大小,只會拷貝字符串直到遇到src字符串的結束符'\0',因此dest的空間大小需要用戶保證。

測試用例一:dest空間大於src指向的字符串個數。

#include <stdio.h>
#include <string.h>

int main(void)
{
    char *str_original = "0123456789";
    char buf_dest[12] = {0};
    char *ret = NULL;
    int i = 0;

    for (i = 0; i < sizeof(buf_dest); i++)
        buf_dest[i] = 1;

    printf("dest:0x%x\n", buf_dest);
    ret = strcpy(buf_dest, str_original);
    printf(" ret:0x%x\n", ret);
    for (i = 0; i < sizeof(buf_dest); i++)
        printf("%d ", buf_dest[i]);
    printf("\n");

    return 0;
}

編譯,運行結果:

$ ./a.out 
dest:0xca8e26c0
 ret:0xca8e26c0
48 49 50 51 52 53 54 55 56 57 0 1 

可以看出,字符串拷貝的時候會拷貝字符串結束符'\0'。

測試用例二:dest空間小於src指向的字符串個數(錯誤用法)。

#include <stdio.h>
#include <string.h>

int main(void)
{
    char *str_original = "0123456789";
    char buf_dest[5] = {0};
    char *ret = NULL;
    int i = 0;

    for (i = 0; i < sizeof(buf_dest); i++)
        buf_dest[i] = 1;

    printf("dest:0x%x\n", buf_dest);
    ret = strcpy(buf_dest, str_original);
    printf(" ret:0x%x\n", ret);
    printf("%s\n", buf_dest);

    return 0;
}

編譯,運行:

$ ./a.out 
dest:0xe31dee10
 ret:0xe31dee10
01234567

由於dest沒有以'\0'結尾,因此printf打印的信息是錯誤的,訪問的內容已經超過了dest的空間。

測試用例三:內存重疊

參考博客:strcpy函數的實現 - Norcy - 博客園 (cnblogs.com)

#include <stdio.h>
#include <string.h>

int main(void)
{
    char str[12] = "hello";
    strcpy(str + 1, str);  //存在段錯誤
    printf("%s\n", str);

    return 0;
}
#include <stdio.h>
#include <string.h>

int main(void)
{
    char str[12] = "hello";

    strcpy(str, str+1);
    printf("%s\n", str);   //打印輸出ello

    return 0;
}

第一種情況:strcpy(str + 1, str),這種情況下dest指向'e',而src第一個字符為'h',當拷貝的時候,'\0'結束符會被覆蓋掉,導致一直拷貝,陷入死循環。

第二種情況:strcpy(str, str+1),這種情況下,僅拷貝"ello"。

strncpy()函數

頭文件:#include <string.h>

函數原型:char *strncpy(char *dest, const char *src, size_t n);

函數描述:功能和strcpy函數類似,但僅拷貝src的n字節給dest。另外如果n字節中沒有結束符'\0',那么拷貝完后,dest中是不會有結束符的,這個需要注意。如果src的長度小於n字節,將會在拷貝src字符串之后繼續拷貝結束符給dest,直到滿足n字節。

函數原型:

char *strncpy (char *s1, const char *s2, size_t n)
{
    reg_char c;
    char *s = s1;

    --s1;

    if (n >= 4) 
    {
        size_t n4 = n >> 2;
        for ( ; ; )
        {
            c = *s2++;
            *++s1 = c;
            if (c == '\0')
                break;
            c = *s2++;
            *++s1 = c;
            if (c == '\0')
                break;
            c = *s2++;
            *++s1 = c;
            if (c == '\0')
                break;
            c = *s2++;
            *++s1 = c;
            if (c == '\0')
                break;
            if (--n4 == 0)
                goto last_chars;
        }
        n = n - (s1 - s) - 1;
        if (n == 0)
            return s;
        goto zero_fill;
    }

last_chars:
    n &= 3;
    if (n == 0)
        return s;

    do
    {
        c = *s2++;
        *++s1 = c;
        if (--n == 0)
            return s;
    } while (c != '\0');

zero_fill:
    do
        *++s1 = '\0';
    while (--n > 0);

    return s;
}

測試用例一:src字符串長度大於n,且前n個字符中存在結束符,則只會拷貝到第一個結束符時就結束拷貝。

#include <stdio.h>
#include <string.h>

int main(void)
{
    char str_original[] = {'a', '\0', 'b', '\0', 'c', 'd', 'e', '\0'};
    char str_dest[8] = {0};
    int i = 0;

    strncpy(str_dest, str_original, 8);
    for (i = 0; i < 12; i++)
        printf("%d ", str_dest[i]);
    printf("\n");

    return 0;
}

編譯,運行:

$ ./a.out    
97 0 0 0 0 0 0 0 

測試用例二:src字符串長度大於n,且前n個字符中不存在結束符,則會拷貝n個字符。處理這種情況,一般需要自己去在dest末尾添加一個結束符。

#include <stdio.h>
#include <string.h>

int main(void)
{
    char str_original[] = {'a', 'b', 'c', 'd', 'e', 'f', '\0'};
    char str_dest[5] = {0};
    int i = 0;

    strncpy(str_dest, str_original, 5);
    for (i = 0; i < 5; i++)
        printf("%d ", str_dest[i]);
    printf("\n");

    return 0;
}

編譯,運行:

$ ./a.out 
97 98 99 100 101 

測試用例三:src字符串長度小於n,將會在拷貝src字符串之后繼續拷貝結束符給dest,直到滿足n字節。

#include <stdio.h>
#include <string.h>

int main(void)
{
    char str_original[] = {'a', 'b', 'c', 'd', 'e', 'f', '\0'};
    char str_dest[12] = {0};
    int i = 0;

    for (i = 0; i < 12; i++)
        str_dest[i] = 1;

    strncpy(str_dest, str_original, 12);
    for (i = 0; i < 12; i++)
        printf("%d ", str_dest[i]);
    printf("\n");

    return 0;
}

編譯,運行:

$ ./a.out    
97 98 99 100 101 102 0 0 0 0 0 0 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM