【C语言】字符串函数与内存操作函数的剖析与模拟实现

目录

1 字符串函数

1.1 求字符串长度函数

1.1.1 strlen函数

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

1.2.1 strcpy函数

1.2.2 strcat函数

1.2.3 strcmp函数

1.3 长度受限制字符串函数

1.3.1 strncpy函数

 1.3.2 strncat函数

1.3.3 strncmp函数

1.4 字符串查找函数

1.4.1 strstr函数

1.4.2 strtok函数

1.5 错误信息报告函数

1.5.1 strerror函数

1.5.2 perror函数

2 内存操作函数

2.1 memcpy函数

2.2 memmove函数

2.3 memcmp函数

2.4 memset函数


在编写代码时,我们经常需要实现对字符串的一些操作,如:求字符串的长度,进行两字符串间的比较,字符串的拷贝等,考虑到这些操作实现的高频性,为使编程更加的高效,C语言中为我们提供了字符串系列库函数可实现这些操作;除此之外,C语言中还提供了内存操作系列库函数可以实现对内存中内容拷贝、比较等操作。接下来,我们就来了解以下这些函数是如何使用以及如何实现相应功能的。

前 言

C语言本身是没有字符串类型的,字符串通常存放在常量字符串中或者字符数组中。

以下所分析函数若无特别注明,均包含在string.h头文件中。

1 字符串函数

1.1 求字符串长度函数

1.1.1 strlen函数

声明:size_t strlen ( const char * str );

其中:

①size_t为函数返回值类型,32位平台下为经过重命名后的unsigned int类型,64位平台下为经过重命名后的unsigned long long类型,函数返回字符串长度;

②使用const进行约束表示我们只需要计算字符串的长度,不应该更改字符串原有内容;

③函数参数为char*型指针,需要传入的是字符串的首地址;

接下来我们来测试使用一下函数:

 如图1所示,我们定义了一个字符数为25的字符串,这里的25包括了字符'\0';通过调用strlen函数计算该字符串长度,并输出返回值,发现返回的字符串长度为24,那是不是说strlen函数是不将'\0'计算在内的;为验证此想法,我们在原字符串中间又添加了一个字符'\0'(如图2,此时包括'\0'在内的字符数为26),再次调用strlen函数计算该字符串长度,并输出返回值,发现返回的字符串长度为10(即第一个'\0'之前的字符数)。由此我们可以发现,strlen函数计算的是从传入地址开始到第一次遇到'\0'结束之间的字符个数(不包括'\0'),所以使用函数时需要注意:所被计算的字符串必须要以'\0'结束,否则strlen函数将会继续计算直到遇到'\0',此时计算值便为随机值。了解了strlen函数的使用后,接下来我们可以自己模拟实现一下strlen函数,以下采用三种不同的方法来实现:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
#include <assert.h>

//方法一:模拟实现strlen
size_t my_strlen1(const char* str) {
    assert(str);  //确保传入指针不为空,否则报错,包含在头文件assert.h中

    size_t count = 0;
    //从起始地址开始,当*str == '\0'时循环结束
    while (*str++) {
        count++;
    }
    return count; //返回字符串长度
}

//方法二:递归模拟实现strlen
size_t my_strlen2(const char* str){
    assert(str);

	if (*str)
	{
		return my_strlen2(str + 1) + 1;
	}
	else
	{
		return 0;
	}
}

//方法三:指针-指针模拟实现strlen
size_t my_strlen3(const char* str){
    assert(str);

	const char* ret = str; //记录起始地址
    while(*str++){
        ;
    }
    return str - ret - 1;
}

//测试
int main() {
    
    char str[] = "We have a bright future!";
    printf("字符串:%s\n", str);
    printf("字符串长度:%zd\n", my_strlen1(str)); // %zd为size_t类型数据打印格式
    printf("字符串长度:%zd\n", my_strlen2(str));
    printf("字符串长度:%zd\n", my_strlen3(str));

    return 0;
}

测试结果:

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

1.2.1 strcpy函数

字符串拷贝函数
声明: char* strcpy ( char * destination , const char * source );

其中:

①char*为函数返回值类型,用于返回指向目标字符串起始地址的指针;

②使用const进行约束表示不应该更改字符串原有内容;

③函数参数为两个char*型指针,需要传入的是目标字符串和拷贝源字符串的首地址;

