C语言中几个常见的字符串函数及内存函数分析及其模拟实现

 C语言中几个常见的字符串函数和内存函数分析及其模拟实现


目录

 

一. 字符串函数

1.strcmp 函数

2.strncmp  函数

3. strcat  函数

4.strncat  函数

5.strstr 函数

6. strcpy 函数

7.strncpy 函数 

8.strlen  函数

9.strerror  函数

10. strtok 函数

二.内存函数

11.memcpy 函数

12. memmove  函数

13.memcmp   函数

一. 字符串函数

1.strcmp 函数

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

   比较俩个字符串函数      比较的方式是逐一比较 俩个字符串中字符的ascii码值。

比较字符串内容只能用strcmp函数,不可直接用    > , < 来比较,如果直接用大小与号来比较,事实上比较的是俩个字符串的首元素的地址,而非字符串内容的比较。

模拟实现strcmp函数,我们需要用到指针,利用指针来实现单独访问字符串的每一个字符并进行比较

代码实现:

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

int my_strcmp(const char* s1, const char* s2)
{
    assert(s1 && s2);     //assert函数,判断起始地址的合法性,如果为空指针则报错
    while (*s1 == *s2)
    {
        if (*s1 == '\0')
        {
            return 0;
        }
        s1++;
        s2++;
    }
    if (*s1 > *s2)
    {
        return 1;
    }
    else
        return -1;

}



int main()
{
    char arr1[] = "abcdef";

    char arr2[] = "abcde";

    printf("%d", my_strcmp(arr1, arr2));
    return 0;
}

2.strncmp  函数

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

与strcmp 一直比较直到字符串结束 不同的是,strncmp   比较到出现不一样的字符或者出现    ' \0' 或者 num 个字符全部比较完。

代码实现

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

int my_strncmp(const char* str1, const char* str2, int num)
{
	assert(str1 && str2);

	if (!num)  //  num = 0 直接返回0
	{
		return 0;
	}
	while(--num && *str1 && *str1 == *str2)  // 前置--  ,判断str 是否为 '\0 ',比较str,str2
	{
		str1++;
		str2++;
	}
	return *str1 - *str2;
}

int main()
{

	char arr1[] = "abcdef";
	char arr2[] = "abcdeg";

    int ret  = my_strncmp(arr1, arr2, 6);

	printf("%d", ret);

	return 0;
}

3. strcat  函数

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

Appends (附加) a copy of the source string to the destination string.The terminating (终端) null character in destination is overwritten by the first charaster of source,and a null-character is include at the end of the new string formd by the concatenation of both in destinion.

将源字符串的内容复制到目标字符串。目标字符串的终止字符 即 ' \0' ,将被源字符串的第一个字符串覆盖,并且,新形成的字符串的末尾包含一个空字符。

由函数的定义我们可以得知实现,模拟实现stract 函数需要掌握的几个关键点。

1.源字符必须要以 ' \0 ' 结束。

2.目标空间必须足够大,可以容纳源字符串内容。

3.目标空间必须可修改。

代码实现:

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

char* my_strcat(char* s1,const char* s2)
{
    assert(s1 && s2);

    char* ret = s1;
    while (*s1)
    {
        s1++;
    }
    while (*s1++ = *s2++)
    {
        ;
    }
    return ret;
}


int main()
{
    char arr1[20] = "hello ";

    char arr2[] = " world";

    char* ret = my_strcat(arr1, arr2);

    printf("%s", ret);
    return 0;
}

4.strncat  函数

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

strncat 函数 与 strcat 函数相比 更为精密。

源字符串的num个字符的内容复制到目标字符串。目标字符串的终止字符 即 ' \0' ,将被源字符串的第一个字符串覆盖,并且,新形成的字符串的末尾包含一个空字符。

代码实现

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

char* my_strncat(char* s1, const char* s2, int num)
{
	assert(s1 && s2);

	char* ret = s1;
	while (*s1)
	{
		s1++;
	}
	while (num-- && *s2)  // 如果 *s2 判断是否为 ' \0 ',避免越界访问引起错误
	{
		*s1++ = *s2++;
	}
	*s1 = '\0'; // 上面s1后置++,直接赋值  \0  即可。
	return ret;
}


int main()
{
	char arr1[40] = "hello \0xxxxxxxx";

	char arr2[] = "worlddddd";

	char* ret = my_strncat(arr1, arr2, 5);

	printf("%s", ret);
	return 0;
}

