字符函数和字符串函数

字符函数和字符串函数

字符函数分类

C语言有一些列函数用于对不同的字符进行分类,一个字符属于何种类型。以下函数都需要包含头文件 ctype.h

函数参数符合以下条件返回真
iscntrl任何控制字符
isspace空白字符:空格 ’ ‘,换页 ’\f‘,换行 ‘\n’,回车 ‘\r’,制表符 ’\t‘,垂直制表符 ‘\v’。
isdigit十进制数字字符 ‘0’ ~ ‘9’
isxdigit十六进制数字,包括十进制数字字符,小写字母a-f,大小字母A-F
islower小写字母 a~z
isupper大写字母 A~Z
isalpha字母或数字,a-z、A-Z、0-9
ispunct标点符号,任何不属于数字或字符的图形字符(可打印)
isgraph任何图形字符
isprint任何可打印字符,包括图像字符、空白字符

我比较推荐使使,isdigit、islower和isupper这三个函数,主要的好记忆,根据英文翻译就可以直到它是何种功能。

digit:数字0-9、lower:下面的、upper:上面的。

#include <stdio.h>
#include <ctype.h>
int main()
{
	printf("%d\n", isdigit(6));
	printf("%d\n", islower('A'));
	printf("%d\n", isupper('a'));
	return 0;//错误使用,返回0,执行代码将放回0 0 0
}
int main()
{
	printf("%d\n", isdigit('6'));
	printf("%d\n", islower('a'));
	printf("%d\n", isupper('A'));
	return 0;
}

在这里插入图片描述
在这里插入图片描述

当然,以上的写法非常粗糙,没有任何技术~

例如:写一串代码,判断字符是否为大写,就可以使用isupper函数 if(isupper('str1') > 0),返回值大于0就说明str1是大写字母。

字符转换函数

头文件:ctype.h

函数原型:

int tolower(int c);//将大写字母转换为小写字母
int toupper(int c);//将小写字母转换为大写字母

用于对字符大小写的的转换

tolower:

  • 转换成功,返回转换后的小写字符。
  • 若c不是大写字母,通常返回原始字符。

toupper:

  • 转换成功,返回转换后的大写字符
  • 若c不是小写字符,通常返回原始字符

strlen的使用和模拟实现

使用strlen函数

函数原型:

size_t strlen(const char* str);

用于统计字符串个数,结束标志是 ’\0‘,在字符串里遇见这个字符’\0‘,就会结束函数调用,将统计的值返回。若传递的字符串里没有以 ’\0‘结尾,那使用strlen函数就会越界访问非字符串的内容,直到碰见 ’\0‘才会停止。

例如,计算一个字符数组的大写:

int main()
{
	char arr[] = { 'a', 'b', 'c'};
	printf("%zd\n", strlen(arr));
}

在这里插入图片描述

运行结果与预期差距太大了,离谱~。由于,字符数组里没有存放 ’\0‘在统计完数组里的字符后,还在数组之外的空间进行统计,直到碰见 ’\0’才结束的。

这里通过调试功能也发现,在内存1里,从61(字符a的十六进制ASCII码值)开始数数,一值数到 fe 7f 00 00这一行,00之前的确有42的字符。

在这里插入图片描述

注意:

  • strlen函数的返回值是size_t类型的,无符号整形类型。

判断以下代码执行后,打印 > 还是 <=。

int main()
{

	char str1[] = "abcdef";
	char str2[] = "abc";

	if ((strlen(str2) - strlen(str1)) > 0)
	{
		printf(">\n");
	}
	else
	{
		printf("<=\n");
	}
	return 0;
}

结果是 > 符号,原因是无符号整形 与 无符号整形计算的结果也是无符号整形

模拟实现strlen函数

strlen的3种写法:

计数器:

size_t my_strlen(const char* str)
{
	assert(str);
	size_t count = 0;
	while (*str)//等价于 *str != '\0'
	{
		str++;
		count++;
	}
	return count;
}

这里我们只需要统计,字符串str在’\0’之前的字符个数,我们可以创建一个count变量用来统计’\0’之前的字符个数,使用while循环,遍历字符串,直到遇见’\0’结束循环,将统计完的count返回即可。

