C语言--常见的字符串函数和内存函数

本文介绍了C语言中常用的字符串函数和内存函数,包括strlen、strcpy、strcat、strcmp、strncpy、strncat、strncmp、strstr、strtok、memcpy、memmove以及memset的用法和模拟实现。详细讲解了每个函数的功能、参数要求及其潜在的注意事项。

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

1、strlen

size_t strlen ( const char * str );

  1. strlen函数求得是字符串中’\0’前面所有的字符个数(注意不含’\0’)
  2. 注意函数的返回值是无符号的,所以两个strlen函数相加减都是大于等于0的

模拟实现

//模拟实现strlen
int my_strlen_1(const char* arr)//1、计数法
{
	//遇到'\0'之前所有元素个数和
	int n = 0;
	while (*arr != '\0')
	{
		n++;
		arr++;
	}
	return n;
}

int my_strlen_2(const char* arr)//2、指针-指针法
{
	//遇到'\0'之前所有元素个数和
	char* str = arr;
	while (*str != '\0')
	{
		str++;
	}
	return str-arr;
}

int my_strlen_3(const char* arr)//3、递归法
{
	//遇到'\0'之前所有元素个数和
	if (*arr != '\0')
	{
		return my_strlen_3(arr + 1) + 1;
	}
	return 0;
}

int main()
{
	char arr[] = "abcdefghj";
	//int sz = my_strlen_1(arr);
	//int sz = my_strlen_2(arr);
	int sz = my_strlen_3(arr);
	printf("%d\n", sz);
	return 0;
}

2、strcpy

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

  1. 源字符串必须要以’\0’结束
  2. strcpy函数会将源字符串中的’\0’拷贝到目标空间
  3. 目标空间必须要确保足够大
  4. 目标空间要确保可以改变

模拟实现

//模拟实现strcpy
void my_strcpy(char* arr, const char* str)
{
	//int n = strlen(str) + 1;
	//while (n > 0)
	//{
	//	*arr = *str;
	//	arr++;
	//	str++;
	//	n--;
	//}
	while (*arr++ = *str++)
		;
}
int main()
{
	char arr[20];
	my_strcpy(arr, "abcdef");
	printf("%s\n", arr);
	return 0;
}

3、strcat

char * strcat ( char * destination, const char * source );

  1. 源字符串必须以’\0’结束
  2. 目标空间必须要确保足够大
  3. 目标空间要确保可以改变

模拟实现

//模拟实现strcat
void my_strcat(char* arr, const char* str)
{
	arr += strlen(arr);
	while (*arr++ = *str++);
}

int main()
{
	char arr[20] = "abc";
	my_strcat(arr, " hard");
	printf("%s\n", arr);
	return 0;
}

4、strcmp

  1. int strcmp ( const char * str1, const char * str2 );
  2. 用来比较字符串大小,第一个大于第二个则返回>0的数,相等返回0,反之返回<0的数

模拟实现

//模拟实现strcmp
int my_strcmp(const char* arr, const char* str)
{
	//while (*arr || *str)
	//{
	//	//如果相等继续比较下一个字节
	//	if (*arr == *str)
	//	{
	//		arr++;
	//		str++;
	//	}
	//	//如果前面大于后面,则返回1
	//	else if (*arr > *str)
	//		return 1;
	//	//如果后面大于前面,则返回-1
	//	else
	//		return -1;
	//}
	全部相等,返回0
	//return 0;

	//改进代码

	assert(arr && str);
	//先判断是否相等,如果发现不相等的直接跳出循环
	while (*arr == *str)
	{
		if (*arr == '\0')
			return 0;
		arr++;
		str++;
	}
	//如果不相等,返回他们的差值(大于0或小于0)
	return (*arr - *str);
}
int main()
{
	char arr[] = "zhangsan";
	char str[] = "zhangaan";
	int ret = my_strcmp(arr, str);
	if (ret == 0)
		printf("=\n");
	else if (ret < 0)
		printf("<\n");
	else
		printf(">\n");
	return 0;
}

5、有长度限制的字符串函数

strncpy

int main()
{
	//有长度限制的字符串函数,不同于strcpy多了一个长度的参数
	char arr[20] = "abcdef";
	char str[] = "hjbe";
	//把str的前三个字符拷贝到arr中的前三个字符
	strncpy(arr, str, 3);
	printf("%s\n", arr);
	return 0;
}

strncat

int main()
{
	//要注意追加的字符串后会跟随着追加上一个'\0'
	//如果追加的长度比源字符串的长度还要长,也只会单单追加完所有字符,不会补'\0'

	return 0;
}

strncmp

int main()
{
	char arr[] = "abcdef";
	char str[] = "abc";
	//后面长度参数表示需要比较多少个字符
	int ret = strncmp(arr, str, 4);
	if (ret == 0)
		printf("=\n");
	else if (ret > 0)
		printf(">\n");
	else
		printf("<\n");
	return 0;
}

6、strstr

  1. char * strstr ( const char *str1, const char * str2);
  2. 函数作用是查找一个字符串中是否存在某一子串
  3. 返回类型为char*,如果子串存在,则返回源字符串中子串的起始地址。如果子串不存在,则返回空指针(NULL)
int main()
{
	//查找子串的一个函数,在一个字符串里找另一个字符串
	char arr[] = "abcdefg@163.com";
	char str[] = "bcd";
	char* jud;
	//如果子串存在返回的的是源字符串中子串的起始地址,所以要用指针来接收
	//如果不存在,返回空指针
	jud = strstr(arr, str);
	if (jud != NULL)
		printf("子串存在\n");
	else
		printf("子串不存在\n");
	return 0;
}

模拟实现