5.strstr 函数

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

Returns a pointer to the first occurrence of str2 in str1,or a null pointer if str2 is not part of str1.

如果字符串2,在字符串1中出现,则返回 字符串1中出现字符串2的字符串2的首地址,如果字符串2没有在字符串1中出现则返回 空指针。

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

char* my_strstr(const char* str1, const char* str2)
{

	char* s1 = str1;

	char* s2 = str2;

	char* str = str1;


	while (*str)
	{
		s1 = str;
		s2 = str2;
		while (*s1 && *s2 && (*s1 == *s2))
		{
			s1++;
			s2++;
		}

		if (*s2 == '\0')
		{
			return str;
		}
	
			str++;
	}
	return NULL;
}

int main()
{
	char arr1[] = "asdfgabcdfe";

	char arr2[] = "abcd";

	char* ret = my_strstr(arr1, arr2);

	if (ret == NULL)
	{
		printf("没找到");
	}
	else
		printf("%s", ret);

	return 0;
}

6. strcpy 函数

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

将源字符串复制到目标字符串中。包括源字符串中的 ' \0' 。

模拟实现时需注意

1. 源字符串必须以 ' \0 ' 结束 。

2. 复制时,包括源字符串的 ' \0 '。

3. 目标字符串的空间足够大,可以容纳源字符串的内容。

4. 目标字符串必须可以更改。

代码:

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

char* my_strcpy(char* dest, char* sru)
{
	char* s1 = dest;
	
	assert(dest && sru);

	while (*dest++ = *sru++)
	{
		;
	}
	return s1;
}


int main()
{
	char arr1[20] = " ";

	char arr2[] = "hai";

	my_strcpy(arr1, arr2);

	printf("%s",arr1);

	return 0;
}

7.strncpy 函数 

char* strcpy(char* destination,const char* source, size_t num)

将源字符串复制 num 个字符的内容 到目标字符串中。包括源字符串中的 ' \0' 。

代码实现

char* my_strncpy(char* dest, char* sru, int num)
{
	assert(dest && sru);

	char* s1 = dest;

	while (num-- && *sru)   //防止越界
	{
		*dest++ = *sru++;
	}
	*dest = '\0';    // 在复制完后,添加\0
	
	return s1;
}


int main()
{
	char arr1[50] = "a \0xxxxxxxxxxxxxxxxx ";

	char arr2[] = "hai";

	my_strncpy(arr1, arr2, 10);

	printf("%s", arr1);

	return 0;
}

既要防止越界,也要处理好结尾(\0)

8.strlen  函数

size_t strlen (const char* str)

1. 字符串以 '\0'为结束标志,strlen 函数返回的是字符串中 ' \0’前出现字符的个数,不包括 '\0'

2. size_t 为函数的返回值类型,是无符号的。

代码实现:

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

int my_strlen(char* s1)
{
	assert(s1);
	int count = 0;
	while (*s1)
	{
		s1++;
		count++;
	}
	return count;
}

int main()
{
	char arr1[] = "qweraaa";

	int ret = my_strlen(arr1);

	printf("%d", ret);

	return 0;
}

9.strerror  函数

char * strerror (int errum)  ;        

必须包含的头文件 <errno.h>

返回错误码所对于的错误信息。

例:

#include<limits.h>
#include<errno.h>
int main()
{
	int* p = (int*)malloc(INT_MAX);
	if (p == NULL)
	{
		printf("%s ", strerror(errno));  // errno为一个全局变量,是错误码,使用时需引用头文 
                                         //    <limits.h>
	}
}

 当我们用malloc函数 向内存申请过于大的空间时 ,程序会报警,我们就可以用 strerror 函数得到错误对应的信息。 malloc 函数 后面的博客会有分析。

10. strtok 函数

char*  strtok ( char* str ,const char* sep ) ;

sep 是个自定义参数,定义了 用作分隔符 的字符集合。

第一个参数给定一个字符串,它包含了0个或者多个有sep字符串中一个或多个分隔符分隔的标记。

1. strtok 函数 找到str中下一个标记,将其用 ' \0' 结束,返回一个指向这个标记的指针。

2. strtok函数会改变该字符串的内容,一般使用strtok函数的都是拷贝的内容,并且可修改。

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

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

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

有点复杂,作者不会模拟实现,以后或许。

但是作者可以简单分析如何更好的使用这个函数。

#include<string.h>