由于传递的str字符串不能为空,也不期望在函数内对齐进行修改,这里使用了assert断言,及const修饰来限制了str字符串,使函数有更好的健壮性。

’\0‘的ASCII码值为0,所以*str,等价于 *str != ‘\0’,

指针-指针:

size_t my_strlen(const char* str)
{
	assert(str);
	char* cur = str;
	while (*cur)
	{
		cur++;
	}
	return cur - str;
}

指针-指针的写法思路与count计数类似,都需将将字符串遍历到’\0’之前,其主要区别,使用最后一个字符的指针减去第一个字符的指针,就等于它们之间的字符个数,并将其放回。

递归(不创建临时变量):

size_t my_strlen(const char* str)
{
    if(str != NULL)
    {
        return 1 + my_strlen(str + 1);
	}
    else
        return 0;
}

strcpy的使用和模拟实现

使用strcpy函数

函数原型:

char* strcpy(char* destination, const char* source);
                     目标空间                   源头

将源头字符串内存拷贝到目标空间中。

返回值:目标空间的起始地址,为了函数间的链式访问

  • 会将源字符串的 ’\0‘拷贝到目标空间

  • 源头字符串结尾必须包含 ’\0‘

  • 目标空间足够大,能够存放源头字符串

  • 目标空间能够被修改

    通过调试发现,的确发生了将源头字符串的 ’\0‘,一起拷贝在目标空间中。
    在这里插入图片描述

int main()
{
	char str1[] = "abcdef";
	char str2[20] = {0};
	strcpy(str2, str1);
	printf("%s\n", str2);
	return 0;
}

在这里插入图片描述

模拟实现strcpy
char* my_strcpy(char* dest, const char* src)
{
    assert(dest && src);
    char* ret = dest;
    while(src != '\0')
    {
        *dest = *src;
        dest++;
        src++;
	}
    *dest = *src;
    return ret;
}

strcpy的底层逻辑也很好理解,就是将src字符串里的内存拷贝到dest,所以通过循环遍历每一个字符将其复制到dest里,需要注意的是,while循环的结束条件是src != '\0',所以在出循环后还需要讲src的末尾赋给dest。

需要返回的是目标空间的起始地址,在函数起始将dest的地址保存 char* ret = dest;

字符串src是不期望发生改变,传递的dest字符串和src字符串不能为空,所以加入了 const和assert进行限制判断。

代码优化:

char* my_strcpy(char* dest, const char* src)
{
    assert(dst && src);
    char* ret = dest;
    while(*dest++ = *src++);
    return ret;
}

while循环里的这一步非常巧妙,是后置++,所以先对字符串dest和src解引用,然后在将src的值赋给dest,此时才得到的表达式结果。当循环进行到最后一步,src的值为’\0‘此时并不会退出循环,因为表达式的结果并没有计算出,将’\0‘赋给dest后,条件为假,跳出循环。

strcat的使用和模拟实现

使用strcat函数

函数原型:

char* strcat(cahr* destination, const char* source);
                     目标空间                   源头

将源字符串的内容接到目标空间,将两个字符串串在一起。

返回目标空间起始地址

  • 源字符串必须以 '\0'结束
  • 目标字符串也要有 '\0',否则无法追加
  • 目标空间足够大,放得下源字符串内容
  • 目标空间必须可以被修改
int main()
{
	char str1[] = "abcdef";
	char str2[20] = "xxxxx";
	strcat(str2, str1);
	printf("%s\n", str2);
	return 0;
}

通过调试代码发现,在进行追加时,是从目标空间字符串的结尾'\0'开始追加,并将其覆盖。

在这里插入图片描述

模拟实现strcat

char* my_strcat(char* dest, const char* src)
{
    char* ret = dest;
    assert(dest && src);
    while(dest != '\0')
		dest++;
    while(*dest++ = *src++);
    return ret;
}

