C语言字符串函数,字符函数,内存函数使用及模拟实现

求字符串长度

strlen

函数功能

字符串长度,求一个字符串中字符的个数(不包含’\0’)。

函数参数:

size_t strlen( const char *string );
# size_t 是函数的返回类型
# char* string 是函数参数

函数使用:

#include 
#include   //strlen对应头文件
int main()
{
	char arr[] = "abcdef";
	int len = strlen(arr);
	printf("%d\n", len);
	return 0;
}

C语言字符串函数,字符函数,内存函数使用及模拟实现_第1张图片

模拟实现(初阶):

#include 
#include   //assert对应头文件

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;
}

C语言字符串函数,字符函数,内存函数使用及模拟实现_第2张图片

模拟实现(进阶):

#include 
#include   //assert对应头文件
size_t my_strlen(const char* str)   //const用于保护源字符串不被修改
{
	assert(str != NULL);
	char* head = str;      //记录字符串开头的地址
	while (*str++);        //找到字符串结束的地址
	return str - head - 1; //指针-指针得到元素个数,-1减去\0
}
int main()
{
	char arr[] = "abcdef";
	int len = my_strlen(arr);
	printf("%d\n", len);
	return 0;
}

C语言字符串函数,字符函数,内存函数使用及模拟实现_第3张图片

注意事项:

  • 字符串以 ‘\0’ 作为结束标志,strlen函数返回的是在字符串中 ‘\0’ 前面出现的字符个数(不包含’\0’)。
  • 函数参数指向的字符串必须要以 ‘\0’ 结束,否则得到的就是随机值(strlen会一直往后找,直到遇到’\0’才结束)。(常考)
  • 注意函数的返回值为size_t,是无符号的。(易错:可能出现算术转换)

长度不受限制的字符串函数

strcpy

函数功能:

字符串拷贝,把一个字符串里面的内容拷贝到另一个字符串中去(包括’\0’)。

函数参数;

char* strcpy(char * destination, const char * source );
# char* 是函数的返回值,返回的是目标空间的起始地址
# source 是要拷贝的字符串
# destination 是目标空间

函数使用:

#include 
#include   //strcpy对应头文件
int main()
{
	char arr1[] = "abcdef";
	char arr2[10] = "";
	strcpy(arr2, arr1);
	printf("%s\n", arr2);
	return 0;
}

C语言字符串函数,字符函数,内存函数使用及模拟实现_第4张图片

模拟实现(初阶):

#include 
#include   //assert对应头文件

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;
}

C语言字符串函数,字符函数,内存函数使用及模拟实现_第5张图片

模拟实现(进阶):

#include 
#include   //assert对应头文件

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;
}

C语言字符串函数,字符函数,内存函数使用及模拟实现_第6张图片

注意事项:

  • 源字符串必须以 ‘\0’ 结束。
  • 会将源字符串中的 ‘\0’ 拷贝到目标空间。
  • 目标空间必须足够大,以确保能存放源字符串。
  • 目标空间必须可变。

strcat

函数功能:

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

函数参数:

char * strcat ( char * destination, const char * source );
# char* 是函数的返回值,返回的是目标空间的起始地址
# source 是要追加的字符串
# destination 是目标空间

函数使用:

#include 
#include   //strcat对应头文件
int main()
{
	char arr1[] = "world!";
	char arr2[20] = "hello ";
	strcat(arr2, arr1);
	printf("%s\n", arr2);
	return 0;
}

C语言字符串函数,字符函数,内存函数使用及模拟实现_第7张图片

模拟实现(初阶):

#include 
#include 

//字符串追加可以分为两部分: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;
}

C语言字符串函数,字符函数,内存函数使用及模拟实现_第8张图片

模拟实现(进阶):

#include 
#include 

//字符串追加可以分为两部分: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’ 结束。
  • 目标空间必须有足够的大,能容纳下源字符串的内容。
  • 目标空间必须可修改。
  • strcat 函数不能自己给自己追加。(追加会覆盖掉末尾的’\0’,导致死循环)

strcmp

函数功能:

字符串比较,以字节为单位比较两个字符串的大小

函数参数:

int strcmp ( const char * str1, const char * str2 );
# int 函数返回值
# char* str1  char* str2 用于比较的两个字符串

