【C语言】字符串函数和内存函数

目录

字符串函数

strlen

模拟实现strlen

 strcpy

模拟实现strcpy

strcat

模拟实现strcat

strcmp

模拟实现strcmp

strncpy

strncat

strncmp

strstr

模拟实现strstr

strtok

内存函数

memcpy

模拟实现memcpy

memmove

模拟实现memmove

memcmp

memset


字符串函数

strlen

size_t strlen ( const char * str );
  • 字符串以'\0'作为结束标志,strlen函数返回的是字符串中'\0'前面出现的字符个数(不包括'\0')。
  • 参数指向的字符串必须以'\0'结束。
  • 函数的返回值是size_t,是无符号的。

模拟实现strlen

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

//指针-指针
size_t my_strlen1(const char* str)
{
	const char* start = str;
	assert(str != NULL);
	while (*str)
		str++;
	return str - start;
}
//递归
size_t my_strlen2(const char* str)
{
	assert(str != NULL);
	if (*str != '\0')
		return 1 + my_strlen2(str + 1);
	else
		return 0;
}
size_t my_strlen3(const char* str)
{
	assert(str != NULL);
	size_t count = 0;
	while (*str != '\0')
	{
		count++;
		str++;
	}
	return count;
}
int main()
{
	char arr[] = "abcdef";
	size_t len1 = my_strlen1(arr);
	printf("%u\n", len1);
	size_t len2 = my_strlen2(arr);
	printf("%u\n", len2);
	size_t len3 = my_strlen3(arr);
	printf("%u\n", len3);
	return 0;
}

 strcpy

char* strcpy(char * destination, const char * source );
  • 将source字符串拷贝到destination字符串中。
  • 源字符串必须以 '\0' 结束。
  • 会将源字符串中的 '\0' 拷贝到目标空间。
  • 目标空间必须足够大,以确保能存放源字符串。
  • 目标空间必须可变。

模拟实现strcpy

char* my_strcpy(char* dest, const char* src)
{
	assert(dest && src);
	char* ret = dest;
	while (*dest++ = *src++)
	{
		;
	}
	return ret;
}
int main()
{
	char arr1[20] = "";
	char arr2[] = "hello world";
	my_strcpy(arr1, arr2);
	printf("%s\n", arr1);
	return 0;
}

strcat

char * strcat ( char * destination, const char * source );
  • 在destination之后追加一个字符串source。
  • 源字符串必须以 '\0' 结束。
  • 目标空间必须足够大,能容纳下源字符串的内容。
  • 目标空间必须可修改。

模拟实现strcat

char* my_strcat(char* dest, const char* src)
{
	assert(dest && src);
	char* ret = dest;
	//找目标空间的\0
	while (*dest != '\0')
	{
		dest++;
	}
	//追加
	while (*dest++ = *src++)
	{
		;
	}
	return ret;
}

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

strcmp

int strcmp ( const char * str1, const char * str2 );
比较str1和str2,比较的是对应位置的字符大小。
规定:
第一个字符串大于第二个字符串,则返回大于 0 的数字
第一个字符串等于第二个字符串,则返回 0
第一个字符串小于第二个字符串,则返回小于 0 的数字

模拟实现strcmp

int my_strcmp(const char* str1,const char* str2)
{
	assert(str1 && str2);
	while (*str1 == *str2)
	{
		if (*str1 == '\0')
			return 0;
		str1++;
		str2++;
	}
	if (*str1 > *str2)
		return 1;
	else
		return -1;
}

上面的三个函数由于字符串的长度不受限制,所以被认为不安全,因此对应再给出三个较安全的函数:strncpy,strncat,strncmp 

strncpy

char * strncpy ( char * destination, const char * source, size_t num );
这里相比strcpy只增加了一个参数num,指定拷贝多少个字符,如果指定的num超过source则超出的部分补\0

strncat

char * strncat ( char * destination, const char * source, size_t num );
这里相比strcat也是只增加了一个参数num,指定追加多少个字符

strncmp

int strncmp ( const char * str1, const char * str2, size_t num );
num指定要比较多少个字符

strstr

