【C语言】关于处理字符和字符串的库函数,以及部分函数的实现

前言

字符和字符串的处理几乎是每个初学者都会使用的程序,今天我们来了解一下一些关于处理字符和字符串的库函数,知道库函数的程序原理,和模仿实现一下部分函数。

函数的介绍

1. strlen函数

size_t strlen ( const char * str );

strlen函数是测量字符串的长度,以\0为结束标志,计算\0之前共有多少个字符个数(这里字符个数不包括\0)并将其返回。
1.1 strlen函数作为一个处理字符串的函数,它的所以它的参数是一个字符指针。
1.2 strlen函数的目的是测量字符串的长度,这里加一个const修饰参数,防止字符串被修改。
1.3 strlen函数的返回值是size_t,是因为一个字符串的长度不可能为负数。
1.4 需要注意的是字符串必须有\0结尾,否则计算出来的值会是大于字符串长度的一个随机值。

2. strcpy函数

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

strcpy函数是将source中指向的字符串复制到destination指向的数组中。
2.1 strcpy函数需要两个参数,一个是指向复制内容目的数组(destination),另一个是我们需要的字符串(source)。
2.2 调用strcpy函数时我们需要改变的是destination指向的数组,所以只需在另一参数上加以const修饰。
2.3 调用strcpy函数时,字符串必须有\0结尾,并且数组必须足够大,能够放下字符串。
2.4 strcpy函数返回的是目的数组的首元素地址。

3. strcat函数

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

strcat函数是将source中指向的字符串复制并追加到destination指向的数组中。
3.1 strcat函数需要两个参数,一个是指向复制内容目的数组(destination),另一个是我们需要的字符串(source)。
3.2 调用strcat函数时我们需要改变的是destination指向的数组,所以只需在另一参数上加以const修饰。
3.3 调用strcat函数时,字符串必须有\0结尾,并且数组必须足够大,能够放下字符串。
3.4 strcat函数返回的是目的数组的首元素地址。
3.5 值得注意的是追加的时候是在\0的位置上开始追加而不是\0之后开始追加。

4. strcmp函数

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

strcmp函数用来比较两个字符串是否相等,并不是比较两个字符串代数和的大小,而是比较两个指针指向的字符的大小。
(1)字符串一大于字符串二时返回一个大于0的数
(2)字符串一等于字符串二时返回0
(3)字符串一小于字符串二时返回一个小于0的数
4.1 strcmp函数需要两个字符串的首地址作为函数参数。
4.2 strcmp函数只是用来比较两个字符串是否相等,并不希望改变字符串,所以为两个参数加上const修饰。
4.3 这个函数需要两个字符串均有\0结尾

5. strncpy函数

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

strncpy函数与strcpy函数功能相似,但不同的是strncpy函数能够控制复制的字符个数。
strncpy函数会将source指向的字符串拷贝num个字符到目标数组。

6.strncat函数

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

strncat函数与strcat函数功能相似,但不同的是strncat函数能够控制追加的字符个数。
strncat函数会将source指向的字符串追加num个字符到目标数组。

7. strncmp函数

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

strncmp函数与strcmp函数功能相似,但不同的是strncmp函数能够控制对比的字符个数。
strncmp函数开始比较每个字符串的第一个字符。如果它们彼此相等,则继续使用以下对,直到字符不同,直到达到终止的空字符,或者直到两个字符串中的 num 字符匹配。

8. strstr函数

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

strstr函数的作用是找子字符串
8.1 函数返回在str2指向的字符串第一次找到str1字符串的地址。若找不到,则返回NULL
8.2 函数将两个字符串匹配的时候到\0终止,但匹配时不包括\0

9. strtok函数

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

9.1 delimiters是一个字符串,它是分割字符的集合。
9.2 第一次调用strtok函数时参数需要一个字符串,而后续调用时需要一个空指针(NULL)
9.3 strtok函数会找到下一个分割符并作标记,并在这个标记置换成\0结尾。例如这里:

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

int main()
{
    
    
	char* p = ":/.";
	char arr[] = "http://t.csdn.cn/h4GfL";
	char* ret = NULL;
	ret = strtok(arr, p);
	printf("%s\n", ret);
	//调用一次函数字符串变为http\0//t.csdn.cn/h4GfL
	//并且strtok函数会记得h的地址,后面的以此类推
	ret = strtok(NULL, p);
	printf("%s\n", ret);
	//调用两次函数字符串变为http\0\0/t.csdn.cn/h4GfL
	ret = strtok(NULL, p);
	printf("%s\n", ret);
	//调用三次函数字符串变为http\0\0\0t.csdn.cn/h4GfL
	ret = strtok(NULL, p);
	printf("%s\n", ret);
	//调用四次函数字符串变为http\0\0\0t\0csdn.cn/h4GfL
	ret = strtok(NULL, p);
	printf("%s\n", ret);
	//调用五次函数字符串变为http\0\0\0t\0csdn\0cn/h4GfL
	ret = strtok(NULL, p);
	printf("%s\n", ret);
	//调用六次函数字符串变为http\0\0\0t\0csdn\0cn\0h4GfL
	return 0;
}