测试使用:

 (1)如图1所示,这里我们定义了字符串str1作为拷贝的目标字符串和字符串str2作为拷贝的来源字符串,为了更清楚的观察目标字符串内容的变化,这里以*串初始化字符串,调用函数并输出目标字符串,打开局部变量窗口观察字符串str1的变化,发现:strcpy函数将包括'\0'在内的所有字符都拷贝到了目标空间中,这样使得返回目标字符串的起始地址并输出后,遇到'\0'便停止输出。因此如图最终输出结果没有目标字符串中后面的*号了。

 (2)那目标字符串的定义是如何要求的呢?图1中我们定义了大于源字符串长度的目标字符串,于是在图2中,我们将目标字符串的长度减小至小于源字符串长度,运行发现报出了栈被破坏的异常提醒;接着在图3中,我们定义了一个字符型指针指向一个常量字符串,并将指针作为参数传给strcpy函数,运行发现报出了访问冲突的异常,这是因为我们在将内容拷贝到目标字符串中时,会修改目标字符串中原有的内容,而常量字符串是存储在静态区中不可修改的,所会发生访问冲突。由此可以发现,当使用strcpy函数时,需保证目标字符串的长度是足够大的才能完成拷贝,且目标空间的内容必须是可修改的。

基于以上测试可以总结出以下strcpy函数使用的注意事项:

源字符串必须以 '\0' 结束;

②函数会将源字符串中的 '\0' 拷贝到目标空间;

目标空间必须足够大,以确保能存放源字符串;

④目标空间的内容必须是可修改的。

了解了strcpy函数的使用后,接下来我们可以自己模拟实现一下strcpy函数:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
#include <assert.h>

//模拟实现strcpy
char* my_strcpy(char* dest, const char* src) {
	assert(dest);
	assert(src);

	char* ret = dest;  //记录目标空间起始地址
	while (*ret++ = *src++) {
		;
	}

	return dest;  //返回目标空间起始地址
}

int main() {

	char str1[] = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
	char str2[] = "We have a bright future!";
	printf("%s\n", my_strcpy(str1, str2));
	return 0;
}
测试结果:

1.2.2 strcat函数

字符串拼接函数
声明: char * strcat ( char * destination , const char * source );

其中:

①char*为函数返回值类型,用于返回指向目标字符串起始地址的指针;

②使用const进行约束表示不应该更改字符串原有内容;

③函数参数为两个char*型指针,需要传入的是目标字符串和拼接源字符串的首地址;

测试使用:

(1)如图1所示,为了更明显的观察到调用函数前后目标空间中内容的变化,这里初始化目标字符串中间设置有'\0',并以'*'串初始化'\0'之后的空间。逐过程进行调试,并打开局部变量窗口观察目标空间中内容的变化,发现:调用函数并运行后 ,源字符串从目标空间中的'\0'处开始将字符逐个向后拼接,直到遇到源字符串中的'\0',并将'\0'也拼接到目标空间中才结束。

 (2)那么字符串能否实现自己拼接自己呢?如图2所示,这里调用函数期望将str1字符串中的内容继续拼接到str1字符串后,但运行调试时报出了异常并陷入了死循环,观察局部变量窗口中str1字符串的变化,发现str1空间中在不断重复原字符串中'\0'前的内容(a bright),并且超出了原有空间。分析:根据(1)中所述函数使用特性,拼接会从目标字符串中的第一个'\0'出开始,到源字符串的第一个'\0'结束,而当字符串自己与自己拼接时,目标空间与源空间重叠,从拼接开始源字符串(目标字符串)的'\0'就被自己的第一个非'\0'字符(此处为'a')给覆盖了,即永远不会找到源字符串中的'\0',也就永远不会结束拼接,从而导致死循环。

基于以上测试可以总结出以下strcat函数使用的注意事项:

源字符串必须以 '\0' 结束;

②目标空间必须有足够的大,能容纳下源字符串的内容;

③目标空间的内容必须是可修改的;

④不能自己与自己拼接。

了解了strcat函数的使用后,接下来我们可以自己模拟实现一下strcat函数:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <assert.h>

//模拟实现strcat
char* my_strcat(char* dest, const char* src) {
	assert(dest);
	assert(src);

	char* ret = dest;
	while (*ret) {
		ret++;
	}
	while (*ret++ = *src++) {
		;
	}

	return dest;
}

