字符函数和字符串函数

本文详细介绍了C语言中的字符和字符串处理函数,包括strlen、strcpy、strcat、strcmp、strncpy、strncat、strncmp等,讨论了它们的工作原理、使用注意事项和应用场景。文章还提到了内存操作函数memcpy、memmove、memset和memcmp,以及字符分类和转换函数如isspace、tolower、toupper。此外,还探讨了错误信息报告函数strerror的用法。

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

目录

函数介绍:

1. 求字符串长度

1.1 strlen

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

2.1 strcpy

2.2 strcat

2.3 strcmp

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

3.1 strncpy

3.2 strncat

3.3 strncmp

4. 字符串查找

4.1 strstr

4.2 strtok

5. 错误信息报告

5.1 streror

6. 字符操作

6.1 字符分类函数

6.2 字符转换函数——tolower、toupper

7. 内存操作函数

7.1 memcpy

7.2 memmove

7.3 memset

7.4 memcmp



函数介绍:

C语言中对字符和字符串有很多处理,但是C语言本身没有字符串类型,字符串通常放在常量字符串中或者字符数组中。
字符串常量适用于那些对它不做修改的字符串函数。

1. 求字符串长度

1.1 strlen

——求字符串长度

size_t strlen ( const char * str );

#include <stdio.h>
int main()
{
	int len = strlen("abcdef");
	printf("%d\n", len);
	return 0;
}//6

之前写strlen()函数的模拟实现中有3种方法:

1、计数器的方法;

2、递归的方法;

3、指针-指针的方法

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

//因为求的是字符串长度,不会改变str所指向字符串的内容,所以加const修饰,
//即使str所指向的内容想要被修改也没有机会了。
int my_strlen(const char* str)
{
	//断言:保证指针的有效性:
	assert(str);
	//用计数器的方法实现:
	int count = 0;
	while (*str != '\0')//或while(*str)都可以
	{
		count++;
		str++;
	}
	return count;
}
int main()
{
	int len = my_strlen("abcdef");
	//表面传的是字符串,实际传的是字符串首字符地址。
	printf("%d\n", len);
	return 0;
}//6

sizeof——是操作符,计算大小。sizeof计算的结果返回的类型是size_t;size_t 是专门为sizeof的返回值设计的。

size_t——其实是unsigned int类型,只表示正数。

#include <stdio.h>
#include <string.h>
int main()
{
	if (strlen("abc") - strlen("abcdef") > 0)
	{
		printf(">");
	}
	else
	{
		printf("<=");
	}
	return 0;
}//>
#include <stdio.h>
#include <string.h>
int main()
{
	const char* str1 = "abcdef";
	const char* str2 = "bbb";
	if (strlen(str2) - strlen(str1) > 0)
	{
		printf("str2>str1\n");
	}
	else
	{
		printf("str1<str2");
	}
	return 0;
}//str2>str1

strlen()函数返回的是无符号数,两个无符号的数相减得到的也是无符号的数,上述代码中会把-3当做无符号的数处理,是一个非常大的正数而会引入bug。若strlen()返回值设计成int,这类问题就不会存在。

strlen()的返回值可以写成int类型,size_t类型或unsigned_int类型都可以。

上述代码强制类型转换为int可以得出“<=”:

#include <stdio.h>
#include <string.h>
int main()
{
	if ((int)strlen("abc") - (int)strlen("abcdef") > 0)
	{
		printf(">");
	}
	else
	{
		printf("<=");
	}
	return 0;
}//<=

或:

#include <stdio.h>
#include <string.h>
int main()
{
	if (strlen("abc") > strlen("abcdef"))
	{
		printf(">");
	}
	else
	{
		printf("<=");
	}
	return 0;
}//<=

注意:

使用库函数时需要包含头文件,不包含会有bug。

使用strlen()函数时:

字符串以 '\0' 作为结束标志,strlen函数返回的是在字符串中 '\0' 前面出现的字符个数(不包
含 '\0' )。

参数指向的字符串必须要以 '\0' 结束。
函数的返回值为size_t,是无符号的。

模拟实现strlen:三种方式:

方式1:计数器方式:

#include <stdio.h>
int my_strlen(const char* str)
{
	int count = 0;
	while (*str)
	{
		count++;
		str++;
	}
	return count;
}
int main()
{
	char arr[] = "abcdefg";
	int len = my_strlen(arr);
	printf("%d\n", len);
	return 0;
}//7

方式2:不创建临时变量计数器,递归实现