在这里插入图片描述

//代码可化简效果一样,可以方便后续改变字符串而不增加上面代码的重复。
#include <stdio.h>
#include <string.h>

int main()
{
    
    
	char* p = ":/.";
	char arr[] = "http://t.csdn.cn/h4GfL";
	char* ret = NULL;
	for (ret = strtok(arr, p); ret != NULL; ret = strtok(NULL, p))
	{
    
    
		printf("%s\n", ret);
	}
	return 0;
}

在这里插入图片描述

9.4 strtok函数具有记忆功能,它能够将分隔符变为\0,并且记住分隔符的地址。
9.5 strtok函数第一个参数不为NULL,函数找str中的第一个分隔符,并保存它在字符串中的位置。
9.6 strtok函数第一次参数为NULL,函数则在同样的字符串中被strtok函数保存的位置,找到下一个分隔符的位置。
9.7 若字符串中没有更多的分割符,则函数会返回NULL
9.8 值得注意的是strtok函数会真的改变字符串的内容,所以我们使用strtok函数的时候一般会临时拷贝一份字符串。

10. strerror函数

char * strerror ( int errnum );

strerror函数能够将errnum中的值转换为错误信息。
这里有个功能相似的函数:perror函数

void perror ( const char * str );

perror函数也能够将erron中的值转换为错误信息,但是相比之下多了一个功能就是能够直接打印错误信息。
所以我们按照是否打印来选择使用哪个函数。

11. memcpy函数

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

memcpy函数能够将源空间内的num个字节直接复制到目标空间内。
11.1 memcpy函数strncpy函数的功能类似,但mencpy函数不止局限与字符串。我们选择时应该选择专攻的,例如使用复制字符串的时候用strcpy函数,其他整形数组、浮点型数组使用strncpy函数。
11.2 该函数不检查源中的任何终止空字符(\0), 所以它总是准确地复制字节数。
11.3 若两个内存块重叠时,因为编译器的原因,结果并不能确定。

12. memmove函数

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

memmove函数的功能与memcpy函数的功能一致,但是两个内存块重叠时,memmove函数并不会受影响,也可以说memmove函数包含了所有memcpy函数的功能但少了重叠的问题。
所以在两个内存块重叠的时候我们尽量使用memmove函数,也可以只使用memmove函数。

13. memcmp函数

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

13.1 memcmp函数与strcmp函数相似但不限制比较类型。
13.2 memcmp函数会将ptr1后面的num个字节与ptr2的第一个字节进行比较,若相同则ptr2向后移动继续与ptr1的后面的字节比较。若全部相等则返回0,否则会按ptr1与ptr2的大小关系返回一个不为零的数。
13.3 注意该函数不检查源中的任何终止空字符(\0)

部分库函数的模拟实现

1. strlen函数的模拟实现

方法一:计数器方式
size_t my_strlen1(const char* str)
{
    
    
	int count = 0;
	while (*str)
	{
    
    
		str++;
		count++;
	}
	return count;
}
方法二:递归方式(不创建临时变量)
size_t my_strlen2(const char* str)
{
    
    
	if (*str != '\0')
		return 1 + my_strlen2(str + 1);
	else
		return 0;
}
方法三:指针-指针
size_t my_strlen3(char* str)
{
    
    
	char* ret = str;
	while (*str != '\0')
	{
    
    
		str++;;
	}
	return str - ret;
}

int main()
{
    
    
	char arr[] = "abcdef";
	//int ret = my_strlen1(arr);
	//int ret = my_strlen2(arr);
	int ret = my_strlen3(arr);
	printf("%d", ret);
	return 0;
}
//a b c d e f \0
//注意指针-指针计算的是\0之前的元素个数

2. strcpy函数的模拟实现

#include <stdio.h>
#include <assert.h>
//const修饰防止src被修改
char* my_strcpy(char* dest,const char* src)
{
    
    
	char* ret = dest;
	//断言,防止dest 与 src 为空指针 ,若为空指针则报错停止程序
	assert(dest && src);
	while (*dest++ = *src++)
	{
    
    
		;
	}
	return ret;
}