int main()
{
	char arr1[] = "hello worid /siwen.2022_8_7";

	char arr2[40] = { 0 };

	strcpy(arr2, arr1);

	char sep[] = " /.";

	printf("%s\n", strtok(arr2, sep));
	printf("%s\n", strtok(NULL, sep));
	printf("%s\n", strtok(NULL, sep));
	printf("%s\n", strtok(NULL, sep));

	return 0;
}

 上述的代码显得不太聪明,当字符串和分隔符太多时,代码会显得特别冗余,也容易出错。

我们可以用for 循环 简单高效的实现。


#include<string.h>

int main()
{
	char arr1[] = "hello worid /siwen.2022_8_7";

	char arr2[40] = { 0 };

	strcpy(arr2, arr1);

	char sep[] = " /.";

	char* str = NULL;

	for (str = strtok(arr2, sep); str != NULL; str = strtok(NULL, sep))
	{
		printf("%s\n", str);
	}

	return 0;
}

二.内存函数

11.memcpy 函数

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

1.memcpy 函数,从source 位置开始复制 num个字符的数据到destination 位置。

2. 这个函数遇到 ' \0 ' ,不会停止。

3. 如果 source 和 destination 有任何的重叠,复制的结果都是未定义的。(即不适用一段数据的自我拷贝,但是我们可以用 memmove函数来实现)

代码实现

void* my_memcpy(void* dest,const void*sur,int num )
{
	assert(dest && sur);
	void* ret = dest;
	while (num--)
	{
		*((char*)dest) = *((char*)sur);
		dest = (char*)dest + 1;
		sur = (char*)sur + 1;
	}
	return  ret;
}

int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8 };

	int arr2[10] = {0};

	my_memcpy(arr2, arr1, 20);

	int i = 0;
	int sz = sizeof(arr2) / sizeof(arr2[0]);

	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr2[i]);
	}
	return 0;
}

12. memmove  函数

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

可以理解为 memcpy 的 plus 版本 ,

memmove处理的源内存块 和 目标内存块是可以重叠的

代码实现

void* my_memmove(void* dest, const void* sur, int num)
{
	void* ret = dest;
	if (dest < sur)
	{
		while (num--)
		{
			*((char*)dest) = *((char*)sur);
			dest = (char*)dest + 1;
			sur = (char*)sur + 1;
		}
		return ret;
	}

	else
	{
		while (num--)
		{
			*((char*)dest + num) = *((char*)sur + num);
		}
		return ret;
	}

}

int main()
{
	int arr1[10] = { 1,2,3,4,5,6,7,8,9 };

	my_memmove(arr1, arr1+2, 20);

	int i = 0;
	int sz = sizeof(arr1) / sizeof(arr1[0]);

	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr1[i]);
	}
	return 0; 
}

简单分析一下这个代码

 如图所示,当dest  < source 时 ,数据的复制顺序为 从前到后 ,依次把 3复制到1 的位置上 ,4 复制到2的位置上,依次复制。但是如果倒叙复制,7复制到5 的位置上,而5本该复制到3的位置 ,5却被提前改为了 7,修改了源数据块的内容, 这个函数就达不到我们预期的目标。我们只能采取顺序复制

 

当 dest  > source 同时 ,小于 source + num 个字节 的 地址时 ,只有 倒叙 复制 ,才不会改变源内存块的数据内容。

 当 dest < (char*)source +num -1 时 ,采取顺序复制 还是倒叙复制 都不会影响源内存块的数据。

13.memcmp   函数

int memcmp (const void* ptr1, const void* ptr2,size_t num)

比较从 ptr1 和ptr2 指针开始的 num个 字节

1. ptr1 > ptr2   return 一个 大于零的数

2. ptr1 < ptr2   return 一个 小于零的数

3. ptr1 = ptr2,  return  0

有点难,作者不会模拟实现,以后或许吧

可以写个简单的代码实现一下这个函数


int main()
{

	int arr1[10] = { 1,2,3,4,5,6 };

	int arr2[10] = { 1,2,3,4,5,8 };

	int ret = memcmp(arr1, arr2, 24);

	if (ret > 0)
	{
		printf("ptr1>ptr2");

	}

	else if (ret < 0)
	{
		printf("ptr1>ptr2");

	}

	else if(ret == 0)
	{
		printf("ptr1 = ptr2");

	}
	return 0;
}

本文介绍的所有函数其头文件都可以用   <string.h>

我或许大抵是写完了 ,感谢观看。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值