#include <stdio.h>
int my_strlen(const char* str)
{
	if (*str == '\0')
	{
		return 0;
	}
	else
	{
		return 1 + my_strlen(str + 1);
	}
}
int main()
{
	char arr[] = "abcdefg";
	int len = my_strlen(arr);
	printf("%d\n", len);
	return 0;
}//7

方式3:指针-指针

#include <stdio.h>
int my_strlen(char* s)
{
	char* p = s;
	while (*p != '\0')
	{
		p++;
	}
	return p - s;
}
int main()
{
	char arr[] = "abcdefg";
	int len = my_strlen(arr);
	printf("%d\n", len);
	return 0;
}//7

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

2.1 strcpy

——strcpy()函数用来拷贝字符串,会把源字符串内容拷贝到目标空间中,\0也会被拷贝过去。

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

/把arr1的内容拷贝放到arr2中
#include <stdio.h>
#include <string.h>
int main()
{
	char arr1[] = "abcdef";
	char arr2[20] = { 0 };
	strcpy(arr2, arr1);
	printf("%s\n", arr2);
	return 0;
}//abcdef

注意:在使用strcpy()函数时:

源字符串必须以 '\0' 结束。
会将源字符串中的 '\0' 拷贝到目标空间。
目标空间必须足够大,以确保能存放源字符串,防止造成越界访问而使程序崩溃。
目标空间必须可变。

#include <stdio.h>
#include <string.h>
int main()
{
	char arr1[] = { 'a','b','c','d','e','f' };
	char arr2[20] = "XXXXXXXX";
	strcpy(arr2, arr1);
	printf("%s\n", arr2);
	return 0;
}//程序会崩溃,拷贝内容混乱
#include <stdio.h>
#include <string.h>
int main()
{
	char arr1[] = { 'a','b','c','d','e','f','\0' };
	char arr2[20] = "XXXXXXXX";
	strcpy(arr2, arr1);
	printf("%s\n", arr2);
	return 0;
}//abcdef
#include <stdio.h>
#include <string.h>
int main()
{
	char arr1[] = { 'a','b','c','d','e','f','\0' };
	const char* p = "xxxxxxxxxx";
	strcpy(p, arr1);
	printf("%s\n", p);
	return 0;
}//程序崩溃,位置发生访问冲突

strcpy()函数可以将源字符串内容拷贝到已有数据的数组中。

注意:遇到\0的时候,打印也结束了。所以即使目标空间在\0(这里的\0是从源数据中拷贝的)后面还有数据也不能打印了。

strcpy()函数的模拟实现:

#include <stdio.h>
#include <assert.h>
//把源头数据拷贝到目标空间中,整个过程中,源数据是不需要发生变化的,目标空间需要变化
//所以在源数据前面可以用const修饰限定,保护源头数据。
char* my_strcpy(char* dest, const char* src)
{
	char* ret = dest;
	assert(dest && src);
	while (*src)
	{
		*dest = *src;
		dest++;
		src++;
	}
	*dest = *src;//拷贝\0
	return ret;
}
int main()
{
	char arr1[] = { 'a','b','c','d','e','f','\0' };
	char arr2[] = "xxxxxxxx";
	my_strcpy(arr2, arr1);
	printf("%s\n", arr2);
	return 0;
}//abcdef

优化代码:

#include <stdio.h>
#include <assert.h>
char* my_strcpy(char* dest, const char* src)
{
	assert(dest && src);
	char* ret = dest;
	while (*dest++ = *src++)
	{
		;
	}
	return ret;
}
int main()
{
	char arr1[] = { 'a','b','c','d','e','f','\0' };
	char arr2[20] = "xxxxxxxx";
	printf("%s\n", my_strcpy(arr2, arr1));
	return 0;
}//abcdef

使用指针的时候,注意:

1、指针不知道赋什么值,就赋给NULL;

2、指针使用完之后,赋值NULL。

3、NULL不能使用。

所以使用指针时,不是空指针就是有效的指针,只有这两种状态才能避免野指针等问题的出现。

assert在断言时只能判断是不是空指针,并不能判断是不是野指针。

链式访问:把一个函数的返回值作为另一个函数的参数。

函数返回值写void,就不能链式访问了,不能把该函数的返回值作为其他函数的参数。

尽量不要返回局部变量的地址可以返回局部变量

#include <stdio.h>
int* test()
{
	int a = 10;//0x0012ff40
	return &a;
}
int main()
{
	//p里面放的是:0x0012ff40
	int* p = test();
	//此时p指向的空间已经还给操作系统,而这个指针p还记住这个空间,
	//等去再访问时就会非法访问内存
	//是空间销毁不是地址销毁,指针所指向的空间不属于这个程序了就不能使用了
	return 0;
}

