C语言进阶——字符函数和字符串函数

ced485cbb11e458d81a746890b32cf3f.gif

作者:敲代码の流川枫

博客主页:流川枫的博客

专栏:C语言从入门到进阶

语录:Stay hungry stay foolish

工欲善其事必先利其器,给大家介绍一款超牛的斩获大厂offer利器——牛客网

点击免费注册和我一起刷题吧

重点介绍处理字符和字符串的库函数的使用和注意事项

目录

1.求字符串长度

1.1 strlen

1.2 strlen的模拟实现

2.长度不受限制的字符串函数

2.1 strcpy

2.2 strcpy的模拟实现

2.3 strcat

2.4 strcat 的模拟实现

2.5 strcmp

2.6 strcmp的模拟实现

3.长度受限制的字符串函数

3.1 strncpy

3.2 strncpy的模拟实现

3.3 strncat

3.4 strncat的模拟实现

3.5 strncmp

4.字符串查找

4.1 strstr

4.2 strstr的模拟实现

4.3 strtok

5.错误信息报告

5.1strerror

6.内存操作函数

6.1 memcpy

6.2 memcpy的模拟实现

6.3 memmove

6.4 memmove的模拟实现

6.5 memset

6.6 memcmp


1.求字符串长度

1.1 strlen

size_t strlen ( const char * str );

函数功能:strlen函数返回的是在字符串中'\0'前面出现的字符个数(不包含'\0')

C语言进阶——字符函数和字符串函数_第1张图片

 注意事项:

参数指向的字符串必须要以'\0'结束,如果不以‘\0’结束,会一直向后读取字符,直到出现零终止符为止,因此会返回随机值, 编译器也会提示需要给字符串添加零终止符

#define _CRT_SECURE_NO_WARNINGS
#include
#include
int main()
{
	char arr1[] = { 'b','i','t' };
	int len = strlen(arr1);
	printf("%d\n", len);
	return 0;
}

注意函数的返回值为size_t,是无符号整型

#define _CRT_SECURE_NO_WARNINGS
#include
#include
int main()
{
	if (strlen("abc") - strlen("abcdef") > 0)
		printf(">");
	if (strlen("abc") - strlen("abcdef") < 0)
		printf("<");
	if (strlen("abc") - strlen("abcdef") == 0)
		printf("==");
	return 0;
}

这段代码不仔细分析会认为输出的是”<“,结果是”>“,因为size_t返回的是无符号整型,-3的补码被当成无符号整型来处理的时候返回的是非常大的正数,因此会是”>“ ,要注意strlen的返回值是无符号整型,可以这样直接比较strlen("abc") > strlen("abcdef"),也可以将strlen函数返回值强制类型转换为有符号整型

1.2 strlen的模拟实现

#define _CRT_SECURE_NO_WARNINGS
#include
#include
//创建计数器方式
size_t my_strlen(const char* str)
{
	size_t count = 0;
	assert(str);
	while (*str !='\0')
	{
		count++;
		str++;
	}
	return count;
}
int main()
{
	char arr[] = "abcdef";
	size_t n = my_strlen(arr);
	printf("%u\n", n);
	return 0;
}

还有两种模拟实现方式:

//指针-指针的方式
int my_strlen(char*s)
{
	char*p=s;
	while(*p!='\0')
		p++;
	return p -s;
}
//不能创建临时变量计数器
int my_strlen(const char*str)
{ 
	if(*str=='\0')
		return 0;
	else return 1+my_strlen(str+1);
}

2.长度不受限制的字符串函数

2.1 strcpy

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

C语言进阶——字符函数和字符串函数_第2张图片

 注意事项:

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

会将源字符串中的'\0'拷贝到目标空间

目标空间必须足够大,以确保能存放源字符串

目标空间必须可变

2.2 strcpy的模拟实现

#define _CRT_SECURE_NO_WARNINGS
#include
#include