函数返回值:

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

函数使用:

#include 
#include   //strcmp对应头文件
int main()
{
	char str1[] = "abcdef";
	char str2[] = "abq";
	int ret = strcmp(str1, str2);
	printf("%d\n", ret);
	return 0;
}

C语言字符串函数,字符函数,内存函数使用及模拟实现_第9张图片

模拟实现:

#include 
#include   //assert对应头文件

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;
}

C语言字符串函数,字符函数,内存函数使用及模拟实现_第10张图片

C语言字符串函数,字符函数,内存函数使用及模拟实现_第11张图片

注意事项:

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

长度受限制的字符串函数

由于strcpy、strcat、strcmp等字符串函数存在安全隐患(目标空间小于源空间等问题),C语言还提供了另外几种相对安全的字符串函数,即strncpy、strncat、strncmp,这些字符串函数相比于原字符串函数多了一个参数,用于指定操作的字节数。(注意:strncpy、strncat、strncmp函数只是相对安全,并不是绝对安全,多一个参数只是起到一个提醒作用)

strncpy

函数功能:

字符串拷贝,把一个字符串中num个字节的内容拷贝到另一个字符串中去。

函数参数:

char * strncpy ( char * destination, const char * source, size_t num );
# char* 是函数的返回值,返回的是目标空间的起始地址
# source 是要拷贝的字符串
# destination 是目标空间
# num 是要拷贝的字节数

函数使用:

#include 
#include   //strncpy对应的头文件
int main()
{
	char arr1[] = "abcdef";
	char arr2[10] = "";
	strncpy(arr2, arr1, sizeof(arr1) / sizeof(arr1[0]) * 1);
	//sizeof(arr)/sizeof(arr[0]) 求得数组元素个数,*1 乘以每个元素的大小,得到整个数组的字节数
	printf("%s\n", arr2);
	return 0;
}

C语言字符串函数,字符函数,内存函数使用及模拟实现_第12张图片

模拟实现:

//模拟实现strncpy
#include 
#include 
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的结束字符赋给了dest,这种情况下源字符串的长度小于num
		{
			while (num--)
			{
				//如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。(与库函数的实现方式保持一致)
				*dest++ = 0;
			}
			break;
		}
        dest++;
        src++;
	}
	*dest = '\0';  //在dest的末尾补上结束字符
	return ret;
}

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

C语言字符串函数,字符函数,内存函数使用及模拟实现_第13张图片

C语言字符串函数,字符函数,内存函数使用及模拟实现_第14张图片

注意事项:

  • 拷贝num个字符从源字符串到目标空间。
  • 如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。

strncat

函数功能:

字符串追加,将一个字符串中num个字节的内容追加到另一个字符串的末尾,并在最后面加上’\0’。

函数参数:

char * strncat ( char * destination, const char * source, size_t num );
# char* 是函数的返回值,返回的是目标空间的起始地址
# source 是要追加的字符串
# destination 是目标空间
# num 是要追加的字节数

函数使用:

#include 
#include   //strncat对应的头文件
int main()
{
	char arr1[] = "world!";
	char arr2[20] = "hello ";
	strncat(arr2, arr1, sizeof(arr1) / sizeof(arr1[0]) * 1);
	//sizeof(arr)/sizeof(arr[0]) 求得数组元素个数,*1 乘以每个元素的大小,得到整个数组的字节数
	printf("%s\n", arr2);
	return 0;
}

C语言字符串函数,字符函数,内存函数使用及模拟实现_第15张图片

模拟实现:

#include 
#include 
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的结束字符赋给了dest,这种情况下源字符串的长度小于num
		{
			//如果源字字符串的长度小于num,则只复制到终止空字符的内容。(与库函数的实现方式保持一致)
			return ret; //直接返回
		}
		dest++;
		src++;
	}
	*dest = '\0';  //如果循环正常结束,则在dest的末尾补上结束字符
	return ret;
}

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

C语言字符串函数,字符函数,内存函数使用及模拟实现_第16张图片

C语言字符串函数,字符函数,内存函数使用及模拟实现_第17张图片