2.2 strcat

——把源字符串内容追加到目标字符串的后面。

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

strcpy()函数是拷贝,覆盖原来的数据。
strcat()函数是把源字符串内容追加到目标字符串的后面。

#include <stdio.h>
int main()
{
	char arr1[30] = "hello";
	char arr2[] = "world";
	//把arr2中的数据追加到arr1中
	strcat(arr1, arr2);
	printf("%s\n", arr1);
	return 0;
}//helloworld

原理:arr2找到arr1的‘\0’,把‘\0’覆盖掉然后拷贝arr2的内容。这里arr2是源字符串,‘\0’不会拷贝。

#include <stdio.h>
#include <string.h>
int main()
{
	char arr1[30] = "hello";
	char arr2[] = { 'w','o','r','l','d' };
	strcat(arr1, arr2);
	printf("%s\n", arr1);
	return 0;
}//源字符串并没有以'\0'结束,程序崩溃
#include <stdio.h>
#include <string.h>
int main()
{
	char arr1[30] = {'h','e','l','l','o'};
	char arr2[] = "world";
	strcat(arr1, arr2);
	printf("%s\n", arr1);
	return 0;
}//helloworld
#include <stdio.h>
#include <string.h>
int main()
{
	char arr1[] = "hello";
	char arr2[] = "world";
	strcat(arr1, arr2);
	printf("%s\n", arr1);
	return 0;
}//缓冲区溢出
#include <stdio.h>
#include <string.h>
int main()
{
	const char arr1[30] = "hello";
	char arr2[] = "world";
	strcat(arr1, arr2);
	printf("%s\n", arr1);
	return 0;
}//会报警告

注意:

源字符串必须以 '\0' 结束。
目标空间必须有足够的大,能容纳下源字符串的内容。
目标空间必须可修改。

模拟实现strcpy()函数:

#include <stdio.h>
#include <assert.h>
char* my_strcat(char* dest, const char* src)
{
	//把源头数据追加到目标空间中:
	//因为这两个指针指向的内容都会进行访问,
	//所以需要保证指针都为非空指针
	assert(dest && src);
	//1、找到目标空间中的\0——遍历字符串
	char* ret = dest;
	while (*dest)
	{
		dest++;
	}
	//2、追加内容到目标空间
		//拷贝
	while (*dest++ = *src++)
	{
		;
	}
	return ret;//返回的是目标空间的起始地址
}
int main()
{
	char arr1[30] = "hello";//目标
	char arr2[] = "world";//源
	printf("%s\n", my_strcat(arr1, arr2));//arr2是源字符串
	return 0;
}//helloworld

注意:

#include <stdio.h>
#include <assert.h>
char* my_strcat(char* dest, const char* src)
{
	assert(dest && src);
	char* ret = dest;
	while (*dest++)
	{
		/*dest++*/;
	}
	while (*dest++ = *src++)
	{
		;
	}
	return ret;
}
int main()
{
	char arr1[30] = "hello";
	char arr2[] = "world";
	printf("%s\n", my_strcat(arr1, arr2));
	return 0;
}//错误,hello

此时word在'\0'的后面追加过去的,追加进去了但是因为放在了\0的后面,所以在打印的时候看到'\0'就不再打印了。

#include <stdio.h>
#include <assert.h>
char* my_strcat(char* dest, const char* src)
{
	assert(dest && src);
	char* ret = dest;
	while (*++dest)
	{
		/*dest++*/;
	}
	while (*dest++ = *src++)
	{
		;
	}
	return ret;
}
int main()
{
	char arr1[30] = "hello";
	char arr2[] = "world";
	printf("%s\n", my_strcat(arr1, arr2));
	return 0;
}//helloworld

虽然这里此时打印没问题,但是当arr1是空字符串时就会出现Bug,*++dest是直接就把空字符串里面的‘\0’跳过了,又追加到'\0'的后面了。

代码不能只在一个例子中试,需要在各个场景中实现。

库函数中提供的参考代码:

char * __cdecl strcat (
        char * dst,
        const char * src
        )
{
        char * cp = dst;

        while( *cp )
                cp++;                   /* find end of dst */

        while((*cp++ = *src++) != '\0') ;       /* Copy src to end of dst */

        return( dst );                  /* return dst */

}


char * __cdecl strcpy(char * dst, const char * src)
{
        char * cp = dst;

        while((*cp++ = *src++) != '\0')
                ;               /* Copy src over dst */

        return( dst );
}
评论 46
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值