char * strstr ( const char *str1, const char * str2);
字符串查找,在str1中找str2
找到了则返回第一次出现的字符串,如果没找到则返回NULL

模拟实现strstr

const char* my_strstr(const char* str1, const char* str2)
{
	assert(str1 && str2);
	if (*str2 == '\0')
		return str1;
	const char* s1 = str1;
	const char* s2 = str2;
	const char* cp = str1;
	while (*cp)
	{
		s1 = cp;
		s2 = str2;
		while (*s1 != '\0' && *s2 != '\0' && * s1 == *s2)
		{
			s1++;
			s2++;
		}
		if (*s2 == '\0')
			return cp;
		cp++;
	}
	return NULL;
}

这里关于字符串查找,有想要继续了解的,可以看我的另一篇文章,带你搞懂字符串查找算法:KMP算法

KMP算法文章

strtok

char * strtok ( char * str, const char * sep );
按照sep提供的字符集合对str字符串进行分割
  • sep参数是个字符串,定义了用作分隔符的字符集合。
  • 第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记。
  • strtok函数找到str中的下一个标记,并将其用\0 结尾,返回一个指向这个标记的指针。比如:“abcdef@ghi”如果以@分割该字符串,那么strtok会找到@位置,将其改成'\0',然后返回a的位置。(注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。)
  • strtok 函数的第一个参数不为 NULL ,函数将找到 str 中第一个标记, strtok 函数将保存它在字符串中的位置。(依然是上面的那个例子,它不仅将@改成\0,而且记录了这个修改过的位置)
  • strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记。(如果在上边的例子的基础上变成了“abcdef@ghi#jkl”,按照上边的操作已经从@处分割好了,那么再次使用strtok切割#处时,第一个参数就需要传NULL,因为strtok在切割@时记录了这个位置)
  • 如果字符串中不存在更多的标记,则返回 NULL 指针。

用例子看一下该函数的用法:

 当然上边的写法是为了更好的观察,实际当中这样写太繁琐,我们使用for循环:

内存函数

memcpy

void * memcpy ( void * destination, const void * source, size_t num );
memcpy相比于strcpy适用于任意类型,所以它的参数也是void*,num表示拷贝多少字节
  • 函数memcpysource的位置开始向后复制num个字节的数据到destination的内存位置。
  • 这个函数在遇到 '\0' 的时候并不会停下来。
  • 如果sourcedestination有任何的重叠,复制的结果都是未定义的。(什么意思:假设我要将“1,2,3,4,5,6,7,8,9”中的“1,2,3,4,5”拷贝到“3,4,5,6,7”位置,最终得到的结果是“1,2,1,2,1,2,1,8,9”,因为存在覆盖的问题)

模拟实现memcpy

void* my_memcpy(void* dest, const void* src, size_t count)
{
	void* ret = dest;
	assert(dest);
	assert(src);
	while (count--) {
		*(char*)dest = *(char*)src;
		dest = (char*)dest + 1;
		src = (char*)src + 1;
	}
	return(ret);
}

考虑到memcpy不能实现存在重叠情况的字符串的拷贝,所以我们提供另一个内存函数memmove

memmove

void * memmove ( void * destination, const void * source, size_t num );
memcpy 的差别就是 memmove 函数处理的源内存块和目标内存块是可以重叠的。
如果源空间和目标空间出现重叠,就得使用 memmove 函数处理。

模拟实现memmove

void* my_memmove(void* dest, const void* src, size_t count)
{
	void* ret = dest;
	if (dest <= src || (char*)dest >= ((char*)src + count)) 
	{
		while (count--) 
		{
			*(char*)dest = *(char*)src;
			dest = (char*)dest + 1;
			src = (char*)src + 1;
		}
	}
	else 
	{
		dest = (char*)dest + count - 1;
		src = (char*)src + count - 1;
		while (count--) 
		{
			*(char*)dest = *(char*)src;
			dest = (char*)dest - 1;
			src = (char*)src - 1;
		}
	}
	return(ret);
}

memcmp

int memcmp ( const void * ptr1, const void * ptr2, size_t num );
规则同strcmp

memset

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

内存设置函数

ptr指向要设置的空间

value代表要设置的值

num代表要设置的字节数

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值