字符串函数和内存函数

本文详细介绍了C语言中的字符串处理函数,如strlen、strcpy、strcmp、strncmp、strncpy、strcat、strstr等,以及内存操作函数memcpy、memmove和memset。通过实例展示了这些函数的使用方法和注意事项,帮助读者理解它们的功能和区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

你知道strcmp和strncmp的区别吗?你知道strcpy和strncpy的区别吗?你知道什么是strstr函数吗?你知道什么是strerror函数吗?你知道什么是内存函数吗?

如果你不知道,以下知识可能会给你一定的帮助!

一、字符串函数

1、strlen函数

作用:计算字符串的长度

三种满分实现方法:

方法一(指针减指针):

//注意:size_t的类型是无符号整形
size_t my_strlen1(const char *p)
{
	assert(p);
	const char *end = p;
	while (*end)
	{
		end++;
	}
	return end - p;
}

方法二(直接算count): 

//方法二(直接算count):
size_t my_strlen2(const char  *p)
{
    assert(p);
	int count = 0;
	while(*p++)
	{
		count++;
	}
	return count;
}

方法三(递归方法): 

//方法三(递归方法):
size_t my_strlen3(const char *p)
{
    assert(p);
	if (*p != '\0')
	{
		return 1 + my_strlen3(p + 1);
	}
	else
		return 0;
}
int main()
{
	char a[] = "ab cdefg";
	printf("%d\n",(int)my_strlen1(a));
	printf("%d\n", (int)my_strlen2(a));
	printf("%d\n", (int)my_strlen3(a));
}

2、strcpy函数

原型:char* strcpy(char *e1,const char *e2)   

作用:将字符串e2的内容拷贝到字符串e1中去。

注意:要求目标数组应该大于等于源数组的长度,不然空间不够,strcpy还把源数组的'\0'也一并拷贝过来了

char * my_strcpy(char *e1, const char* e2)
{
	//写具体一点  assert(e1!==NULL&&e2!==NULL)
	assert(e1&&e2);
	char *sta = e1;
	while (*e1++ = *e2++)
	{

	}
	return sta;
}
int main()
{
	char a[] = "hello world";
	char b[] = "hello";
	printf("%s\n", my_strcpy(a, b));
	return 0;
}

3、strcmp函数

原型:int strcmp(const char *e1,const char *e2)

作用:比较两个字符串函数的大小
如果第一个字符串大于第二个,则返回大于0的数
如果第一个字符串小于第二个,则返回小于0的数
如果第一个字符串等于第二个,则返回等于0的数

int my_strcmp(const char *e1, const char *e2)
{
    assert(e1&&e1);
	while (*e1 == *e2&&*e1)
	{
		e1++;
		e2++;
	}
	return *e1 - *e2;
}
int main()
{
	char arr1[] = "hello world";
	char arr2[] = "hello my dear";
	int ret = my_strcmp(arr1, arr2);
	if (ret > 0)
		printf(">\n");
	else if (ret < 0)
		printf("<\n");
	else
		printf("=\n");
	return 0;
}

4、strncmp函数

原型:int strcmp(const char * str1,const char *str2,size_t num)

作用:比较str1和str2前num个字符的大小,如果比较到比num个小的字符时已经有结果了,就可以停止比较了

#include<stdio.h>
#include<string.h>
int main()
{
    char str1[10]="strncmp";
    char str2[10]="strnc";
    int ret = strncmp(str1,str2,3);//返回值为0
    ret=strncmp(str1,str2,7);//返回值为大于0的数,并且只比较前6个字符就会返回
    return 0;
}

5、strncpy函数

原型:char* strcpy(char * str1,const char *str2,size_t num)

返回的是str1的地址

作用:将str2前num个字符拷贝到str1的前num个字符中,如果str1字符串长度小于num,则拷贝完str1后,在目标的后边追加0,知道num个。

#include<stdio.h>
#include<string.h>
int main()
{
	char str1[] = "str";
	char str2[] = "strstrstrncpy";
	printf("%s", strncpy(str2, str1, 5));
	return 0;
}

输出:

 

6、strncat函数

原型:char*  strncat(char * str1,const char *str2,size_t num)

作用:(1)把str2中前num个字符和'\0'都传过去

(2)str2字符串不足num个会在后面补'\0'

#include<stdio.h>
#include<string.h>
int main()
{
	char str1[] = "str";
	char str2[] = "strs\0XXXX";
	printf("%s", strncat(str2, str1, 5));
	return 0;
}

输出:

7、strcat函数

原型:char * my_strcat(char* e1,const char* e2)

作用:将e2的内容连接到e1的内容后面

思路:找到目标字符串中的第一个'\0',如何让*e2覆盖,把arr2字符串的所有内容都赋值过来(包括'\0')

注意 : arr1的长度要内容纳下原内容再加上arr2的内容.

char * my_strcat(char *e1, const char*e2)
{
    assert(e1&&e2);
	char * start = e1;
	while (*e1)
	{
		e1++;
	}
	while (*e2)
	{
		*e1 = *e2;
		e1++;
		e2++;
	}
	return start;
}
int main()
{
	char arr1[10] = "abcd";
	char arr2[] = " efg";
	printf("%s", my_strcat(arr1, arr2));
}

8、strstr函数
 

原型:char * strstr(const char * e1,const char *e2)

strstr函数的作用:从字符串arr1中找到有没有字符串arr2

                                且返回arr2中第一个arr1数组的首地址