char* my_strcpy(char* str1,const char* str2)
{
	assert(str1);
	assert(str2);
	char*ret = str1;
	while (*str1++ = *str2++)
	{
		;
	}
	return ret;
}
int main()
{
	char arr1[] = "abcdef";
	char arr2[20] = { 0 };
	my_strcpy(arr2, arr1);
	printf("%s\n",arr2);
	return 0;

}

 char*ret = str1;在函数结束要返回str1的地址时,str1已经改变,因此要在未改变之前创建ret保存它的初始地址用于返回

2.3 strcat

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

C语言进阶——字符函数和字符串函数_第3张图片

 注意事项:

目标空间必须可修改,能够将数据追加上去

源字符串必须以'\0'结束,否则无法追加数据

目标空间必须有足够的大,能容纳下源字符串的内容,否则编译器会提示警告,也可以成功追加上去字符串,但是会覆盖掉原有地址的数据,是非常危险的行为

功能:

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

2.4 strcat 的模拟实现

#define _CRT_SECURE_NO_WARNINGS
#include
#include
char* my_strcat(char* dest, const char* src)
{
	char* ret = dest;
	assert(dest && src);
	while(*dest != '\0')
	{
		dest++;
	}
	while (*dest++ = *src++)
	{
		;
	}
	return ret;
}
int main()
{
	char arr1[20] = "hollo";
	my_strcat(arr1, "world");
	printf("%s\n", arr1);
	return 0;
}

该函数要避免自己给自己追加数据,因为开始追加时,第一个字符会将零终止符覆盖掉,当全部字符都追加完之后,没有零终止符了,出现死循环

2.5 strcmp

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

功能:

比较两个字符串

将 C 字符串 str1 与 C 字符串 str2 进行比较
此函数开始比较每个字符串的第一个字符。如果它们彼此相等,则继续比较下一对,直到字符不同或达到终止空字符
此函数执行字符的二进制比较

C语言进阶——字符函数和字符串函数_第4张图片

2.6 strcmp的模拟实现

#define _CRT_SECURE_NO_WARNINGS
#include
int my_strcmp(const char* str1, const char* str2)
{
	while (*str1 == *str2)
	{
		if (*str1 == '\0')
			return 0;
		str1++;
		str2++;
	}
	return  (*str1 - *str2);
}
int main()
{
	char arr1[] = "abc";
	char arr2[] = "abc";
	int ret = my_strcmp(arr1, arr2);
	if (ret == 0)
		printf("==\n");
	if (ret > 0)
		printf(">\n");
	if (ret < 0)
		printf("<\n");
	return 0;
}

 my_strcmp 函数的思路是,让两个字符串从第一对开始比较,如果相等则都自增实现下一对的比较,如果比较到最后,第一个字符串结束,即出现零终止符,那么返回0,两字符串相等;如果在出现零终止符之前有一对不相等,那么返回两字符串相减的值,可以比较出大小

3.长度受限制的字符串函数介绍

3.1 strncpy

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

功能:

拷贝num个字符从源字符串到目标空间

如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个

#define _CRT_SECURE_NO_WARNINGS
#include
#include
int main()
{
	char arr1[10] = "abcde";
	char arr2[] = "fghi";
	strncpy(arr1, arr2, 7);
	printf("%s\n", arr1);
	return 0;
}

3.2 strncpy的模拟实现

char * __cdecl strncpy (
        char * dest,
        const char * source,
        size_t count
        )
{
        char *start = dest;

        while (count && (*dest++ = *source++) != '\0')    /* copy string */
                count--;

        if (count)                              /* pad out with zeroes */
                while (--count)
                        *dest++ = '\0';

        return(start);
}

3.3 strncat

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

功能:
将源的第一个字符追加到目标,以及终止空字符
如果源中字符串的长度小于num,则仅重复终止空字符之前的内容

#define _CRT_SECURE_NO_WARNINGS
#include
#include
int main()
{
	char arr1[10] = "abcde";
	char arr2[] = "fghi";
	strncat(arr1, arr2, 3);
	printf("%s\n", arr1);
	return 0;
}

