欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C語言字符串函數(shù),字符函數(shù),內(nèi)存函數(shù)使用及模擬實現(xiàn)

 更新時間:2022年09月07日 17:07:19   作者:野豬佩奇`  
這篇文章主要介紹了C語言字符串函數(shù),字符函數(shù),內(nèi)存函數(shù)使用及模擬實現(xiàn),文章圍繞主題展開詳細的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下

求字符串長度

strlen

函數(shù)功能

字符串長度,求一個字符串中字符的個數(shù)(不包含’\0’)。

函數(shù)參數(shù):

size_t strlen( const char *string );
# size_t 是函數(shù)的返回類型
# char* string 是函數(shù)參數(shù)

函數(shù)使用:

#include <stdio.h>
#include <string.h>  //strlen對應(yīng)頭文件
int main()
{
	char arr[] = "abcdef";
	int len = strlen(arr);
	printf("%d\n", len);
	return 0;
}

模擬實現(xiàn)(初階):

#include <stdio.h>
#include <assert.h>  //assert對應(yīng)頭文件

size_t my_strlen(const char* str)
{
	assert(str != NULL);   //檢查str是否為空指針
	int count = 0;
	while (*str != '\0')
	{
		str++;
		count++;
	}
	return count;
}

int main()
{
	char arr[] = "abcdef";
	int len = my_strlen(arr);
	printf("%d\n", len);
	return 0;
}

模擬實現(xiàn)(進階):

#include <stdio.h>
#include <assert.h>  //assert對應(yīng)頭文件
size_t my_strlen(const char* str)   //const用于保護源字符串不被修改
{
	assert(str != NULL);
	char* head = str;      //記錄字符串開頭的地址
	while (*str++);        //找到字符串結(jié)束的地址
	return str - head - 1; //指針-指針得到元素個數(shù),-1減去\0
}
int main()
{
	char arr[] = "abcdef";
	int len = my_strlen(arr);
	printf("%d\n", len);
	return 0;
}

注意事項:

  • 字符串以 ‘\0’ 作為結(jié)束標志,strlen函數(shù)返回的是在字符串中 ‘\0’ 前面出現(xiàn)的字符個數(shù)(不包含’\0’)。
  • 函數(shù)參數(shù)指向的字符串必須要以 ‘\0’ 結(jié)束,否則得到的就是隨機值(strlen會一直往后找,直到遇到’\0’才結(jié)束)。(常考)
  • 注意函數(shù)的返回值為size_t,是無符號的。(易錯:可能出現(xiàn)算術(shù)轉(zhuǎn)換)

長度不受限制的字符串函數(shù)

strcpy

函數(shù)功能:

字符串拷貝,把一個字符串里面的內(nèi)容拷貝到另一個字符串中去(包括’\0’)。

函數(shù)參數(shù);

char* strcpy(char * destination, const char * source );
# char* 是函數(shù)的返回值,返回的是目標空間的起始地址
# source 是要拷貝的字符串
# destination 是目標空間

函數(shù)使用:

#include <stdio.h>
#include <string.h>  //strcpy對應(yīng)頭文件
int main()
{
	char arr1[] = "abcdef";
	char arr2[10] = "";
	strcpy(arr2, arr1);
	printf("%s\n", arr2);
	return 0;
}

模擬實現(xiàn)(初階):

#include <stdio.h>
#include <assert.h>  //assert對應(yīng)頭文件

char* my_strcpy(char* dest, const char* src)  //const用于保護源字符串
{
	assert(dest && src);  //保證傳遞過來的不是空串
	char* ret = dest;  //記錄目標空間的起始地址
	while (*src != '\0')
	{
		*dest = *src;
		dest++;
		src++;
	}
	*dest = *src;  //將末尾的'\0'賦給dest
	return ret;
}

int main()
{
	char arr1[] = "abcdef";
	char arr2[10] = "";
	my_strcpy(arr2, arr1);
	printf("%s\n", arr2);
	return 0;
}

模擬實現(xiàn)(進階):

#include <stdio.h>
#include <assert.h>  //assert對應(yīng)頭文件

char* my_strcpy(char* dest, const char* src)  //const用于保護源字符串
{
	assert(dest && src);  //保證傳遞過來的不是空串
	char* ret = dest;  //記錄目標空間的起始地址
	while (*dest++ = *src++);  //連同末尾的'\0'一起賦給了dest
	return ret;
}

int main()
{
	char arr1[] = "abcdef";
	char arr2[10] = "";
	my_strcpy(arr2, arr1);
	printf("%s\n", arr2);
	return 0;
}

注意事項:

  • 源字符串必須以 ‘\0’ 結(jié)束。
  • 會將源字符串中的 ‘\0’ 拷貝到目標空間。
  • 目標空間必須足夠大,以確保能存放源字符串。
  • 目標空間必須可變。

strcat

函數(shù)功能:

字符串追加,在一個字符串的末尾追加另外一個字符串(包括’\0’)。

函數(shù)參數(shù):

char * strcat ( char * destination, const char * source );
# char* 是函數(shù)的返回值,返回的是目標空間的起始地址
# source 是要追加的字符串
# destination 是目標空間

函數(shù)使用:

#include <stdio.h>
#include <string.h>  //strcat對應(yīng)頭文件
int main()
{
	char arr1[] = "world!";
	char arr2[20] = "hello ";
	strcat(arr2, arr1);
	printf("%s\n", arr2);
	return 0;
}

模擬實現(xiàn)(初階):

#include <stdio.h>
#include <assert.h>

//字符串追加可以分為兩部分:1、找到目標字符串的末尾 2、字符串拷貝
char* my_strcat(char* dest, const char* src)  //const用于保護源字符串
{
	assert(dest && src);  //保證傳遞過來的不是空串
	char* ret = dest;  //記錄目標空間的起始地址
	while (*dest != '\0')  //找到目標空間的末尾
	{
		dest++;
	}
	while (*src != '\0')
	{
		*dest = *src;
		dest++;
		src++;
	}
	*dest = *src;  //將末尾的'\0'賦給dest
	return ret;
}

int main()
{
	char arr1[] = "world!";
	char arr2[20] = "hello ";
	my_strcat(arr2, arr1);
	printf("%s\n", arr2);
	return 0;
}

模擬實現(xiàn)(進階):

#include <stdio.h>
#include <assert.h>

//字符串追加可以分為兩部分:1、找到目標字符串的末尾 2、字符串拷貝
char* my_strcat(char* dest, const char* src)  //const用于保護源字符串
{
	assert(dest && src);  //保證傳遞過來的不是空串
	char* ret = dest;  //記錄目標空間的起始地址

	while (*dest++ != '\0');   //找到目標空間的末尾
	dest--;  //抵消最后一次自增的副作用
	while (*dest++ = *src++);  //字符串拷貝

	return ret;
}

int main()
{
	char arr1[] = "world!";
	char arr2[20] = "hello ";
	my_strcat(arr2, arr1);
	printf("%s\n", arr2);
	return 0;
}

注意事項:

  • 源字符串必須以 ‘\0’ 結(jié)束。
  • 目標空間必須有足夠的大,能容納下源字符串的內(nèi)容。
  • 目標空間必須可修改。
  • strcat 函數(shù)不能自己給自己追加。(追加會覆蓋掉末尾的’\0’,導致死循環(huán))

strcmp

函數(shù)功能:

字符串比較,以字節(jié)為單位比較兩個字符串的大小

函數(shù)參數(shù):

int strcmp ( const char * str1, const char * str2 );
# int 函數(shù)返回值
# char* str1  char* str2 用于比較的兩個字符串

函數(shù)返回值:

>0 : str1 大于 str2;
=0 : str1 等于 str2;
<0 : str1 小于 str2

函數(shù)使用:

#include <stdio.h>
#include <string.h>  //strcmp對應(yīng)頭文件
int main()
{
	char str1[] = "abcdef";
	char str2[] = "abq";
	int ret = strcmp(str1, str2);
	printf("%d\n", ret);
	return 0;
}

模擬實現(xiàn):

#include <stdio.h>
#include <assert.h>  //assert對應(yīng)頭文件

int my_strcmp(const char* str1, const char* str2)
{
	assert(str1 && str2);
	while (*str1 == *str2 && *str1 != '\0' && *str2 != '\0')  //找到字符串中不相等字符的位置
	{
		str1++;
		str2++;
	}
	if (*str1 != '\0' && *str2 != '\0')   //如果此位置不是字符串末尾,則不相等,返回差值
		return *str1 - *str2;
	return 0;  //若前面沒返回,說明相等,返回0
}

int main()
{
	char str1[] = "abcdef";
	char str2[] = "abq";
	int ret = my_strcmp(str1, str2);
	printf("%d\n", ret);
	return 0;
}

注意事項:

  • 對每一對字符進行比較,直到遇到不相等的字符或者遇到’\0’。
  • 比較的是每一對字符的ASCII值。

長度受限制的字符串函數(shù)

由于strcpy、strcat、strcmp等字符串函數(shù)存在安全隱患(目標空間小于源空間等問題),C語言還提供了另外幾種相對安全的字符串函數(shù),即strncpy、strncat、strncmp,這些字符串函數(shù)相比于原字符串函數(shù)多了一個參數(shù),用于指定操作的字節(jié)數(shù)。(注意:strncpy、strncat、strncmp函數(shù)只是相對安全,并不是絕對安全,多一個參數(shù)只是起到一個提醒作用)

strncpy

函數(shù)功能:

字符串拷貝,把一個字符串中num個字節(jié)的內(nèi)容拷貝到另一個字符串中去。

函數(shù)參數(shù):

char * strncpy ( char * destination, const char * source, size_t num );
# char* 是函數(shù)的返回值,返回的是目標空間的起始地址
# source 是要拷貝的字符串
# destination 是目標空間
# num 是要拷貝的字節(jié)數(shù)

函數(shù)使用:

#include <stdio.h>
#include <string.h>  //strncpy對應(yīng)的頭文件
int main()
{
	char arr1[] = "abcdef";
	char arr2[10] = "";
	strncpy(arr2, arr1, sizeof(arr1) / sizeof(arr1[0]) * 1);
	//sizeof(arr)/sizeof(arr[0]) 求得數(shù)組元素個數(shù),*1 乘以每個元素的大小,得到整個數(shù)組的字節(jié)數(shù)
	printf("%s\n", arr2);
	return 0;
}

模擬實現(xiàn):

//模擬實現(xiàn)strncpy
#include <stdio.h>
#include <assert.h>
char* my_strncpy(char* dest, const char* src, size_t num)
{
	assert(dest && src);
	char* ret = dest;  //記錄目標空間的起始地址
	while (num--)
	{
		*dest = *src;
		if (*dest == '\0')  //如果*dest為0,說明將src的結(jié)束字符賦給了dest,這種情況下源字符串的長度小于num
		{
			while (num--)
			{
				//如果源字符串的長度小于num,則拷貝完源字符串之后,在目標的后邊追加0,直到num個。(與庫函數(shù)的實現(xiàn)方式保持一致)
				*dest++ = 0;
			}
			break;
		}
        dest++;
        src++;
	}
	*dest = '\0';  //在dest的末尾補上結(jié)束字符
	return ret;
}

int main()
{
	char arr1[] = "abc";
	char arr2[10];
	my_strncpy(arr2, arr1, 4);
	printf("%s\n", arr2);
	return 0;
}

注意事項:

  • 拷貝num個字符從源字符串到目標空間。
  • 如果源字符串的長度小于num,則拷貝完源字符串之后,在目標的后邊追加0,直到num個。

strncat

函數(shù)功能:

字符串追加,將一個字符串中num個字節(jié)的內(nèi)容追加到另一個字符串的末尾,并在最后面加上’\0’。

函數(shù)參數(shù):

char * strncat ( char * destination, const char * source, size_t num );
# char* 是函數(shù)的返回值,返回的是目標空間的起始地址
# source 是要追加的字符串
# destination 是目標空間
# num 是要追加的字節(jié)數(shù)

函數(shù)使用:

#include <stdio.h>
#include <string.h>  //strncat對應(yīng)的頭文件
int main()
{
	char arr1[] = "world!";
	char arr2[20] = "hello ";
	strncat(arr2, arr1, sizeof(arr1) / sizeof(arr1[0]) * 1);
	//sizeof(arr)/sizeof(arr[0]) 求得數(shù)組元素個數(shù),*1 乘以每個元素的大小,得到整個數(shù)組的字節(jié)數(shù)
	printf("%s\n", arr2);
	return 0;
}

模擬實現(xiàn):

#include <stdio.h>
#include <assert.h>
char* my_strncat(char* dest, const char* src, size_t num)
{
	assert(dest && src);
	char* ret = dest;  //記錄目標空間的起始地址

	//找到dest末尾
	while (*dest != '\0')
	{
		dest++;
	}

	//strncpy
	while (num--)
	{
		*dest = *src;
		if (*dest == '\0')  //如果*dest為0,說明將src的結(jié)束字符賦給了dest,這種情況下源字符串的長度小于num
		{
			//如果源字字符串的長度小于num,則只復制到終止空字符的內(nèi)容。(與庫函數(shù)的實現(xiàn)方式保持一致)
			return ret; //直接返回
		}
		dest++;
		src++;
	}
	*dest = '\0';  //如果循環(huán)正常結(jié)束,則在dest的末尾補上結(jié)束字符
	return ret;
}

int main()
{
	char arr1[20] = "abcdef";
	char arr2[] = "ABCDEF";
	my_strncat(arr1, arr2, 5);
	printf("%s\n", arr1);
	return 0;
}

注意事項:

  • 將源字符串中num個字節(jié)的內(nèi)容追加到目標字符串的末尾,并在最后添加’\0’。
  • 如果源中字符串的長度小于num,則只復制到終止空字符的內(nèi)容

strncmp

函數(shù)功能:

字符串比較,比較兩個字符串中前num個字節(jié)的大小。

函數(shù)參數(shù):

int strncmp ( const char * str1, const char * str2, size_t num );
# int 函數(shù)返回值
# char* str1  char* str2 用于比較的兩個字符串
# num 要比較的字節(jié)數(shù)

函數(shù)返回值:

>0 : str1 大于 str2;
=0 : str1 等于 str2;
<0 : str1 小于 str2

函數(shù)使用:

#include <stdio.h>
#include <string.h>  //strncmp對應(yīng)的頭文件
int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "abcq";
	int len = strncmp(arr2, arr1, 3);
	printf("%d\n", len);
	return 0;
}

注意事項:

  • 對前num對字節(jié)中的每一對字符進行比較,直到遇到不相等的字符。
  • 比較的是每一對字符的ASCII值。

字符串查找函數(shù)

strstr

函數(shù)功能:

查找子串,查找一個字符串中是否包含子串。

函數(shù)參數(shù):

char * strstr ( const char *str1, const char * str2);	
# char* 函數(shù)返回值,返回字符串中子串的起始地址,若找不到,則返回NULL;
# char* str1 要搜索的字符串;
# char* str2 子串

函數(shù)使用:

#include <stdio.h>
#include <string.h>  //strstr對應(yīng)頭文件
int main()
{
	char arr1[] = "abbbcdef";
	char arr2[] = "bcd";
	char* ret = strstr(arr1, arr2);
	printf("%s\n", ret);
	return 0;
}

模擬實現(xiàn)(重要):

#include <stdio.h>
#include <assert.h>
char* my_strstr(const char* str1, const char* str2)
{
    assert(str1 && str2);
	//如果中途匹配失敗需要回到str2的起始地址,所以用其他變量標識str2,保證str2的首地址不會丟失
	const char* p1 = str1;
	const char* p2 = str2;  
	const char* mark = str1;  //用來標記每次第一個字符成功匹配的位置
	while (*mark != '\0')
	{
		p1 = mark;  //從mark處開始往后匹配
		p2 = str2;  //每次匹配后p2回到str2開頭
		while (*p1 == *p2 && *p1 != '\0' && *p2 != '\0')  //一直往后匹配,直到遇到不相等的字符
		{
			p1++;
			p2++;
		}
		if (*p2 == '\0')  //如果不相等處p2為'\0'(子串全部匹配成功),則返回Mark處地址
			return (char*)mark;
		mark++;  //否則,說明這一次匹配失敗,從mark后面一個字節(jié)處開始重新匹配
	}
	return NULL;  //字符串找完都沒有子串就返回空指針
}

int main()
{
	char arr1[] = "abbbcdef";
	char arr2[] = "bcdq";
	char* ret = my_strstr(arr1, arr2);
	printf("%s\n", ret);
	return 0;

注意事項:

  • 被查找的字符串和子串都不能是空串,且都以’\0’結(jié)尾。
  • 如果查找成功,返回字符串中子串所在位置的首地址,如果查找失敗,則返回NULL。

注:我們上面模擬實現(xiàn)的查找子串的函數(shù)效率比較低,如果要追求高效率,則需要使用KMP算法,有關(guān)KMP算法的相關(guān)知識,我會在后面的文章中進行介紹。

strtok

函數(shù)功能:

字符串分割,把一個字符串按照分割標志分割為幾個字符串。

函數(shù)參數(shù):

char * strtok ( char * str, const char * sep );
# char* 函數(shù)返回值,strtok函數(shù)會找到str中的下一個標記,并將其用'\0'結(jié)尾,返回一個指向這個標記的指針;
# char* str 指定一個字符串,它包含了0個或者多個由sep字符串中一個或者多個分隔符分割的標記;
# char* sep 一個字符串,定義了用作分隔符的字符集合;

函數(shù)使用:

#include <stdio.h>
#include <string.h>
int main()
{
	char* sep = "@.";
	char email[] = "1684277750@qq.com";
	char tmp[20] = "";
	//由于strtok函數(shù)會改變被操作的字符串,所以在使用strtok函數(shù)切分的字符串一般都會臨時拷貝一份,操作拷貝的數(shù)據(jù)
	strcpy(tmp, email);
	printf("%s\n", strtok(tmp, sep));  //第一次第一個參數(shù)傳遞被切割字符串的首地址
	printf("%s\n", strtok(NULL, sep));   //第二次及以后第一個參數(shù)傳遞空指針(strtok會記住上一次切割的位置)
	printf("%s\n", strtok(NULL, sep));
	return 0;
}

這里我們知道目標字符串會被分隔符切割為三個字符串,所以這里我們調(diào)用了三次strtok函數(shù),但是當我們不知道目標字符串的內(nèi)容時,這種方法顯然就不能用了;那么我們該如何正確的使用strtok函數(shù)呢?

我們知道,strtok函數(shù)第一次調(diào)用時需要傳遞目標字符串的地址,其余調(diào)用都只需要傳遞NULL即可,那么我們可以利用這個特點結(jié)合for循環(huán)的特性來正確調(diào)用strtok函數(shù)。

#include <stdio.h>
#include <string.h>
int main()
{
	char* sep = "@.";
	char email[] = "1684277750@qq.com";
	char tmp[20] = "";
	//由于strtok函數(shù)會改變被操作的字符串,所以在使用strtok函數(shù)切分的字符串一般都會臨時拷貝一份,操作拷貝的數(shù)據(jù)
	strcpy(tmp, email);
	char* ret = NULL;  //用來保存strtok函數(shù)的返回值
	for (ret = strtok(tmp, sep);   //初始化部分:第一次傳遞tmp的地址
		ret != NULL;               //判斷部分:只要strtok的返回值ret不為空,說明繼續(xù)分割
		ret = strtok(NULL, sep))   //調(diào)整部分:第二次及以上傳遞NULL
	{
		printf("%s\n", ret);  //ret不為空,說明字符串沒被分割完,則打印
	}
	return 0;
}

注意事項:

  • sep參數(shù)是個字符串,定義了用作分隔符的字符集合;
  • 第一個參數(shù)指定一個字符串,它包含了0個或者多個由sep字符串中一個或者多個分隔符分割的標 記;
  • strtok函數(shù)找到str中的下一個標記,并將其用 \0 結(jié)尾,返回一個指向這個標記的指針。(注: 由于strtok函數(shù)會改變被操作的字符串,所以在使用strtok函數(shù)切分的字符串一般都會臨時拷貝一份,操作拷貝的數(shù)據(jù) )
  • strtok函數(shù)的第一個參數(shù)不為 NULL ,函數(shù)將找到str中第一個標記,strtok函數(shù)將保存它在字符串中的位置;
  • strtok函數(shù)的第一個參數(shù)為 NULL ,函數(shù)將在同一個字符串中被保存的位置開始,查找下一個標記;
  • 如果字符串中不存在更多的標記,則返回 NULL 指針;

strerror

函數(shù)功能:

C語言有一系列的庫函數(shù),當這些庫函數(shù)調(diào)用失敗時,會返回相應(yīng)的錯誤碼,而strerror函數(shù)的作用就是獲取錯誤碼對應(yīng)的錯誤信息的首地址,讓使用者知道程序發(fā)生錯誤的原因。

函數(shù)參數(shù):

char * strerror ( int errnum );
# char* 函數(shù)返回值,返回錯誤碼對應(yīng)的錯誤信息的字符串的地址;
# int errnum 錯誤碼

函數(shù)使用:

#include <stdio.h>  
#include <string.h>  //strerror的頭文件
int main()
{
	printf("%s\n", strerror(0));
	printf("%s\n", strerror(1));
	printf("%s\n", strerror(2));
	printf("%s\n", strerror(3));
	printf("%s\n", strerror(4));
	printf("%s\n", strerror(5));
	return 0;
}

這里有一個問題,C語言中那么多的錯誤信息,那么我們需要記住每一個錯誤信息對應(yīng)的錯誤碼嗎?其實,C語言中設(shè)置了一個全局的用于存放錯誤碼的變量errno,只要調(diào)用C語言庫函數(shù)發(fā)生錯誤,那么errno就會記錄相應(yīng)的錯誤碼,所以strerror函數(shù)和errno一般都是配合使用的。

#include <stdio.h>
#include <string.h>  //strerror對應(yīng)頭文件
#include <errno.h>   //errno對應(yīng)頭文件
int main()
{
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		printf("%s\n", strerror(errno));
		return 1;
	}
	else
	{
		printf("文件打開成功!\n");
	}
	return 0;
}

字符函數(shù)

字符分類函數(shù)

函數(shù)如果他的參數(shù)符合下列條件就返回真
iscntrl任何控制字符
isspace空白字符:空格‘ ’,換頁‘\f’,換行’\n’,回車‘\r’,制表符’\t’或者垂直制表符’\v’
isdigit十進制數(shù)字 0~9
isxdigit十六進制數(shù)字,包括所有十進制數(shù)字,小寫字母a~f,大寫字母A~F
islower小寫字母a~z
isupper大寫字母A~Z
isalpha字母a~z或A~Z
isalnum字母或者數(shù)字,a~z,A~Z,0~9
ispunct標點符號,任何不屬于數(shù)字或者字母的圖形字符(可打?。?/td>
isgraph任何圖形字符
isprint任何可打印字符,包括圖形字符和空白字符

字符轉(zhuǎn)換函數(shù)

函數(shù)返回值
tolower返回對應(yīng)小寫字母的ASCII值
toupper返回對應(yīng)大寫字母的ASCII值

內(nèi)存操作函數(shù)

前面我們學習的strcpy、strcat、strcmp、strncpy、strncat、strncmp等函數(shù)都是字符串函數(shù),只能對字符串進行相關(guān)操作,如果要對其他數(shù)據(jù)類型,如整形、字符、結(jié)構(gòu)體等進行類似操作的話,就需要學習內(nèi)存操作函數(shù),常見的內(nèi)存操作函數(shù)有memcpy、memmove、memcmp、memset。

memcpy

函數(shù)功能:

內(nèi)存拷貝,將一塊內(nèi)存中num個字節(jié)的內(nèi)容拷貝到另一塊內(nèi)存中,常用來處理不重疊內(nèi)存數(shù)據(jù)的拷貝。

函數(shù)參數(shù):

void * memcpy ( void * destination, const void * source, size_t num );
# void* 函數(shù)返回值,返回dest內(nèi)存空間的地址;
# void* destination 目標內(nèi)存空間地址;
# void* source 源空間地址;
# size_t num 要拷貝的字節(jié)數(shù);

函數(shù)使用:

#include <stdio.h>
#include <string.h>   //memcpy對應(yīng)頭文件
int main()
{
	int arr1[] = { 1,2,3,4,5,6 };
	int arr2[10] = { 0 };
	memcpy(arr2, arr1, 6 * sizeof(int));
	for (int i = 0; i < 6; i++)
	{
		printf("%d ", arr2[i]);
	}
	return 0;
}

模擬實現(xiàn):

#include <stdio.h>
#include <assert.h>
void* my_memcpy(void* dest, const void* src, size_t num)
{
	assert(dest && src);
	void* ret = dest;  //保存目標空間的地址
	while (num--)   //以字節(jié)為單位進行拷貝
	{
		*(char*)dest = *(char*)src;  //強轉(zhuǎn)為char*類型后賦值
        //這里不要寫成(char*)dest++,在某些編譯器下會報錯,因為強制類型轉(zhuǎn)是一種臨時效果
		dest = (char*)dest + 1;
		src = (char*)src + 1;
	}
	return ret;
}

int main()
{
	int arr1[] = { 1,2,3,4,5,6 };
	int arr2[10] = { 0 };
	my_memcpy(arr2, arr1, 6 * sizeof(int));
	for (int i = 0; i < 6; i++)
	{
		printf("%d ", arr2[i]);
	}
	return 0;
}

注意事項(重要):

在C語言標準中,memcpy只負責處理內(nèi)存不重疊的數(shù)據(jù),內(nèi)存重疊的數(shù)據(jù)的拷貝是memmove函數(shù)負責實現(xiàn)的,即下面這種情況在C語言標準中memcpy函數(shù)是不能實現(xiàn)的:

memcpy(arr1 + 2, arr1, 4 * sizeof(int));

從上面我們memcpy的模擬實現(xiàn)中也可以看出,memcpy是從前向后拷貝的,這就導致在拷貝重疊內(nèi)存數(shù)據(jù)時會發(fā)生數(shù)據(jù)覆蓋(即arr1[2]中的數(shù)據(jù)在前面賦值中被改為1,導致將arr[2]中的數(shù)據(jù)賦給arr[4]時不是4,而是1),但是在VS下的memcpy函數(shù)是具備拷貝重疊數(shù)據(jù)的能力的,也就是說,VS下的memcpy函數(shù)同時實現(xiàn)了memmove函數(shù)的功能,但是其他編譯器下的memcpy函數(shù)是否也具備memmove函數(shù)功能是未知的,所以我們在處理重疊內(nèi)存數(shù)據(jù)拷貝的時候盡量還是使用memmove函數(shù),以免發(fā)生錯誤。

memmove

函數(shù)功能:

內(nèi)存移動,將一塊內(nèi)存數(shù)據(jù)中的內(nèi)容移動覆蓋至另一塊內(nèi)存數(shù)據(jù),常用來處理重疊內(nèi)存數(shù)據(jù)的拷貝。

函數(shù)參數(shù):

# memmove 函數(shù)的參數(shù)和 memcpy 函數(shù)完全相同
void * memmove ( void* destination, const void * source, size_t num );

函數(shù)使用:

#include <stdio.h>
#include <string.h>  //memmove對應(yīng)頭文件
int main()
{
	int arr1[] = { 1,2,3,4,5,6 ,7,8 };
	int arr2[10] = { 0 };
	memmove(arr1 + 2, arr1, 4 * sizeof(int));
	for (int i = 0; i < 8; i++)
	{
		printf("%d ", arr1[i]);
	}
	return 0;
}

模擬實現(xiàn)(重要):

思路分析:

在memcpy中我們提到在拷貝重疊內(nèi)存的數(shù)據(jù)時會發(fā)生內(nèi)存覆蓋的情況,其實這種覆蓋分為兩種情況:

(1):dest的地址大于src的地址

如圖,如果這時我們從前往后移動的話,那么4就會覆蓋掉6,從而導致將6賦給8變成4賦給8,所以我們應(yīng)該從后往前移。

(2):dest的地址小于src的地址

 如果這時我們從后往前移動的話,那么7就會覆蓋掉5,導致將5賦給2的時候變成7賦給2,所以這里我們應(yīng)該從前往后移動。

代碼實現(xiàn):

#include <stdio.h>
#include <assert.h>
void* my_memmove(void* dest, void* src, size_t num)
{
	assert(dest && src);
	void* ret = dest;  //保存目標空間的地址
	if (dest < src)
	{
		//前 -> 后 等價于memcpy
		while (num--)
		{
			*(char*)dest = *(char*)src;
			dest = (char*)dest + 1;
			src = (char*)src + 1;
		}
	}
	else
	{
		//后 -> 前
		while (num--)
		{
			*((char*)dest + num) = *((char*)src + num);
		}
	}
	return ret;
}

int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9 };
	my_memmove(arr + 1, arr + 3, 4 * sizeof(int));
	for (int i = 0; i < 9; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

memcmp

函數(shù)功能:

內(nèi)存比較,比較兩塊內(nèi)存中前num個字節(jié)的大小。

函數(shù)參數(shù):

int memcmp ( const void * ptr1, const void * ptr2, size_t num );
# int 函數(shù)返回值;
# void* ptr1 void* ptr2 要比較的兩塊內(nèi)存;
# size_t num 要比較的字節(jié)數(shù);

函數(shù)返回值:

>0 : ptr1 大于 ptr2;
=0 : ptr1 等于 ptr2;
<0 : ptr1 小于 ptr2;··

函數(shù)使用:

#include <stdio.h>
#include <string.h>  //memcmp對應(yīng)頭文件
int main()
{
	int arr1[] = { 1,2,3,4,5 };
	int arr2[] = { 1,2,3 };
	int ret = memcmp(arr1, arr2, 3 * sizeof(int));
	printf("%d\n", ret);
	return 0;
}

模擬實現(xiàn):

#include <stdio.h>
#include <assert.h>

int my_memcmp(const void* ptr1, const void* ptr2, size_t num)
{
	assert(ptr2 && ptr2);
	size_t count = 0;  //用來標記相等的字節(jié)數(shù)
	while (*(char*)ptr1 == *(char*)ptr2 && count < num)  //一直循環(huán),直到找到不相等的字節(jié)數(shù)據(jù)
	{
		count++;  //進入一次循環(huán)表示有一對字節(jié)相等,count++
		ptr1 = (char*)ptr1 + 1;
		ptr2 = (char*)ptr2 + 1;
	}
	if (count < num)  //如果count小于num,說明兩塊內(nèi)存的前num個字節(jié)中有不相等的數(shù)據(jù)
		return *(char*)ptr1 - *(char*)ptr2;  //直接返回數(shù)據(jù)的差值
	else
		return 0;  //count等于num,說明相等
}
int main()
{
	int arr1[] = { 1,2,3,4,5 };
	int arr2[] = { 1,2,3 };
	int ret = my_memcmp(arr1, arr2, 3 * sizeof(int));
	printf("%d\n", ret);
	return 0;
}

memset

函數(shù)功能:

內(nèi)存設(shè)置,把一塊內(nèi)存中num個字節(jié)的內(nèi)容設(shè)置為指定的數(shù)據(jù)。

函數(shù)參數(shù):

void *memset( void *dest, int c, size_t count );
# void* 函數(shù)返回值,返回目標空間的地址;
# int c 函數(shù)參數(shù),指定你想要初始化的數(shù)據(jù);
# size_t count 函數(shù)參數(shù),指定初始化的字節(jié)數(shù)

函數(shù)使用:

#include <stdio.h>
#include <string.h>  //memset對應(yīng)頭文件
int main()
{
	char str[] = "hello world";
	memset(str, 'x', 5);
	printf("%s\n", str);
	return 0;
}

模擬實現(xiàn):

#include <stdio.h>
#include <assert.h>

void* my_memset(void* dest, int c, size_t num)
{
	assert(dest != NULL);
	void* ret = dest;  //記錄目標空間的地址
	while (num--)  //循環(huán)將num個字節(jié)的內(nèi)容初始化為指定值
	{
		*(char*)dest = c;
		dest = (char*)dest + 1;
	}
	return ret;
}

int main()
{
	char str[] = "hello world";
	my_memset(str, 'x', 5);
	printf("%s\n", str);
	return 0;
}

到此這篇關(guān)于C語言字符串函數(shù),字符函數(shù),內(nèi)存函數(shù)使用及模擬實現(xiàn)的文章就介紹到這了,更多相關(guān)C語言字符串內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論