注意事项:

  • 将源字符串中num个字节的内容追加到目标字符串的末尾,并在最后添加’\0’。
  • 如果源中字符串的长度小于num,则只复制到终止空字符的内容

strncmp

函数功能:

字符串比较,比较两个字符串中前num个字节的大小。

函数参数:

int strncmp ( const char * str1, const char * str2, size_t num );
# int 函数返回值
# char* str1  char* str2 用于比较的两个字符串
# num 要比较的字节数

函数返回值:

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

函数使用:

#include 
#include   //strncmp对应的头文件
int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "abcq";
	int len = strncmp(arr2, arr1, 3);
	printf("%d\n", len);
	return 0;
}

C语言字符串函数,字符函数,内存函数使用及模拟实现_第18张图片

注意事项:

  • 对前num对字节中的每一对字符进行比较,直到遇到不相等的字符。
  • 比较的是每一对字符的ASCII值。

字符串查找函数

strstr

函数功能:

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

函数参数:

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

函数使用:

#include 
#include   //strstr对应头文件
int main()
{
	char arr1[] = "abbbcdef";
	char arr2[] = "bcd";
	char* ret = strstr(arr1, arr2);
	printf("%s\n", ret);
	return 0;
}

C语言字符串函数,字符函数,内存函数使用及模拟实现_第19张图片

模拟实现(重要):

#include 
#include 
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后面一个字节处开始重新匹配
	}
	return NULL;  //字符串找完都没有子串就返回空指针
}

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

C语言字符串函数,字符函数,内存函数使用及模拟实现_第20张图片

注意事项:

  • 被查找的字符串和子串都不能是空串,且都以’\0’结尾。
  • 如果查找成功,返回字符串中子串所在位置的首地址,如果查找失败,则返回NULL。

注:我们上面模拟实现的查找子串的函数效率比较低,如果要追求高效率,则需要使用KMP算法,有关KMP算法的相关知识,我会在后面的文章中进行介绍。

strtok

函数功能:

字符串分割,把一个字符串按照分割标志分割为几个字符串。

函数参数:

char * strtok ( char * str, const char * sep );
# char* 函数返回值,strtok函数会找到str中的下一个标记,并将其用'\0'结尾,返回一个指向这个标记的指针;
# char* str 指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记;
# char* sep 一个字符串,定义了用作分隔符的字符集合;

函数使用:

#include 
#include 
int main()
{
	char* sep = "@.";
	char email[] = "[email protected]";
	char tmp[20] = "";
	//由于strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都会临时拷贝一份,操作拷贝的数据
	strcpy(tmp, email);
	printf("%s\n", strtok(tmp, sep));  //第一次第一个参数传递被切割字符串的首地址
	printf("%s\n", strtok(NULL, sep));   //第二次及以后第一个参数传递空指针(strtok会记住上一次切割的位置)
	printf("%s\n", strtok(NULL, sep));
	return 0;
}

C语言字符串函数,字符函数,内存函数使用及模拟实现_第21张图片

这里我们知道目标字符串会被分隔符切割为三个字符串,所以这里我们调用了三次strtok函数,但是当我们不知道目标字符串的内容时,这种方法显然就不能用了;那么我们该如何正确的使用strtok函数呢?

我们知道,strtok函数第一次调用时需要传递目标字符串的地址,其余调用都只需要传递NULL即可,那么我们可以利用这个特点结合for循环的特性来正确调用strtok函数。

#include 
#include 
int main()
{
	char* sep = "@.";
	char email[] = "[email protected]";
	char tmp[20] = "";
	//由于strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都会临时拷贝一份,操作拷贝的数据
	strcpy(tmp, email);
	char* ret = NULL;  //用来保存strtok函数的返回值
	for (ret = strtok(tmp, sep);   //初始化部分:第一次传递tmp的地址
		ret != NULL;               //判断部分:只要strtok的返回值ret不为空,说明继续分割
		ret = strtok(NULL, sep))   //调整部分:第二次及以上传递NULL
	{
		printf("%s\n", ret);  //ret不为空,说明字符串没被分割完,则打印
	}
	return 0;
}

C语言字符串函数,字符函数,内存函数使用及模拟实现_第22张图片