int main() {

	char str1[40] = "We have ";
	char str2[] = "a bright future!";
	printf("字符串1:%s\n", str1);
	printf("字符串2:%s\n", str2);
	printf("合并后字符串:%s\n", my_strcat(str1, str2));

	return 0;
}

测试结果:

1.2.3 strcmp函数

字符串比较函数

声明:int strcmp ( const char * str1, const char * str2 );

其中:

①int为函数返回值类型,返回字符串比较情况;

②使用const进行约束表示不应该更改字符串原有内容;

③函数参数为两个char*型指针,需要传入的是两字符串的首地址;

测试使用:

 如图所示,这里设置了三组不同的字符串进行比较,可以发现,当遇到不同的字符时,如果str1中的字符大于str2中的字符则返回1,小于则返回-1,若比较完字符串中的所有内容都相等则返回0。以下总结:

strcmp函数使用的注意事项:

第一个字符串大于第二个字符串,则返回大于0的数字;

②第一个字符串等于第二个字符串,则返回0 ;

第一个字符串小于第二个字符串,则返回小于0的数字。

接下来我们可以自己模拟实现一下strcmp函数:

#define _crt_secure_no_warnings 1
#include <stdio.h>
#include <assert.h>

//模拟实现strcmp
int my_strcmp(const char* str1, const char* str2) {
	assert(str1);
	assert(str2);

	while (1) {
		if (*str1 != *str2) {
			return *str1 - *str2;
		}
		else if(*str1 == '\0') {
			return 0;
		}
		str1++;
		str2++;
	}
}

int main() {

	char str1[] = "we have a bright future!";
	char str2[] = "we have a bright future!";
	char str3[] = "we have a future!";
	printf("字符串1:%s\n", str1);
	printf("字符串2:%s\n", str2);
	printf("字符串3:%s\n", str3);
	if (my_strcmp(str1, str2)) {
		printf("字符串1和2不相等!\n");
	}
	else {
		printf("字符串1和2相等!\n");
	}

	if (my_strcmp(str1, str3)) {
		printf("字符串1和3不相等!\n");
	}
	else {
		printf("字符串1和3相等!\n");
	}

	return 0;
}

测试结果:

1.3 长度受限制字符串函数

与不受限制的字符串函数相比,受限制的字符串函数在参数上都多了一个size_t类型的参数,用来限制字符串间拷贝、拼接与比较的字符个数。

1.3.1 strncpy函数

限制长度的字符串拷贝函数

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

测试使用:

 (1)如图1所示,这里调用函数将str2中的前7个字符拷贝到了str1中,相比于strcpy函数,由于限制了拷贝字符数,没有将'\0'拷贝到目标空间中,所以最终输出时,除了输出了拷贝内容,还继续输出了'*'串,直到遇到目标空间中原有的'\0'才停止。

(2)如图2所示,这里调用函数时限制拷贝字符数为20个,但源字符串只有15个字符,调试观察目标空间内容变化发现:在将源字符串的15个字符拷贝到目标空间后,剩余的5个额度的拷贝字符数都使用了'\0'来完成 。

基于以上测试可以总结出以下strncpy函数使用的注意事项:

①函数拷贝num个字符从源字符串到目标空间;

②若源字符串长度小于num,则拷贝完源字符串之后,在目标的后边追加'\0',直到num个。

接下来模拟实现strncpy函数:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
#include <assert.h>

//模拟实现strncpy
char* my_strncpy(char* dest, const char* src, size_t num) {
	assert(dest);
	assert(src);

	char* ret = dest;
	while (num--) {
		if (*src) {
			*ret++ = *src++;
		}
        else {
			*ret++ = '\0';
		}
	}

	return dest;
}

int main() {

	char str1[] = "********************************";
    char str2[] = "********************************";
	char str3[] = "We have a bright future!";
	printf("%s\n", my_strncpy(str1, str3, 7));
    printf("%s\n", my_strncpy(str2, str3, 30));

	return 0;
}

测试结果:

 1.3.2 strncat函数

限制长度的字符串拼接函数

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

测试使用:

如图所示,通过局部变量窗口观察目标空间内容变化,发现strncat函数与strcat函数一样都是从目标空间中的'\0'处开始向后拼接,不同的是由于限制了字符数,所以这里拼接完相应数量的字符后还没到达源字符串的'\0'处,但strncat函数在拼接完相应数量的字符后,又自动在后一个位置添加了'\0',使得可以输出从起始地址到拼接后的最后的一个字符,而不再输出目标空间中'\0'后的其他内容。

