目录
前言
一、字符串函数
1.strlen
strlen模拟实现
2.strcpy
strcpy模拟实现
3.strcat
strcat模拟实现
4.strcmp
strcmp模拟实现
5.strncpy
strncpy模拟实现
6.strncat
strncat模拟实现
7.strncmp
strncmp模拟实现
8.strstr
strstr模拟实现
9.strtok
strtok模拟实现
10.strerror
二、字符分类函数
三、字符转换函数
四、内存函数
1.memcpy
memcpy模拟实现
2.memmove
memmove模拟实现
3.memcmp
小结
- 个人主页:库库的里昂
- CSDN新晋作者
- 欢迎 点赞✍评论⭐收藏
- ✨收录专栏:C语言进阶
- ✨其他专栏:代码小游戏、C语言初阶、C语言每日一练
- 希望作者的文章能对你有所帮助,有不足的地方请在评论区留言指正,大家一起学习交流!
C语言中对字符和字符串的处理很是频繁,但是C语言本身是没有字符串类型的,字符串通常放在常量字符串中或者字符数组中。字符串常量适用于那些对它不做修改的字符串函数。
函数介绍:
size_t strlen ( const char * str );
函数功能:
求字符串的长度
#include
#include
#include
size_t my_strlen(const char* p)
{
int count = 0;
assert(p != NULL);//如果等于空指针就会报错
while((*p) != '\0')
{
count++;
p++;
}
return count;
}
int main()
{
char arr[] = "CSDN";
int ret = my_strlen(arr);
printf("%d", ret);
return 0;
}
函数介绍:
char * strcpy ( char * destination, const char * source );
函数功能:
字符串拷贝函数,把源字符串拷贝到目标空间
注意事项:
char* my_strcpy(char* dest, const char* src)
{
assert(dest && src);
char* ret = dest;
while (*dest++ = *src++)
{
;
}
return ret;
}
int main()
{
char arr1[20] = { 0 };
char arr2[] = "hello world";
my_strcpy(arr1, arr2);
printf("%s\n", arr1);
return 0;
}
函数介绍:
char * strcat ( char * destination, const char * source );
函数功能:
字符串追加函数,将源字符串追加到目标字符串后面,目标中的终止字符’\0’会被源字符串的第一个字符覆盖
注意事项:
char* my_strcat(char* dest, const char* src)
{
assert(dest && src);
char* ret = dest;
//找目标空间的'\0'
while (*dest != '\0')
{
dest++;
}
//追加
while (*dest++ = *src++)
{
;
}
return ret;
}
int main()
{
char arr1[20] = "hello ";
char arr2[] = "world";
my_strcat(arr1, arr2);
printf("%s\n", arr1);
return 0;
}
函数介绍:
int strcmp ( const char * str1, const char * str2 );
函数功能:
字符串比较函数
注意事项:
int my_strcmp(const char* str1, const char* str2)
{
assert(str1 && str2);
while (*str1 == *str2)//如果相等就进去,两个指针加加,但是可能会出现两个字符串相等的情况,两个指针都指向'\0',此时比较就结束了
{
if (*str1 == '\0')
{
return 0;
}
str1++;
str2++;
}
if (*str1 > *str2)
{
return 1;
}
else
{
return -1;
}
}
int main()
{
char arr1[] = "abq";
char arr2[] = "abq";
int ret=my_strcmp(arr1, arr2);
printf("%d\n", ret);
return 0;
}
函数介绍:
char * strncpy ( char * destination, const char * source, size_t num );
函数功能:
长度受限的字符串拷贝函数
注意事项:
char* my_strncpy(char* dest, const char* src, int num)
{
assert(dest && src);
char* ret = dest;
while (num)
{
if (*src == '\0')//此时说明src指针已经指向了待拷贝字符串的结束标志'\0'处,src指针就不用再++了
{
*dest = '\0';
dest++;
}
else
{
*dest = *src;
dest++;
src++;
}
num--;
}
return ret;
}
int main()
{
char arr1[20] = "xxxxxxxxxxxxxxxxxxx";
my_strncpy(arr1, "abcdef", 10);
printf("%s\n", arr1);
return 0;
}
函数介绍:
char * strncat ( char * destination, const char * source, size_t num );
函数功能:
长度受限的字符串追加函数
注意事项:
char* my_strncat(char* dest, const char* src, int sz)
{
assert(dest && src);
char* ret = dest;
//找目标空间的\0
while (*dest != '\0')
{
dest++;
}
//追加
while (sz)
{
*dest++ = *src++;
sz--;
}
*dest = '\0';
return ret;
}
int main()
{
char arr1[20] = "abc\0xxxxxxxxxxx";
my_strncat(arr1, "defjhigk", 3);
printf("%s\n", arr1);
return 0;
}
函数介绍:
int strncmp ( const char * str1, const char * str2, size_t num );
函数功能:
长度受限的字符串比较函数
注意事项:
int my_strncmp(const char* str1, const char* str2, int sz)
{
assert(str1 && str2);
while (sz)
{
if (*str1 < *str2)
{
return -1;
}
else if (*str1 > *str2)
{
return 1;
}
else if(*str1 == '\0'||*str2 =='\0')//当有一个为'\0',说明比较就可以结束了
{
if (*str1 == '\0' && *str2 == '\0')//如果二者都是'\0',说明两个字符串相等
{
return 0;
}
else if(*str1 =='\0')//如果str1为'\0',说明str1小,str2大
{
return -1;
}
else//如果src为'\0',说明str1大,str2小
{
return 1;
}
}
sz--;
str1++;
str2++;
}
}
int main()
{
int ret = my_strncmp("abcdef", "abcd", 5);
printf("%d\n", ret);
return 0;
}
函数介绍:
char * strstr ( const char *str1, const char * str2);
函数功能:
字符串查找函数
注意事项:
char* my_strstr(char* str1, char* str2)
{
assert(str1 && str2);
if (*str2 == '\0')
{
return str1;
}
char* s1 = str1;
char* s2 = str2;
char* cp = str1;
while (*cp)
{
s1 = cp;
s2 = str2;
while (*s1 != '\0' && *s2 != '\0' && *s1 == *s2)
{
s1++;
s2++;
}
if (*s2 == '\0')
{
return cp;
}
cp++;
}
return NULL;
}
int main()
{
char arr1[] = "abbvcbcbbdbbvbnui";
char arr2[] = "bbvb";
char* ret = my_strstr(arr1, arr2);
if (ret == NULL)
{
printf("找不到\n");
}
else
{
printf("%s\n", ret);
}
return 0;
}
函数介绍:
char * strtok ( char * str, const char * sep );
函数功能:
字符串拆分函数
注意事项:
int main()
{
char arr[] = "[email protected]";
char* p = "@.";
char buf[20] = { 0 };
strcpy(buf, arr);
char* ret=NULL;
for (ret = strtok(buf, p); ret != NULL; ret = strtok(NULL, p))
{
printf("%s\n", ret);
}
return 0;
}
函数介绍:
char * strerror ( int errnum );
函数功能:
把错误码转换成错误信息的函数
注意事项:
一些错误码对应的错误信息:
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));
printf("%s\n", strerror(5));
return 0;
}
//结果:
No error
Operation not permitted
No such file or directory
No such process
Interrupted function call
Input/output error
函数 如果它的参数复合下列条件就返回真(一个非零数字)
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 任何可打印字符,包括图形字符和空白字符
tolower 将大写字母转换为小写字母
toupper 将小写字母转换为大写字母
int tolower ( int c );
int toupper ( int c );
注意事项:
示例:
int main()
{
char arr[] = "I Have An Apple";
int i = 0;
while (arr[i])
{
if (isupper(arr[i]))
{
printf("%c", tolower(arr[i]));//把转换的小写字母直接打印出来
}
else
{
printf("%c", arr[i]);
}
i++;
}
return 0;
}
//结果:
i have an apple
int main()
{
char arr[] = "I Have An Apple";
int i = 0;
while (arr[i])
{
if (isupper(arr[i]))
{
arr[i] = tolower(arr[i]);//用转换后的小写字符替换原有的大写字符
}
printf("%c", arr[i]);
i++;
}
return 0;
}
以上介绍的这些函数都是针对字符串或者字符的,那如果我们要拷贝其他类型的数据呢?用上面这些函数自然就行不通了,此时就要用到即将介绍的内存函数了。
函数介绍:
void * memcpy ( void * destination, const void * source, size_t num );
函数功能:
内存拷贝函数
注意事项:
这里的destination指向要在其中赋值内容的目标数组,source指向要复制的数据源,num是要复制的字节数,注意这里前两个指针的的类型还有函数返回值都是void*,这是因为,memcpy这个函数是内存拷贝函数,它有可能拷贝整型,浮点型,结构体等等各种类型的数据……虽然返回类型是void*,但他也是必不可少的,void*也表示一个地址,用户可以把它强制转换成自己需要的类型去使用。
void* my_memcpy(void* dest, const void* src, size_t num)
{
assert(dest && src);
void* ret = dest;
while (num)
{
*(char*)dest = *(char*)src;
((char*)dest)++;
((char*)src)++;
num--;
}
return ret;
}
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[10] = { 0 };
my_memcpy(arr2, arr1+2, 16);
return 0;
}
上面代码中模拟出来的my_memcpy函数已经可以实现把arr1数组中的元素拷贝到arr2数组里面去。但当我们想把arr1数组中的1、2、3、4、5拷贝到arr1数组中的3、4、5、6、7上去时,就会发生错误,如下图:
原因是:当1拷贝到3上时,原来的3已经被1替换,当2拷贝到4上的时候,原来的4已将被2替换。所以当拷贝arr[2]到arr[4]上的时候,原本arr[2]里面存放的3已将被1替换了,同理,所以才得出了不符合我们预期的结果。那如何解决这个问题呢?先来分析这个问题产生的原因,这是因为源空间与目标空间之间有重叠,这里的arr[2]、arr[3]、arr[4]既是源空间也是目标空间,当拷贝1和2的时候把源空间中开没有拷贝的3和4就给覆盖了,此时源空间arr[2]和arr[3]里面存的就不再是3和4了,而是1和2,所以此时拷贝arr[2]和arr[3]里面的数据,其实拷贝的就是1和2。为了解决这个问题,我们可以从后往前拷贝,此时就不会出现这样的问题
解决的思路就是:先拷贝重叠区域的元素,避免其还没拷贝就被覆盖掉。但并不是一遇到源空间和目标空间重叠就从后王前拷贝,比如下面这样,此时要把3、4、5、6、7拷贝到1、2、3、4、5里面
此时就不能用上面的方法从后往前拷贝了,但是可以根据上面提到的解决思路:先拷贝重叠区域的元素,所以此时就应该从前往后拷贝,即按照3、4、5、6、7的顺序去拷贝
因此在拷贝的时候,是按照从前往后的顺序还是从后往前的顺序得看具体的情况。当目标空间的起始位置在源空间的前面时,就得从前往后进行拷贝。当目标空间的起始位置在源空间的后面的时候,就要从后往前拷贝。当源空间和目标空间没有任何重叠的时候,不管是从前往后拷贝还是从后往前拷贝效果都一样。
接下来介绍的memmove函数就可以完成重叠空间的元素拷贝。
函数介绍:
void * memmove ( void * destination, const void * source, size_t num );
函数功能:
内存拷贝函数
注意事项
它的参数、返回值与memcpy函数一模一样。这里就不过多介绍。对于这两个函数来说,目标空间必须足够大,不然就会发生越界访问。
void* my_memmove(void* dest, const void* src, int 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--)//num为1的时候,下面的num就是0
{
*((char*)dest + num) = *((char*)src + num);//通过num的减减就可以实现对每一个字节的访问
}
}
return ret;
}
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
my_memmove(arr1+2, arr1, 20);
return 0;
}
函数介绍:
int memcmp ( const void * ptr1,const void * ptr2,size_t num )
函数功能:
内存比较函数
注意事项:
上述的这些都是我们经常会在平时的代码中使用到的,熟练掌握起来为我们提供很大的帮助。当然,除此之外,C语言的库函数中还有许多有趣的有关字符和字符串的函数,如果你有兴趣想要了解,可以在下面的网站或软件找到它们,里面都有对每一个函数详细的各个部分的说明、作用的解析和示例,博主就不一一赘述了。