c語言與字串相關的庫函式的模擬實現

QUIET_F發表於2020-11-08

重點梳理

  • 與字串相關的庫函式的介紹
  • 重點與字串相關的庫函式的模擬實現與使用

一、與字串相關的庫函式的介紹

c語言衝提供了許多與字串相關的函式,包括字串的複製、求字串的長度等。本節主要介紹C語言庫函式中與字串相關的庫函式,熟悉這些函式方便以後程式設計時的使用。

1.求字串長度

求字串長度:strlen()

庫函式:size_t strlen ( const char * str )

2.長度不受限制的字串函式

字串複製:strcpy()

庫函式:char * strcpy ( char * destination, const char * source );

字串拼接:strcat()

庫函式:char * strcat ( char * destination, const char * source )

字串比較:strcmp()

庫函式:int strcmp ( const char * str1, const char * str2 );

3.長度受限制的字串函式

以下函式與長度不受限制的字串函式最大的區別就是,下面這些函式操作的字元個數是n個上邊的函式操作的是整個字串。

字串複製:strncpy()

庫函式:char * strncpy ( char * destination, const char * source, size_t num );

字串拼接:strncat()

庫函式:char * strncat ( char * destination, const char * source, size_t num );

字串比較:strcnmp()

庫函式:int strncmp ( const char * str1, const char * str2, size_t num );

4.字串查詢

判斷子串:strstr()

庫函式:const char * strstr ( const char * str1, const char * str2 );

                        char * strstr ( char * str1, const char * str2 );

字串分割:strtok()

5.錯誤資訊報告

錯誤資訊報告:strerror()

6.記憶體操作函式

記憶體操作函式主要指從記憶體中進行操作,比如memmove和memcpy就是記憶體拷貝函式除此之外記憶體操作函式按位元組進行操作,這樣無論是char型別int型別還是其他型別都可以使用,而上面講的那些函式只適用於char型別字串。

記憶體複製:memcpy()

庫函式:void * memcpy ( void * destination, const void * source, size_t num );

memmove()

庫函式:void * memmove ( void * destination, const void * source, size_t num );

memset()

庫函式:void * memset ( void * ptr, int value, size_t num );

記憶體比較:memcmp()

庫函式:int memcmp ( const void * ptr1, const void * ptr2, size_t num );

注:以上函式使用時,都要引入標頭檔案:#include<string.h>,否則可能會出現隱式函式宣告問題(關於隱式函式宣告問題可以檢視部落格:https://blog.csdn.net/qq_47406941/article/details/109330844

二、比較重要的庫函式的模擬實現和使用

 有些庫函式需要我們熟練掌握並熟悉它的實現過程,這些字串無論是在開發還是在找工作面試是都是一個重點,以下內容就是C語言中需要熟練掌握並熟悉實現過程的一些庫函式的模擬實現和使用:

1.strlen()

size_t strlen ( const char * str )

size_t是返回值型別,一般是整型

str是需要計算的字串,其中const表示該字串只可以使用不可以修改。

模擬實現 

//方法一
int my_strlen(const char* str)
{
	//count記錄字串的長度
	int count = 0;
	while (*str++)
		count++;
	return count;
}

//注意:*str++,由於後置++的優先順序高於*的優先順序,所以str先與++結合在於*結合。但,後置++是先使用在++,所以上邊程式碼還可以寫成下面的形式

int my_strlen(const char* str)
{
	//count記錄字串的長度
	int count = 0;
	while (*str)
    {
		count++;
        str++;
    }
	return count;
}

//方法二---遞迴
int my_strlen(const char * str) 
{ 
     if(*str == '\0') 
         return 0; 
     else 
         return 1+my_strlen(str+1); 
}


//方法三---指標
int my_Strlen(const char* str)
{
    char* ret = str;
    while(*ret)
    {
        ret++;
    }
    return ret - str;
}

使用方法

//方法一
char* str = "abac";
strlen(str);

//方法二
strlen("abcac");

2.strcpy()

char * strcpy ( char * destination, const char * source );

返回值型別為char*,destination表示目標字串,source表示源字串

該函式的作用是將源字串複製到目標字串並返回複製後的目標字串。

模擬實現 

char* my_strcpy(char * destination, const char * source)
{
    assert(source);
    assert(destination);
	while (*source)
	{
		*destination = *source;
		source++;
		destination++;
	}
	*destination = '\0';
	return destination;
}

//注意:assert是斷言的意思,確保字串不為空,在使用assert時要引入標頭檔案:#include<assert.h>

使用 

    char str1[] = "Sample string";
	char str2[40];
	char str3[40];
	my_strcpy(str2, str1);
	my_strcpy(str3, "copy successful");
	/*str1: Sample string
	str2 : Sample string
	str3 : copy successful*/
	printf("str1: %s\nstr2: %s\nstr3: %s\n", str1, str2, str3);

 3.strcat()

char * strcat ( char * destination, const char * source )

將源字串拼接到目標字串後,並返回拼接後的目標字串。

模擬實現

char* my_strcat(char * destination, const char * source)
{
	assert(destination != NULL);
	assert(source != NULL);
	char* ret = destination;
	while (*destination)
	{
		destination++;
	}
	while (*source)
	{
		*destination = *source;
		source++;
		destination++;
	}
	*destination = '\0';
	return ret;
}

使用方法

    char str[80];
	strcpy(str, "these ");
	my_strcat(str, "strings ");
	my_strcat(str, "are ");
	my_strcat(str, "concatenated.");
	puts(str);

//輸出結果為:these strings are concatenated.

4.strcmp()

int strcmp ( const char * str1, const char * str2 );