该函数追加完要求的字符后会自动追加一个零终止符保证追加完后的数据仍然是一个字符串 

3.4 strncat的模拟实现

char * __cdecl strncat (
        char * front,
        const char * back,
        size_t count
        )
{
        char *start = front;

        while (*front++)
                ;
        front--;

        while (count--)
                if ((*front++ = *back++) == 0)
                        return(start);

        *front = '\0';
        return(start);
}

3.5 strncmp

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

功能:比较两个字符串的字符

将 C 字符串 str1 的数目与 C 字符串 str2 的数目进行比较
此函数开始比较每个字符串的第一个字符。如果它们彼此相等,则继续比较下一对,直到字符不同,直到达到终止空字符,或者直到两个字符串中的数字字符匹配,以先发生者为准

C语言进阶——字符函数和字符串函数_第5张图片

#define _CRT_SECURE_NO_WARNINGS
#include
#include
int main()
{
	char arr1[] = "abcde";
	char arr2[] = "fghi";
	int ret = strncmp(arr1, arr2, 3);
	printf("%d\n", ret);
	return 0;
}

4.字符串查找

4.1 strstr

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

功能:

查找子串的字符串函数返回指向 str1 中第一次出现的 str2 的指针,如果 str2 不是 str1 的一部分,则返回空指针

C语言进阶——字符函数和字符串函数_第6张图片

#define _CRT_SECURE_NO_WARNINGS
#include
#include
int main()
{
	char arr1[] = "chencli";
	char arr2[] = "enc";
	char* ret = strstr(arr1, arr2, 3);
	if (ret == NULL)
		printf("字串不存在");
	else
	printf("找到字串为:%s\n", ret);
	return 0;
}

4.2 strstr的模拟实现
 

找字串有两种情况,如下图:

C语言进阶——字符函数和字符串函数_第7张图片

 情况2中,bbc和bc需要多次匹配

第一次比较:s1!=s2,p++,指向b,s1被赋值指向b,s2被复制为str2

第二次比较:s1==s2相等,s1++,s2++

第三次比较:s1!=s2,跳出while循环,p++,指向第二个b,s1被赋值指向第二个b;s2被赋值为str2

第四次比较:s1==s2,s1++,s2++

第五次比较:s1==s2,返回p,查找子串完成

#define _CRT_SECURE_NO_WARNINGS
#include
#include
char* my_strstr(const char* str1, const char* str2)
{
	assert(str1 && str2);
	const char* s1 = str1;
	const char* s2 = str2;
	const char* p = str1;

	while (*p)
	{
		s1 = p;
		s2 = str2;
		while (*s1 != '\0' && *s2 != '\0' && *s1 == *s2)
		{
			s1++;
			s2++;
		}
		if (*s2 == '\0')
			return p;
		p++;
	}
	return NULL;

}

4.3 strtok

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

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

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

