字符函数和字符串函数
字符函数分类
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;
}