比較字串str1和str2,當str1大於str2返回1,當str1小於str2返回-1,當str1與str2相等返回0;

模擬實現 

//方法一
int my_strcmp(const char * str1, const char * str2)
{
	assert(str1);
	assert(str2);
	while (*str1 && *str2)
	{
		if (*str1 == *str2)
		{
			str1++;
			str2++;
		}
		else
		{
			if (*str1 > *str2)
				return 1;
			else
				return -1;
		}
	}
	if (*str1)
	{
		return 1;
	}
	else
	{
		if (*str1 == *str2)
			return 0;
		else
			return -1;
	}
}

//方法二
int my_strcmp (const char * src, const char * dst) 
{ 
 int ret = 0 ; 
 assert(src != NULL); 
 assert(dest != NULL); 
 while( ! (ret = *(unsigned char *)src - *(unsigned char *)dst) && *dst) 
 ++src, ++dst; 
 if ( ret < 0 ) 
 ret = -1 ; 
 else if ( ret > 0 ) 
 ret = 1 ; 
 return( ret ); 
}

使用方法

char key[] = "apple";
	char buffer[80];
	do {
		printf("Guess my favorite fruit? ");
		fflush(stdout);
		scanf("%79s", buffer);
	} while (my_strcmp(key, buffer) != 0);
	puts("Correct answer!");


//result
Guess my favourite fruit? orange
Guess my favourite fruit? apple
Correct answer!

5.strncpy、strncmp、strncat

這幾個函式與上面幾個類似,只是操作範圍不同。

char * strncpy ( char * destination, const char * source, size_t num );

將源字串source中的num個字元複製到目標字串destination字串,並返回目標字串

char * strncat ( char * destination, const char * source, size_t num );

將源字串中的num個字串拼接到目標字元中,並返回目標字串

int strncmp ( const char * str1, const char * str2, size_t num );

比較str1和str2中的num個字元

模擬實現

char* my_strncpy(char * destination, const char * source, int num)
{
	while (num)
	{
		*destination = *source;
		destination++;
		source++;
		num--;
	}
	*destination = '\0';
	return destination;
}

char str1[] = "To be or not to be";
	char str2[40];
	char str3[40];
	/* copy to sized buffer (overflow safe): */
	my_strncpy(str2, str1, sizeof(str2)-1);
	 /*partial copy (only 5 chars): */
	my_strncpy(str3, str2, 5);
	//str3[5] = '\0';   /* null character manually added */
	puts(str1);
	puts(str2);
	puts(str3);


char * my_strncat(char * destination, const char * source, int num)
{
	char* ret = destination;
	while (*destination)
	{
		destination++;
	}
	while (num)
	{
		*destination = *source;
		destination++;
		source++;
		num--;
	}
	*destination = '\0';
	return ret;
}

char str1[20];
	char str2[20];
	my_strcpy(str1, "To be ");
	my_strcpy(str2, "or not to be");
	my_strncat(str1, str2, 6);
	puts(str1);

6.memcpy()

 void * memcpy ( void * destination, const void * source, size_t num );

描述:Copies the values of num bytes from the location pointed to by source directly to the memory block pointed to by destination.

從源字串中複製num個位元組到目標字串中

注意:為了能過適應更多型別,複製時按位元組複製

 

 模擬實現

void * my_memcpy(void * destination, const void * source, int num)
{
	char* dst = (char*)destination;
	char* src = (char*)source;
	while (num)
	{
		*dst = *src;
		dst++;
		src++;
		num--;
	}
	*dst = '\0';
	return destination;
}

//注意:由於char型別佔用一個位元組,所以將字串強制轉換成char*型別後進行操作,可以實現每次只操作一個字元

//使用
struct {
	char name[40];
	int age;
} person, person_copy;

char myname[] = "Pierre de Fermat";
	/* using memcpy to copy string: */
	my_memcpy(person.name, myname, strlen(myname) + 1);
	person.age = 46;
	/* using memcpy to copy structure: */
	my_memcpy(&person_copy, &person, sizeof(person));
	printf("person_copy: %s, %d \n", person_copy.name, person_copy.age);

7.memmove()

void * memmove ( void * destination, const void * source, size_t num );

描述:Copies the values of num bytes from the location pointed by source to the memory block pointed by destination.

從源字串複製num個位元組到目標字串

注意:看似memmove和memcpy都是從源字串複製num個字元到目標字串,但是兩個函式有很大區別

memcpy函式在進行復制時不考慮重疊問題,而memmove函式考慮重疊問題。關於重疊問題,在下面我們將詳細介紹。

void * memmove ( void * dst, const void * src, size_t count) 
{ 
     void * ret = dst; 
    //前重疊或者不影響結果的後重疊,此時從前往後複製和從後往前複製都可以
     if (dst <= src || (char *)dst >= ((char *)src + count)) 
    { 
         while (count--) 
         { 
             *(char *)dst = *(char *)src; 
             dst = (char *)dst + 1; 
             src = (char *)src + 1; 
         } 
     }
    //後重疊,從後往前複製 
     else 
    { 
         dst = (char *)dst + count - 1; 
         src = (char *)src + count - 1; 
         while (count--) 
        { 
             *(char *)dst = *(char *)src; 
             dst = (char *)dst - 1; 
             src = (char *)src - 1; 
         } 
     } 
     return(ret); 
}

 

8.memmove函式和memcpy函式的辨析---前後重疊問題

 在字串的拷貝過程中,由於mem系列函式是記憶體操作函式即記憶體拷貝,所以有可能會遇到記憶體重疊導致拷貝失敗的問題,具體描述如下圖:

關於memmove函式從前往後複製和從後往前複製的解析:

 

 

相關文章