char * my_strstr(const char * e1, const char *e2)
{
	assert(e1&&e2);
	const char *valid = e1;
	const char *start = e2;
	while (*valid)
	{
		while (*e1 == *e2&&*e1&&*e2)
		{
			e1++;
			e2++;
		}
        //找到了子串的位置
		if (*e2 == '\0')
		{
			//valid是const char *,因此要注意转换成char*
			return (char*)valid;
		}
		valid++;
		e2 = start;
		e1 = valid;
	}
	return NULL;
}
int main()
{
	char arr1[] = "abbcdbbddefexdbbdd";
	char arr2[] = "bbdd";
	if (my_strstr(arr1, arr2) != NULL)
	{
		printf("%s", my_strstr(arr1, arr2));
	}
	else
		printf("不是它的子串\n");
}

9、strtok函数

原型:char *strtok( char * str, const char*sep)
sep 参数的作用:sep参数是个字符串,定义了用作分隔符的字符集合
规则:(1)当str不是NULL时,函数将找到str中第一个标记(找到sep中的内容),如果strtok                        函数将保存它在字符串的位置,下一次调用就会从这个位置开始查找
           (2)当str是NULL时,函数将在同一个字符串中被保存的位置开始,查找下一个标记
           (3)如果字符串中还存在标记,则返回这一次开始查找时的地址
           (4)如果字符串中不存在更多的标记,则返回NULL的指针

int main()
{
	char a[] = "who: yaki  what:will be more and more excellent!";
	char b[100];
	strcpy(b, a);//strtok函数会改变被操作的字符串,所以在使用strtok函数切分的函数一般都是临时拷贝的内容
	const char *sep = ": ";  //sep的内容有':'和' '还有'\0',但'\0'不是标记,但但但是'\0'作为字符串结束的标志,所以遇到它就会停止
	char *str = NULL;
	for (str = strtok(b, sep); str != NULL; str = strtok(NULL, sep))
	{
		printf("%s\n", str);
	}
	return 0;
}

10、strerror和perror函数

strerror
原型:char * strerror (int errnum)
作用:返回错误码所对应的错误信息

perror(strerror的好兄弟)
原型:void perror(const char *str)
因此,你如果想要用他得到错误信息,那么你就得传一个常量字符串,这个常量字符串是什么都可以

#include<stdio.h>
#include<errno.h> //全局变量errno的头文件,errno是一个专门存储错误信息的全局变量
#include<stdlib.h>//perror的头文件
#include<string.h>//strerror的头文件
#include<limits.h>//INT_MAX的头文件
int main()
{
	printf("%s\n", strerror(0));
	printf("%s\n", strerror(1));
	//都会打印出错误码对应的错误信息,strerror这个函数会返回错误信息的首元素地址
	int *p = (int *)malloc(INT_MAX);//向堆区申请内存,INt_MAX代表整形最大值;CHAR_MAX代表字符型最大值,127
	if(p==NULL)
	{
		printf("%s\n", strerror(errno));//errno这是一个全局变量,用来存储错误码的变量,使用时要引用头文件:#include<errno.h>
	}
	if (p ==NULL)
	{
		perror("haha");//这个“”里面可以不是haha,可以输入任意字符串
	}
	return 0;
}

输出:

二、内存函数

内存函数有什么特别之处呢?它们是以字节为单位处理数据的。

1、memcpy函数

原型:void * memcpy(void * destination , const  void  * source , size_t  num )

作用:(1)函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存                位置这个函数遇到'\0'不会停下来
           (2)如果source和destination有任何重叠,复制的结果都是未定义的

#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--)
	{
		*(char *)dest= *(char *)src;
		((char *)dest)++;
		((char *)src)++;
	}
	return ret;//注意函数的定义,是返回void* 指针,而非函数是void型
}
int main()
{
	int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[10] = { 0 };
	my_memcpy(arr2, arr1, 20);//把前五个数字copy到arr2中
	return 0;
}

memcpy 有个小不足,就是destination和source不能有重叠的部分,那么此时我们就要用另一个函数来实现

2、memmove函数

原型:void * memmove(void *dest,const void *src,size_t num);

作用:不仅能实现memcpy的功能,还允许destination和source能够重叠

#include<stdio.h>
#include<assert.h>
void* my_memmove(void *dest, const void * src, size_t num)
{
	assert(dest&&src);
	void *ret = dest;
	if (dest < src)
	{
		while (num--)
		{
			/*从前往后改变*/
			*(char *)dest = *(char *)src;
			++(char *)dest;
			++(char *)src;
		}
	}
	else
	{
		while (num--)
		{
			/*从后往前改变*/
			*((char *)dest+num) = *((char *)src+num);
			/*注意是+num,而非num-1,此时程序已经执行了num--了*/
		}
	}
	return ret;
}
int main()
{
	int arr1[10] = { 1, 2, 3, 4, 5, 6,7,8,9,10 };
	my_memmove(arr1+3, arr1, 12);
	return 0;
}

3、memset函数

原型:void * memset(void * dest,int c,size_t num);

作用:一个一个字节的改变

#include<stdio.h>
#include<string.h>
int main()
{
	int arr1[10] = { 1, 2, 3, 4, 5, 6,7,8,9,10 };
	memset(arr1, 6, 12);
	注意这个地方不是把前三个数都改成6,而是把它们都改成0x06060606
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值