注意事项:

  • sep参数是个字符串,定义了用作分隔符的字符集合;
  • 第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标 记;
  • strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。(注: 由于strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都会临时拷贝一份,操作拷贝的数据 )
  • strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置;
  • strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记;
  • 如果字符串中不存在更多的标记,则返回 NULL 指针;

strerror

函数功能:

C语言有一系列的库函数,当这些库函数调用失败时,会返回相应的错误码,而strerror函数的作用就是获取错误码对应的错误信息的首地址,让使用者知道程序发生错误的原因。

函数参数:

char * strerror ( int errnum );
# char* 函数返回值,返回错误码对应的错误信息的字符串的地址;
# int errnum 错误码

函数使用:

#include   
#include   //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语言字符串函数,字符函数,内存函数使用及模拟实现_第23张图片

这里有一个问题,C语言中那么多的错误信息,那么我们需要记住每一个错误信息对应的错误码吗?其实,C语言中设置了一个全局的用于存放错误码的变量errno,只要调用C语言库函数发生错误,那么errno就会记录相应的错误码,所以strerror函数和errno一般都是配合使用的。

#include 
#include   //strerror对应头文件
#include    //errno对应头文件
int main()
{
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		printf("%s\n", strerror(errno));
		return 1;
	}
	else
	{
		printf("文件打开成功!\n");
	}
	return 0;
}

C语言字符串函数,字符函数,内存函数使用及模拟实现_第24张图片

字符函数

字符分类函数

函数 如果他的参数符合下列条件就返回真
iscntrl 任何控制字符
isspace 空白字符:空格‘ ’,换页‘\f’,换行’\n’,回车‘\r’,制表符’\t’或者垂直制表符’\v’
isdigit 十进制数字 0~9
isxdigit 十六进制数字,包括所有十进制数字,小写字母a~f,大写字母A~F
islower 小写字母a~z
isupper 大写字母A~Z
isalpha 字母a~z或A~Z
isalnum 字母或者数字,a~z,A~Z,0~9
ispunct 标点符号,任何不属于数字或者字母的图形字符(可打印)
isgraph 任何图形字符
isprint 任何可打印字符,包括图形字符和空白字符

字符转换函数

函数 返回值
tolower 返回对应小写字母的ASCII值
toupper 返回对应大写字母的ASCII值

内存操作函数

前面我们学习的strcpy、strcat、strcmp、strncpy、strncat、strncmp等函数都是字符串函数,只能对字符串进行相关操作,如果要对其他数据类型,如整形、字符、结构体等进行类似操作的话,就需要学习内存操作函数,常见的内存操作函数有memcpy、memmove、memcmp、memset

memcpy

函数功能:

内存拷贝,将一块内存中num个字节的内容拷贝到另一块内存中,常用来处理不重叠内存数据的拷贝。

函数参数:

void * memcpy ( void * destination, const void * source, size_t num );
# void* 函数返回值,返回dest内存空间的地址;
# void* destination 目标内存空间地址;
# void* source 源空间地址;
# size_t num 要拷贝的字节数;

函数使用:

#include 
#include    //memcpy对应头文件
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;
}

C语言字符串函数,字符函数,内存函数使用及模拟实现_第25张图片

模拟实现:

#include 
#include 
void* my_memcpy(void* dest, const void* src, size_t num)
{
	assert(dest && src);
	void* ret = dest;  //保存目标空间的地址
	while (num--)   //以字节为单位进行拷贝
	{
		*(char*)dest = *(char*)src;  //强转为char*类型后赋值
        //这里不要写成(char*)dest++,在某些编译器下会报错,因为强制类型转是一种临时效果
		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语言字符串函数,字符函数,内存函数使用及模拟实现_第26张图片

注意事项(重要):

在C语言标准中,memcpy只负责处理内存不重叠的数据,内存重叠的数据的拷贝是memmove函数负责实现的,即下面这种情况在C语言标准中memcpy函数是不能实现的:

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

C语言字符串函数,字符函数,内存函数使用及模拟实现_第27张图片

从上面我们memcpy的模拟实现中也可以看出,memcpy是从前向后拷贝的,这就导致在拷贝重叠内存数据时会发生数据覆盖(即arr1[2]中的数据在前面赋值中被改为1,导致将arr[2]中的数据赋给arr[4]时不是4,而是1),但是在VS下的memcpy函数是具备拷贝重叠数据的能力的,也就是说,VS下的memcpy函数同时实现了memmove函数的功能,但是其他编译器下的memcpy函数是否也具备memmove函数功能是未知的,所以我们在处理重叠内存数据拷贝的时候尽量还是使用memmove函数,以免发生错误。

memmove

函数功能:

内存移动,将一块内存数据中的内容移动覆盖至另一块内存数据,常用来处理重叠内存数据的拷贝。

函数参数:

# memmove 函数的参数和 memcpy 函数完全相同
void * memmove ( void* destination, const void * source, size_t num );

函数使用:

#include 
#include   //memmove对应头文件
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;
}

C语言字符串函数,字符函数,内存函数使用及模拟实现_第28张图片

模拟实现(重要):

思路分析:

在memcpy中我们提到在拷贝重叠内存的数据时会发生内存覆盖的情况,其实这种覆盖分为两种情况:

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

C语言字符串函数,字符函数,内存函数使用及模拟实现_第29张图片

如图,如果这时我们从前往后移动的话,那么4就会覆盖掉6,从而导致将6赋给8变成4赋给8,所以我们应该从后往前移。

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

C语言字符串函数,字符函数,内存函数使用及模拟实现_第30张图片

 如果这时我们从后往前移动的话,那么7就会覆盖掉5,导致将5赋给2的时候变成7赋给2,所以这里我们应该从前往后移动。

代码实现:

#include 
#include 
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;
}

C语言字符串函数,字符函数,内存函数使用及模拟实现_第31张图片

C语言字符串函数,字符函数,内存函数使用及模拟实现_第32张图片

memcmp

函数功能:

内存比较,比较两块内存中前num个字节的大小。

函数参数:

int memcmp ( const void * ptr1, const void * ptr2, size_t num );
# int 函数返回值;
# void* ptr1 void* ptr2 要比较的两块内存;
# size_t num 要比较的字节数;

函数返回值:

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

函数使用:

#include 
#include   //memcmp对应头文件
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;
}

C语言字符串函数,字符函数,内存函数使用及模拟实现_第33张图片

模拟实现:

#include 
#include 

int my_memcmp(const void* ptr1, const void* ptr2, size_t num)
{
	assert(ptr2 && ptr2);
	size_t count = 0;  //用来标记相等的字节数
	while (*(char*)ptr1 == *(char*)ptr2 && count < num)  //一直循环,直到找到不相等的字节数据
	{
		count++;  //进入一次循环表示有一对字节相等,count++
		ptr1 = (char*)ptr1 + 1;
		ptr2 = (char*)ptr2 + 1;
	}
	if (count < num)  //如果count小于num,说明两块内存的前num个字节中有不相等的数据
		return *(char*)ptr1 - *(char*)ptr2;  //直接返回数据的差值
	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;
}

C语言字符串函数,字符函数,内存函数使用及模拟实现_第34张图片

memset

函数功能:

内存设置,把一块内存中num个字节的内容设置为指定的数据。

函数参数:

void *memset( void *dest, int c, size_t count );
# void* 函数返回值,返回目标空间的地址;
# int c 函数参数,指定你想要初始化的数据;
# size_t count 函数参数,指定初始化的字节数

函数使用:

#include 
#include   //memset对应头文件
int main()
{
	char str[] = "hello world";
	memset(str, 'x', 5);
	printf("%s\n", str);
	return 0;
}

C语言字符串函数,字符函数,内存函数使用及模拟实现_第35张图片

模拟实现:

#include 
#include 

void* my_memset(void* dest, int c, size_t num)
{
	assert(dest != NULL);
	void* ret = dest;  //记录目标空间的地址
	while (num--)  //循环将num个字节的内容初始化为指定值
	{
		*(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;
}

C语言字符串函数,字符函数,内存函数使用及模拟实现_第36张图片

到此这篇关于C语言字符串函数,字符函数,内存函数使用及模拟实现的文章就介绍到这了,更多相关C语言字符串内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

你可能感兴趣的:(C语言字符串函数,字符函数,内存函数使用及模拟实现)