接下来模拟实现strncat函数:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
#include <assert.h>

//模拟实现strncat
char* my_strncat(char* dest, const char* src, size_t num) {
	assert(dest);
	assert(src);

	char* ret = dest;
	while (*ret) {
		ret++;
	}
	while (num--) {
		*ret++ = *src++;
	}
	*ret = '\0';

	return dest;
}

int main() {

	char str1[] = "We have \0xxxxxxxxxxxxxxxxxx";
	char str2[] = "a bright future!";
	printf("字符串1:%s\n", str1);
	printf("字符串2:%s\n", str2);
	printf("限制长度合并后字符串:%s\n", my_strncat(str1, str2, 16));

	return 0;
}

 测试结果:

1.3.3 strncmp函数

限制长度的字符串比较函数

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

测试使用:

 如图所示,strncmp函数只进行限制长度下两字符之间的比较,不论后边的内容是否相等,只要在限制长度下的字符串相等则返回0,若是限制长度内一旦两字符串中有不相等的字符,则返回非零值。

接下来模拟实现strncmp函数:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
#include <assert.h>

//模拟实现strncmp
int my_strncmp(const char* str1, const char* str2, size_t num) {
	assert(str1);
	assert(str2);

	while (num--) {
		if (*str1 != *str2) {
			return *str1 - *str2;
		}
		str1++;
		str2++;
	}
	return 0;
}

int main() {

	char str1[] = "we have a bright future!";
	char str2[] = "we have a future!";
	printf("字符串1:%s\n", str1);
	printf("字符串2:%s\n", str2);
	if (my_strncmp(str1, str2, 16)) {
		printf("字符串1和2在限制长度下不相等!\n");
	}
	else {
		printf("字符串1和2在限制长度下相等!\n");
	}
	return 0;
}

测试结果:

1.4 字符串查找函数

1.4.1 strstr函数

字符串查找函数:在字符串str1中查找是否存在字符串str2

声明:char * strstr ( const char *str1, const char * str2);

其中:

①第一个char*为返回值类型,如果查找成功则函数返回指向字符串str2第一次在字符串str1中出现的位置的指针,如果查找失败则返回空指针。

测试使用:

 分析:如上图所示,要在"afgbrzfdybrzsdf"中查找"brzs",基本思想就是从字符串str1的第一个字符开始逐个向后比较,第一次比较到相同字符时记录下起始位置,接着继续向后比较,若中途有不同字符,则在字符串str1中向中断位置之后重新寻找第一个相同字符,若是遍历完字符串str1后仍未找到字符串str2,则返回空指针,若是找到了就返回记录的第一个字符相等的起始位置。

基于以上思想接下来模拟实现strstr函数:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
#include <assert.h>

//模拟实现strstr
char* my_strstr(const char* str1, const char* str2) {
	assert(str1);
	assert(str2);

	const char* ret1 = str1;
	const char* ret2 = str2;
    
    //若是待查找字符串为空字符串则直接返回str1
	if (!*str2) {
		return (char*)str1;
	}
    //若是后面可被查找的字符串长度小于待查找字符串长度,则跳出循环返回空指针
	while (strlen(ret1) >= strlen(ret2)) {
		str1 = ret1;
		str2 = ret2;
		while (*str2 && *str1 == *str2) {
			str1++;
			str2++;
		}
		if (!*str2) {
			return (char*)ret1;
		}
		ret1++;
	}

	return NULL;
}

int main() {

	char str1[] = "afgbrzfdybrzsdf";
	char str2[] = "brzs";
	char str3[] = "brzd";
	if (strstr(str1, str2)) {
		printf("%s\n", my_strstr(str1, str2));
	}
	else {
		printf("字符串'%s'中不存在字符串'%s'\n", str1, str2);
	}
	
	if (strstr(str1, str3)) {
		printf("%s\n", my_strstr(str1, str3));
	}
	else {
		printf("字符串'%s'中不存在字符串'%s'\n", str1, str3);
	}

	return 0;
}

测试结果:

1.4.2 strtok函数

字符串分割函数,根据指定字符分割字符串

声明:char * strtok ( char * str, const char * sep );

其中:

①sep参数是个字符串,定义了用作分隔符的字符集合;