int main()
{
    
    
	char arr1[] = "abcdef";
	char arr2[10] = {
    
     0 };
	my_strcpy(arr2, arr1);
	printf("%s\n", arr2);
	return 0;
}

3. strncpy函数的模拟实现

char* my_strncpy(char* dest,const char* src , size_t num)
{
    
                                           //注意num的单位是字节
	char* ret = dest;
	assert(dest && src);
	while (num--)
	{
    
    
		*dest++ = *src++;
	}
  return ret;
}

4. strcat函数的模拟实现

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

char* my_strcat(char* dest, const char* src)
{
    
    
	char* ret = dest;
	assert(dest && src);
	while (*dest)
	{
    
    
		dest++;
	}
	//找到dest指向'\0'的位置
	while (*dest++ = *src++)
	{
    
    
		;
	}
	return ret;
}

int main()
{
    
    
	char arr1[10] = "abcdef";
	char arr2[10] = "ghi";
	my_strcat(arr1, arr2);
	printf("%s\n", arr1);
	//abcdefghi
	return 0;
}

5. strncat函数的模拟实现

char* my_strncat(char* dest, const char* src , size_t num)
{
    
                                             //注意num的单位是字节
	char* ret = dest;
	assert(dest && src);
	while (*dest)
	{
    
    
		dest++;
	}
	while (num--)
	{
    
    
		*dest++ = *src++;
	}
	return ret;
}

6. strstr函数的模拟实现

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

char* my_strstr(const char* str1, const char* str2)
{
    
    
	assert(str1 && str2);
	char* p1 = NULL;
	char* p2 = NULL;
	char* mark =(char*) str1;
	//这里str1的类型是const char* ,mark的类型是char* 将str1赋给mark,类型由不可变变为可变
	//变的不安全,编译器会报警告,所以将str1强制类型转换为char*,下面的p2同理
	while (1)
	{
    
    
		p1 = mark;
		p2 = (char*)str2;
		//这里*p1 和 *p2 为'\0'就没有继续循环的必要了
		//如果*p1为'\0',p1指向的字符串已经结束,要么*p2为'\0'找到了,要么*p2不为'\0'找不到
		//如果*p2为’\0'则p2指向的字符串已经遍历完了,p1指向的字符串存在p2指向的字符串
		while (*p1!= '\0' && *p2 != '\0' && *p1 == *p2)
		{
    
    
			p1++;
			p2++;
		}
		if (*p2 == '\0')
		{
    
    
			return mark;
		}
		mark++;
	}
	return NULL;
}

int main()
{
    
    
	char arr1[10] = "abcdef";
	char arr2[10] = "bcd";
	char* ret = my_strstr(arr1, arr2);
	printf("%s\n", ret);
	return 0;
}

7. strcmp函数的模拟实现

#include <stdio.h>
#include <assert.h>
//const修饰防止字符串被修改
int my_strcmp(const char* dest, const char* src)
{
    
    
	assert(dest && src);
	//*dest == *src && *dest == '\0' 则*dest == *src == '\0'
	//两字符串都遍历完了且相同
	while (*dest == *src)
	{
    
    
		if (*dest == '\0')
			return 0;
		dest++;
		src++;
	}
    return *dest - *src;
}

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

	int ret = my_strcmp(arr1, arr2);
	printf("%d\n", ret);
	return 0;
}

8.strncmp函数的模拟实现

#include <stdio.h>
#include <assert.h>
//const修饰防止字符串被修改
int my_strncmp(const char* dest, const char* src , size_t num)
{
    
    												 //注意num的单位是字节
	assert(dest && src);
	while (*dest == *src && num--)
	{
    
    
		dest++;
		src++;
	}
    return *dest - *src;
}

9. memcpy函数的模拟实现

//写这个代码的人并不知道使用者用这个函数处理什么样的类型
//所以用void*,因为void*可以接受任意类型的指针
void* my_memcpy(void* dest,const void* src, size_t num)
{
    
                                        //这里注意num的单位是字节
	assert(dest && src);
	void* ret = dest;
	while (num--)
	{
    
    
		*(char*)dest = *(char*)src;
		dest = (char*)dest + 1;
		src = (char*)src + 1;
	}
	return ret;
}

10. memmove函数的模拟实现

memmove函数与memcpy函数功能基本相同,但有了处理两块内存块重叠的能力,所以在模拟memmvoe函数的时候就要分情况,到底是从前往后覆盖,还是从后往前覆盖。

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

总结

到这里部分处理字符和字符串的库函数就已经讲述完毕了。
希望我的文章能够对你一定的帮助!!

猜你喜欢

转载自blog.csdn.net/qq_55401402/article/details/129605310