strcat函数的逻辑与strcpy类似,都需要拷贝,但拷贝的起始位置不同,strcpy是从目标字符串起始位置拷贝,strcat是从目标字符串的结束位置开始拷贝,所以需要使用两个循环,第一个循环使目标字符串的指针指向 '\0',然后将src的内容拷贝进dest里。最后返回目标空间起始位置。

字符串src是不期望发生改变,传递的dest字符串和src字符串不能为空,所以加入了 const和assert进行限制判断。

自己给自己追加?我们使用自己模拟的strcat函数,会陷入死循环,

'\0'覆盖进入死循环。

在这里插入图片描述

这里,dest和src将指向同一个字符串,从 '\0'开始追加后,将 '\0'覆盖,这样目标字符串和源字符串都没有以'\0'结尾,那就不会跳出循环,一直追加下去。

而头文件里的strcat在vs编译器里,自己给自己追加不会出现这种情况,当然其余编译器也不一定支持这种做法。

strcmp的使用和模拟实现

使用strcmp函数

函数原型:

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

是用来比较字符串大小的

  • 比较对应位置字符的ASCII码值,比较出结果直接返回,不会全部比较完字符串。

  • str1 > str2 返回 大于0

  • str1 < str2 返回小于0

  • 相等 放回 0;

int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "axcdef";
	printf("%d\n", strcmp(arr2, arr1));
	printf("%d\n", strcmp(arr1, arr2));
	return 0;
}

在这里插入图片描述

  • 字符串arr2的x大于arr1里的b,第一个strcmp返回大于0的值

  • 字符串arr1的b小于arr1里的x,第二个strcmp返回小于0的值

模拟实现strcmp

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

strcmp函数的逻辑就是,让两个字符串里的字符一一对应,比较它们的ASCII码值。

使用循环遍历比较两个字符串相对应对应得大写,在循环内若其中一个字符串等于'\0',就说明此时两个字符串都遍历到了字符串末尾而没有跳出循环,说明两个字符串大小相等放回 0。

而在跳出循环则两个字符串大小不相等,通过if语句返回大于0,或小于0的值。而大于0和小于0的返回值可以进行优化合并,*str1 - *str2);返回两个字符相减的结果,若str1大于str2相减后返回大于0的值,反之返回小于0的值。

strncpy、strncat、strncmp的使用

这是三个字符串函数是在前面三个字符串的基础上进行优化,它们都需要传递一个无符号整形的值(size_t num),用来说明最大拷贝字符个数、最大追加字符个数、最大比较字符个数。通过这种限制,使得它们在使用上更加灵活。下列一一介绍它们在细节得差别。

strncpy

char* strcpy(char* destination, const char* source, size_t num);
  • num指的是最大字符拷贝个数
  • 当num < 字符串sourc的字符个数不会拷贝'\0'
  • num > 字符串sourc的字符个数,会接着拷贝 '\0'
int main()
{
	char arr1[] = "abcdef";
	char arr2[20] = "xxxxxxxx";
	printf("%s\n", strncpy(arr2, arr1, 6));
	return 0;
}

当字符串sourc的字符个数大于或等于(>=)num时,不会拷贝 '\0'

在这里插入图片描述
在这里插入图片描述

而字符串sourc的字符个数小于(<)num时,会拷贝 '\0'

strncat

char* strcat(char* destination, const char* source, size_t num);
//指定最多追加的个数,会在末尾放\0

char arr1[] = "abcde\0yyyyy";
char arr2[] = "xxxxxxxxxxxxxx";
strcar(arr1, arr2, 3);
  • 指定最大追加个数。
  • 在追加完后字符串末尾放置一个 '\0'
  • 而给的得num大于 source字符串的字符个数,会多余的向后放置 '\0'
  • 其余限制条件与strcat一样

可以自己于自己追加

int main()
{
	char arr1[20] = "ab\0yy";
	strncat(arr1, arr1, 3);
	printf("%s\n", arr1);
	return 0;
}

在这里插入图片描述

strncmp

int strcmp(const char* str1, const char* str2,size_t num);
  • 最大比较字符个数
int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "abcqef";
	printf("%d\n", strncmp(arr1, arr2, 3));
	return 0;
}

在这里插入图片描述

评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值