②第一个参数指定的字符串中包含了0个或多个由sep字符串中一或多个分隔符分割的标记;

③strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针(注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。)

④strtok函数的第一个参数不为空,函数找到str中第一个标记,将保存它在字符串中的位置。

⑤strtok函数的第一个参数为空,函数将在同一个字符串中被保存的位置开始,查找下一个标记。

⑥如果字符串中不存在更多的标记,则返回 NULL 指针。

测试使用:

1.5 错误信息报告函数

这里介绍两个错误报告函数:strerror函数包含在error.h头文件中,perror包含在stdio.h头文件中。

1.5.1 strerror函数

声明:char * strerror ( int errnum );

其中:

errnum为错误码,被定义为全局变量;

②函数返回指向错误码所对应的错误信息的指针

测试使用:

 如图所示:不同的错误码对应的不同的错误信息。当程序运行出错时就会返回对应的错误码,以此得到对应的错误信息,上图中执行文件打开操作,若文件顺利打开,指向该流的文件指针就会被返回。但此时对应路径中没有该文件,即文件打开失败,返回NULL,并把错误代码存在errnum中。此时获取最新的错误码并调用strerror函数,即可输出对应返回的错误信息。

1.5.2 perror函数

声明:void perror(const char* str);

其中:

①perror函数调用后会直接输出当前程序运行对应的错误信息;

②str是希望在错误信息前增加输出的内容。

测试使用:

2 内存操作函数

内存操作函数即是以字节为单位对内存空间中的内容进行相应的拷贝、比较、设置等操作,与字符串操作函数相比,内存操作函数不限制函数参数类型,只需要确定好要操作的内存空间长度(字节数),即可对任意类型的数据所在的内存空间进行相关操作。

2.1 memcpy函数

内存拷贝函数

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

其中:

函数memcpysource的位置开始向后复制num个字节的数据到destination的内存位置;

②函数在遇到 '\0' 的时候并不会停下来;
③如果 source destination 有任何的重叠,复制的结果都是未定义的;
④使用void*定义参数使得函数可以操作任意类型的数据的内存空间。

测试使用:

 如图所示,这里进行了两种不同的测试,左边第一次调用函数进行内存拷贝是将一个数组中前16个字节的空间内容拷贝到了另一个数组中的前16个字节空间中,不存在空间重叠,设置16个字节是因为其为int型数据大小(4个字节)的整数倍(4倍),所以可以通过数组中前四个元素的变化来了解函数的使用;右边第二次调用函数将目标空间的起始地址设置在源空间起始地址偏移两个字节的空间,即目标空间与源空间存在重叠,但是从运行结果可以发现,即使存在内存重叠,函数调用后也成功完成了拷贝。但其实C语言规定中memcpy函数只需要实现不会重叠的内存空间的拷贝即可,而对存在内存空间重叠的拷贝可由接下来的memmove函数实现。图中使用的是VS2022,在VS的库函数中,memcpy函数也是可以实现存在内存重叠的拷贝的,但这不代表所有编译器下库中的memcpy函数都能实现重叠内存的拷贝,只能说memmove函数是包含memcpy函数功能的,反之不然。若是不能实现重叠内存拷贝的memcpy函数,则在第二次函数调用后会造成如下结果:重复拷贝目标起始地址前内存空间中的内容(1 2)

 基于测试接下来模拟实现C语言规定下的memcpy函数:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
#include <assert.h>

//模拟实现memcpy
void* my_memcpy(void* dest, const void* src, size_t num) {
	assert(dest);
	assert(src);

	void* ret = dest;
	while (num--) {
		//*((char*)dest)++ = *((char*)src)++;  //这种写法不一定在所有编译器下都能通过
		*(char*)dest = *(char*)src;  //以字节为单位进行访问
		dest = (char*)dest + 1;
		src = (char*)src + 1;
	}
	return ret;
}