char* my_strstr(const char* arr, const char* str)
{
//要考虑多种情况,最好情况是在源串中找到子串的开头后,接着的字符都能和子串一一对应
//也有特殊情况,找到了子串的开头后,接着的字符和子串的不一样或者找到了子串的某一段后接着的和子串不能对应
//所以要用一个指针来记录找到子串开头时的位置,如果碰到后面的不一样那还可以返回到开头下一个字符继续找
	assert(arr && str);
	const char* p;
	const char* a;
	const char* s;
	a = arr;
	s = str;
	p = arr;
	while (*p)
	{
		a = p;
		s = str;
		while (*a != '\0' && *s != '\0' && * a == *s)
		{
			a++;
			s++;
		}
		if (*s == '\0')
			return (char*)p;
		p++;
	}
	return NULL;
}
int main()
{
	char arr[] = "abcdefg@163.com";
	char str[] = "bcd";
	char* jud;
	jud = my_strstr(arr, str);
	if (jud != NULL)
		printf("子串存在\n");
	else
		printf("子串不存在\n");
	return 0;
}

7、strtok

  1. char * strtok ( char * str, const char * sep );
  2. 函数作用:利用分隔符来切割字符串可输出
  3. 第一个参数为原字符串的起始地址,第二个则为分隔符组成的字符串的起始地址
  4. strtok函数会改变原字符串的数据,所以最好是创建新的字符数组来操作
int main()
{
	//切割字符串
	//因为strtok会改变原字符串的数据,所以最好创建新的字符数组用来操作
	//第一次调用时,第一个参数要为原字符串,当下一次调用时因为函数将分隔符字符改为了'\0'
	//所以下一次调用就需要传入空指针参数
	const char* sep = "@.";
	char arr[] = "abcdefg@163.com";
	char cp[30] = { 0 };
	strcpy(cp, arr);
	char* ret;
	//ret = strtok(cp, sep);
	//printf("%s\n", ret);
	//ret = strtok(NULL, sep);
	//printf("%s\n", ret);
	//ret = strtok(NULL, sep);
	//printf("%s\n", ret);
	for (ret = strtok(cp, sep); ret != NULL; ret = strtok(NULL, sep))
	{
		printf("%s\n", ret);
	}
	return 0;
}

8、memcpy

  1. void * memcpy ( void * destination, const void * source, size_t num );
  2. 函数作用:内存拷贝,相比于strcpy,该函数可以拷贝任意的类型数组
int main()
{
	//内存拷贝,strcpy和strncpy只能拷贝字符串,而memcpy不关注类型
	//void* memcpy(void* destination, const void* source, size_t num(这里是拷贝的元素的字节和));
	//memcpy负责两个独立空间的数据
	//重叠覆盖的拷贝不能实现
	int arr1[] = { 1,2,3,4,5,6,7 };
	int arr2[10] = { 0 };
	memcpy(arr2, arr1, 28);
	return 0;
}

模拟实现

void* my_memcpy(void* arr, const void* str, unsigned int n)
{
	assert(arr && str);
	//因为还不能确定数据类型,所以强制类型转换成char一个字节一个字节的拷贝
	while (n--)
	{
		*(char*)arr = *(char*)str;
		arr = (char*)arr + 1;
		str = (char*)str + 1;
	}
}
int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[10] = { 0 };
	my_memcpy(arr2, arr1, 40);
	int i;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr2[i]);
	}
	return 0;
}

9、memmove

  1. void * memmove ( void * destination, const void * source, size_t num );
  2. 第一个参数为目标空间地址,第二个为需要拷贝的数据起始地址,第三个是拷贝的数据字节和
  3. 和memcpy的参数一样,不过功能要比memcpy强大,该函数可以负责一个独立空间内存的数据
  4. 比如一个数组,使用该函数可以使该数组内部进行拷贝
int main()
{
	//函数用来处理重叠的内存空间之间的拷贝
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	memmove(arr + 2, arr, 20);
	int i;
	for (i = 0; i < 10; i++)
		printf("%d ", arr[i]);
	return 0;
}

模拟实现

实现memmove有多种情况,假设现在有一个数组[1,2,3,4,5,6],我们需要吧1,2,3拷贝到2,3,4的位置,那如果我们直接采用从前往后依次交换的话就会输出[1,1,1,1,5,6]这种情况,所以要分情况讨论是从前往后依次交换还是从后往前依次交换。

//模拟实现memmove
void* my_memmove(void* arr, const void* str, unsigned int n)
{
	assert(arr && str);
	void* ret = arr;
	if (arr < str)
	{
		//当目标空间的地址比拷贝数据的起始地址要前时
		//从前向后拷贝
		while (n--)
		{
			*(char*)arr = *(char*)str;
			arr = (char*)arr + 1;
			str = (char*)str + 1;
		}
	}
	else
	{
		//当目标空间的地址比拷贝数据的起始地址要后时
		//从后向前拷贝
		while (n--)
		{
			*((char*)arr + n) = *((char*)str + n);
		}
	}
	return ret;
}

int main()
{
	//函数用来处理重叠的内存空间之间的拷贝
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	my_memmove(arr + 2, arr, 20);
	int i;
	for (i = 0; i < 10; i++)
		printf("%d ", arr[i]);
	return 0;
}

memset

  1. void * memset ( void * ptr, int value, size_t num );
  2. 函数作用是设置数组的元素,按照起始地址来
  3. 第一个参数为数组的起始地址,第二个为设置的元素,第三个为要设置值的字节数
int main()
{
	char str[] = "almost every programmer should know memset!";
	memset(str, '-', 6);
	puts(str);
	return 0;
}

END

以上就是对常用字符串函数与内存函数的总结与理解,有很多需要补充更深刻说明的地方,之后有改正会第一时间更新。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值