高端的C语言,往往只有需要最朴素的学习方式!
忙碌了五个小时的邓师傅,终于完成了这篇文章,希望大家多多支持
希君生羽翼,一化北溟鱼
C语言中,有一系列专门为字符所设立的函数,称为字符函数,要想使用字符函数就需要包含头文件ctype.h
#include<ctype.h>
字符分类函数就是用来分辨一个字符到底是属于哪一类型的字符的函数
以下是我所了解到的部分字符函数,及其cplusplus官网中这些函数的链接:
函数 | 若其参数是符合下列条件则返回真 |
---|---|
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 |
isalnum | 字母或者数字,a~z,A~Z,0~9 |
ispunct | 标点符号,任何不属于数字或者字母的图形字符(可打印) |
isgraph | 任何图形字符 |
isprint | 任何可打印字符,包括图形字符和空白字符 |
这些函数的使用十分简单,我们举一个例子大家就了解了:
#include<stdio.h>
#include<ctype.h>
void main()
{
char ch = 'a'; //给ch赋值为字符'a'
if (islower(ch)) //用islower函数判断ch中存放的值是不是小写字符
{ //若是小写字母,则返回一个非0的整形
printf("YES"); //若不是小写字母,则返回0
}
}
在C语言中,提供了两种字符转换函数
tolower | 将大写字符转换成小写字符 |
---|---|
toupper | 将小写字符转换成大写字符 |
#include<stdio.h>
#include<string.h>
#include<ctype.h>
void main()
{
char ch[] = "ABCDEFGhij";
for (int i = 0; i < strlen(ch); i++)
{
ch[i] = tolower(ch[i]);
printf("%c ", ch[i]);
}
}
模拟实现的具体思路同样十分简单,我们知道,大写字母和小写字母之间的ASCII码值的大小差距为32;故我们只需要判断其是否为大写字母或者小写字母,之后+-32即可;
#include<stdio.h>
#include<string.h>
#include<ctype.h>
int my_tolower(int n)
{
if (isupper(n))
{
n += 32;
}
return n;
}
void main()
{
char ch[] = "ABCDEFGhij";
for (int i = 0; i < strlen(ch); i++)
{
ch[i] = my_tolower(ch[i]);
printf("%c ", ch[i]);
}
}
在C语言中,有一些专门为字符串设计的函数,称为字符串函数;
要想使用字符串函数,则需要包含头文件
#include<string.h>
获取字符串的长度,字符串的长度由终止字符决定,即'\0' ,字符串的长度为字符串开头和终止字符之间的字符数;
例如:
void main()
{
char ch[100] = "abcdefg";
printf("%d", strlen(ch));
}
//结果会是7还是100呢?
通过调试我们可知:
在该字符串的末尾,有'\0',故最终的答案为7;
让我看接下来这个例子:
void main()
{
char ch1[] = "abcdefg";
char ch2[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g' };
printf("%d\n", strlen(ch1));
printf("%d\n", strlen(ch2));
}
//这个结果又是什么呢?
我们再调试一次:
我们会发现,这两种给数组赋值的结果有一个小的区别,就是字符串的结尾是否有'\0' ,通过这两个例子我们知道,若是用ch2的赋值方式,则只会将花括号中的的值赋给数组,而若是用ch1的赋值方法,则不仅会将引号中的值赋给数组,最终还会在字符串的末尾自动添加一个'\0' ;
而其最终的结果又会是什么呢?
ch1则比较简单,就为7,但是ch2呢?ch2中并没有'\0' ,那其长度又会如何判断呢?
通过内存监视器我们可以观察到:
该图中,0x0000008BB0AFF744(x64环境)就是ch2的首元素的地址,内存中存放的是16进制的数字,61即十进制97,即字符'a',一行代表四个字节,即四个字符,我们会发现,从61(包括61)到下一个'\0'之间一共有33个字符,故最终的结果应该为33。
但这个结果并不是固定不变的,由于内存中存放的数是随机的,故当我们重新生成一个程序之后,其结果会变为另外一个随机值;
我们知道strlen函数遇到'\0'就会停止,故我们可以使用一个循环,当循环遇到'\0'则停止计数;
通过官网我们知道,strlen函数的返回值是size_t,即无符号的整形,参数为const char* str;
通过代码实现:
#include<stdio.h>
#include<string.h>
#include<assert.h>
size_t my_strlen(const char* str)
{
assert(str); //防止str为空指针
size_t count = 0;
while (*str++) //直到*str = '\0' 停止循环
{
count++; //记录一共循环了几次
}
return count; //返回的count即为该字符串的长度
}
void main()
{
char ch[] = "abcdefg";
printf("%d", my_strlen(ch)); //结果为7
}
我们知道指针 - 指针的结果为两个地址之间的元素的个数,故我们可以通过指针 - 指针的方式来判断该字符串中有多少个元素;
#include<stdio.h>
#include<string.h>
#include<assert.h>
size_t my_strlen(const char* str)
{
assert(str); //断言防止str为空指针
char* ret = str; //ret与str的指向相同,都为ch数组;
while(*ret) //使ret运行到'\0'处
{
ret++;
}
return ret - str; //返回两个指针相减,即其地址之间的元素个数
}
void main()
{
char ch[] = "abcdefg";
printf("%d", my_strlen(ch)); //结果为7
}
在某些特殊情况下,若我们不能或不想多创建一个变量来实现模拟strlen函数,我们可以使用函数递归的方式去得到字符串的长度 :
#include<stdio.h>
#include<string.h>
#include<assert.h>
size_t my_strlen(const char* str)
{
assert(str); //防止str为空指针
if (*str) //判断str是否为 '\0'
{
return 1 + my_strlen(str + 1); //若不为'\0',则长度+1,并判断下一字符是否为'\0'
}
else
{
return 0; //若为'\0'返回0,即长度不 +1;
}
}
void main()
{
char ch[] = "abcdefg";
printf("%d", my_strlen(ch));
}
strcpy函数是字符串拷贝函数,将strcpy函数的源字符串(第二个参数)内的字符串拷贝给目标字符串(第一个参数);
以下是其简单的使用方法:
1. 使用该函数,源字符串必须包含'\0' ,同时'\0'也会被拷贝到目标字符串中;
2. 使用该函数,必须保证目标空间要足够大,能够放得下需要拷贝的数据;
3. 要确保目标空间可以被修改;
我们可以使用循环的方法,使源字符串的每一个字符都被拷贝到目标空间中去
#include<stdio.h>
#include<string.h>
#include<assert.h>
char* my_strcpy(char* dest, const char* src)
{
char* ret = dest; //保存其首元素地址
while (*src != '\0') //判断是否拷贝结束
{
*dest = *src; //进行拷贝
src++;
dest++; //将源头字符串的每一个字符都进行拷贝
}
*dest = '\0'; //将目标数组最后置为'\0'
return ret; //返回目标数组首元素地址
}
void main()
{
char ch1[] = "abcdefg";
char ch2[20];
printf("%s", my_strcpy(ch2, ch1));
}
该代码目前可以简单实现strcpy函数的部分基本功能,但是还是有不少缺陷,例如:
若是ch1或者ch2数组无指向,及传入函数的两个参数为空指针怎么办?
我们的代码又可不可以更加简介高效呢?
#include<stdio.h>
#include<string.h>
#include<assert.h>
char* my_strcpy(char* dest, const char* src)
{
assert(dest && src); //用assert断言,判断两个参数是否为空指针
char* ret = dest; //保存目标数组的首元素地址
while (*dest++ = *src++); //判断其是否为'\0',且同时进行赋值操作和指针后移操作
return ret; //若*src为'\0'了,那么'\0'将会被赋值给*dest
} //则while中的表达式值为'\0',也就为假,停止循环,停止拷贝
void main()
{
char ch1[] = "abcdefg";
char ch2[20];
printf("%s", my_strcpy(ch2, ch1));
//结果为abcdefg
}
这种写法更加贴近strcpy函数,也更加简洁明了高效;
strcat函数是将源字符串(第二个参数)追加到目标字符串(第一个参数)之后
以下是其简单的使用方法:
1. 源字符串必须含有'\0' ,否则不知需要追加的字符串的长度;
2. 目标字符串同样需要含有'\0' ,否则不知到在目标字符串的何处开始追加源字符串;
3. 目标空间必须足够大,能够放得下本身+所需追加的字符串的大小;
4. 目标空间必须可修改;
模拟实现:
#include<stdio.h>
#include<string.h>
#include<assert.h>
char* my_strcat(char* dest, const char* src)
{
assert(dest && src); //断言判断其是否为空指针
char* ret = dest; //保存目标数组的首元素地址
while (*dest != '\0') //判断*dest是否为'\0'
{
dest++; //将dest指针向后移动直到'\0'为止
}
while (*dest++ = *src++); //将src字符串中的元素赋值给dest字符串之后
return ret; //返回dest首元素地址
}
void main()
{
char ch1[] = "abcdefg";
char ch2[20] = "jjj";
printf("%s", my_strcat(ch2, ch1));
//结果为jjjabcdefg
}
感谢大家的阅读,欢迎大家在评论区讨论,我会及时回复的
若有错误和缺陷,希望大家能够指出,我会及时改正;
制作不易,希望三连