int main() {

	int arr1[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	int arr2[10] = { 0 };
	my_memcpy(arr2, arr1, 16);
	my_memcpy(arr1 + 2, arr1, 16);
	int length1 = sizeof(arr1) / sizeof(arr1[0]);
	int length2 = sizeof(arr2) / sizeof(arr2[0]);
	int i = 0;
	printf("\n无重叠内存拷贝:\n");
	for (i = 0; i < length2; i++) {
		printf("%d ", arr2[i]);
	}
	printf("\n\n存在重叠内存拷贝:\n");
	for (i = 0; i < length1; i++) {
		printf("%d ", arr1[i]);
	}

	return 0;
}

2.2 memmove函数

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

其中:

①和memcpy的差别在于memmove函数处理的源内存块和目标内存块是可以重叠的,因此如果源空间和目标空间出现重叠,应该使用memmove函数处理。

除对重叠内存拷贝的处理外,memmove函数与memcpy函数的实现类似,而要处理重叠内存的拷贝,关键在于拷贝的顺序,需要根据具体情况判断应该从前向后拷贝还是从后向前拷贝才不会造成原有待拷贝内容提前被其它数据覆盖。

接下来模拟实现memmove函数:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
#include <assert.h>

//模拟实现memmove
void* my_memmove(void* dest, const void* src, size_t num) {
	assert(dest);
	assert(src);
	
	void* ret = dest;
	//从前向后拷贝
	if (dest < src) {
		while (num--) {
			//*((char*)dest)++ = *((char*)src)++;
			*(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 arr1[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	int arr2[10] = { 0 };
	my_memmove(arr2, arr1, 16);
	my_memmove(arr1 + 2, arr1, 16);
	int length1 = sizeof(arr1) / sizeof(arr1[0]);
	int length2 = sizeof(arr2) / sizeof(arr2[0]);
	int i = 0;
	printf("\n无重叠内存拷贝:\n");
	for (i = 0; i < length2; i++) {
		printf("%d ", arr2[i]);
	}

	printf("\n\n存在重叠内存拷贝:\n");
	for (i = 0; i < length1; i++) {
		printf("%d ", arr1[i]);
	}

	return 0;
}

 测试结果:

2.3 memcmp函数

声明:int memcmp ( const void * ptr1,  const void * ptr2,  size_t num );

其中:

①函数比较从ptr1ptr2指针开始的num个字节,若中途遇到空间中存储的字节内容不同,则返回非零值;若比较完仍未遇到,则返回0。

下面是memcmp函数的模拟实现:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
#include <assert.h>

//模拟实现memcmp
int my_memcmp(const void* ptr1, const void* ptr2, size_t num) {
	assert(ptr1 && ptr2);

	while (num--) {
		if (*(char*)ptr1 != *(char*)ptr2) {
			return *(char*)ptr1 - *(char*)ptr2;
		}
		ptr1 = (char*)ptr1 + 1;
		ptr2 = (char*)ptr2 + 1;
	}
	return 0;
}

int main()
{
	char str1[] = "DWgaOtP12df0";
	char str2[] = "DWGAOTP12DF0";
	int flag;
	flag = my_memcmp(str1, str2, sizeof(str1));  //比较范围包括整个字符串
	if (flag > 0) printf("'%s' is greater than '%s'.\n", str1, str2);
	else if (flag < 0) printf("'%s' is less than '%s'.\n", str1, str2);
	else printf("'%s' is the same as '%s'.\n", str1, str2);
	return 0;
}

测试结果:

2.4 memset函数

声明:void * memset (void * ptr, int value, size_t num);

其中:

①函数将从ptr指针开始的num个字节的内存空间中的内容都设置为value值。

测试使用:

(1)如图1所示,memset函数以字节为单位设置内存空间内容,这里以int型数组为例,调用函数设置数组起始地址开始的9个字节内存空间中的内容为15(十六进制表示则为0x0f),查看arr1数组的内存空间发现其中中前9个字节的空间内容均变成了0f, 再观察数组元素变化,发现前三个元素值均发生了改变,这是因为一个int型数据大小为4个字节,而数组元素是由低地址向高地址存储的,所以当前9个字节空间发生改变时,数组的前三个元素的值也发生了改变。

(2)既然memset函数是以字节为单位设置内存空间, 那如果需要设置的value值大小超过了一个字节会如何呢?如图2所示,这里设置value值为366(内存中用十六进制表示其补码:0x16e),调用函数运行发现:数组前9个字节的内存空间均变成了6e,这是value值的低字节数据。可以由此推出:当value值大小超过了一个字节时,函数会将其低字节数据存储到要设置的内存空间中。

以上是我对字符串函数与内存操作函数的一些学习记录总结,如有错误,希望大家帮忙指正,也欢迎大家给予建议和讨论,谢谢!

  • 11
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大米饭_Mirai

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值