strtok函数找到str中的下一个标记,并将其用\0结尾,返回一个指向这个标记的指针(注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改

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

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

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

#define _CRT_SECURE_NO_WARNINGS
#include
#include
int main()
{
	const char* sep = "@.";
	char email[] = "[email protected]";
	char cp[30] = { 0 };
	

    strcpy(cp, email);//strtok函数找到str中的下一个标记,并将其用\0结尾,返回一个指向这个标记的指针
	//(注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。)
	

    char* ret = strtok(cp, sep);//strtok函数的第一个参数不为NULL,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置
	printf("%s\n", ret);
	

    ret = strtok(NULL, sep);//strtok函数的第一个参数为NULL,函数将在同一个字符串中被保存的位置开始,查找下一个标记
	printf("%s\n", ret);


	ret = strtok(NULL, sep);
	printf("%s\n", ret);//如果字符串中不存在更多的标记,则返回NULL指针

	return 0;
}

C语言进阶——字符函数和字符串函数_第8张图片

5.错误信息报告

5.1strerror

char* strerror ( int errnum );

功能:

返回错误码,所对应的错误信息

C语言的库函数执行失败的时候都会设置错误码

#define _CRT_SECURE_NO_WARNINGS
#include
#include
int main()
{
	printf("%s\n", strerror(0));
	printf("%s\n", strerror(1));
	printf("%s\n", strerror(2));
	printf("%s\n", strerror(3));
	printf("%s\n", strerror(4));

	return 0;
}

C语言进阶——字符函数和字符串函数_第9张图片

#define _CRT_SECURE_NO_WARNINGS
#include
#include
int main()
{
	
	FILE* pf = fopen("test.text", "r");
	if (pf == NULL)
	{
		printf("%s\n", strerror(errno));
		return 1;
	}
	else
	{
		//
	}
	return 0;
}

errno是c语言设置的一个全局的错误码存放的变量

当没有test.txt被读时,errno就会更新

6.内存操作函数

6.1 memcpy

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

功能:

复制内存块

将 num 字节值从源指向的位置直接复制到目标指向的内存块
源指针和目标指针所指向的对象的基础类型与此函数无关

结果是数据的二进制副本
该函数不检查源中是否有任何终止空字符 - 它始终精确地复制数字字节

注意事项:

函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置

这个函数在遇到'\0'的时候并不会停下来

如果source和destination有任何的重叠,复制的结果都是未定义的,为避免溢出,目标参数和源参数所指向的数组的大小应至少为 num 个字节,并且不应重叠(对于重叠的内存块,memmove 是一种更安全的方法)

6.2 memcpy的模拟实现

#define _CRT_SECURE_NO_WARNINGS
#include
#include

void* my_memcpy(void* dest,const void* src, size_t num)
{
	assert(dest && src);
	void* ret = dest;

	while (num--)
	{
		*(char*)dest = *(char*)src;
		dest = (char*)dest + 1;
		src = (char*)src + 1;
	}
	return ret;
}
int main()
{
	char arr1[100] = "abcdef";
	char arr2[100] = { 0 };
	my_memcpy(arr2, arr1,5);
	printf("%s\n", arr2);
	return 0;
}

 函数参数的类型是void*型的,因此可以复制不同元素类型的数组

该函数不能用于内存空间上有重叠的数据

6.3 memmove

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

功能:

和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的

如果源空间和目标空间出现重叠,就得使用memmove函数处理

6.4 memmove的模拟实现

当需要处理的数据空间上有重叠时,往往有时候需要从前往后拷贝,有时候从后往前拷贝

分析如图:

destsrc

C语言进阶——字符函数和字符串函数_第10张图片

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;
			dest = (char*)dest + 1;
			src = (char*)src + 1;
		}
	}
	else
	{
		//后向前拷贝
		while(num--)
		{
			*((char*)dest + num) = *((char*)src + num);
		}
	}
}

6.5 memset

功能:

填充内存块

将 ptr 所指向的内存块的前一个字节数设置为指定(解释为无符号字符)

C语言进阶——字符函数和字符串函数_第11张图片

#define _CRT_SECURE_NO_WARNINGS
#include
#include
int main()
{
	char arr[] = "hello world";
	memset(arr + 6, 'x', 3);
	printf("%s\n", arr);
	return 0;
}

6.6 memcmp

功能:

比较两个内存块

将 ptr1 所指向的内存块的前 num 个字节与 ptr2 指向的第一个字节数进行比较,如果它们都匹配,则返回零,或者如果不匹配,则返回与零不同的值,表示哪个值更大
请注意,与 strcmp 不同,该函数在找到空字符后不会停止比较

C语言进阶——字符函数和字符串函数_第12张图片

该组内存操作函数和字符串操作函数不同在于字符串操作函数只能作用于字符串,该内存操作函数可以作用于任意类型的数据

你可能感兴趣的:(C语言从入门到进阶,c语言,经验分享,c++,学习)