史上最强C语言教程—-字符函数&字符串函数
目录
1、C语言中是否存在字符串?
C语言中对字符和字符串的处理很是频繁,但是C语言本身是没有字符串类型的,字符串通常放在 常量字符串中或者字符数组中。 字符串常量适用于那些对它不做修改的字符串函数!
2、函数介绍
2.1 strlen()
strlen()函数的作用是求字符串的长度。
2.1.1模拟实现strlen()(三种方法实现)
(1)计数器方法
#include<assert.h> int my_strlen(const char* str) { assert(str != NULL); int count = 0; while (*str != '')//while(*str)---''的ascii码值为0 { count++; str++; } return count; }
(2)递归实现(不定义临时变量的方法)
#include<stdio.h> int my_strlen(const char* str) { if (*str != '') { return 1+my_strlen(++str); } else { return 0; } }
(3)尾指针-头指针
#include<stdio.h> #include<assert.h> int my_strlen(const char* str) { const char* ret = NULL; assert(*str != NULL); ret = str; while (*ret != "") { ret++; } return ret - str; }
2.1.2 注意点
代码:
#include<stdio.h> #include<string.h> int main() { const char* str1 = "abcdef"; const char* str2 = "bbb"; if (strlen(str2) - strlen(str1) > 0) { printf("str2>str1n"); } else { printf("srt1>str2n"); } return 0; }
运行截图:
原因:
strlen()函数的返回类型是size_t即unsigned int类型,在操作符两侧的数据类型都是unsigned int ,运算结果类型也必然是unsigned int,这个值永远是大于0的,所以会出现上面的结果!
结论
自己实现的时候将返回类型写成size_t和int各有利弊。
int可以有效的应对上面的这种特殊情况,但是不容易理解,因为int包括负数。
size_t更容易理解,但是无法有效的应对像上面的这种情况。
2.1.3 strlen()函数介绍
(1)字符串以 '' 作为结束标志,strlen函数返回的是在字符串中 '' 前面出现的字符个数(不包 含 '' )。
(2)参数指向的字符串必须要以 '' 结束。
(3)注意函数的返回值为size_t,是无符号的( 易错 )
2.2 strcpy()
strcpy()函数的作用是将一个字符串的内容拷贝到另一个字符串中去(包括'')。
2.2.1模拟实现strcpy()
#include<stdio.h> #include<assert.h> char* my_strcpy(char* dest, const char* src) { assert(dest != NULL); assert(src != NULL); char* ret = dest; //拷贝src指向的字符串到dest指向的空间,包含'' while (*dest++ = *src++) {} //返回目的空间的起始地址 return ret; }
2.2.2 注意事项
(1)源字符串必须以''结束,比如源字符串像这样定义的就无法正常拷贝了:
char src[] = {'m','i','n'};
(2)要将''拷贝到目标空间
(3)目标空间必须足够大,以确保能存放源字符串
(4)目标空间必须可变,比如目标字符串这样定义就不行了:
char *dest = "abcde";
因为此时的目标字符串所存放的空间是在字符串常量区,此处的字符串是无法进行修改的,此时程序会崩溃。
2.3 strcat()
strcpy()的作用是追加字符串。
2.3.1 模拟实现strcat()
#include<stdio.h> #include<assert.h> char* my_strcat(char* str1, const char* str2) { assert(str1&&str2); char* ret = str1; //1、找到目的字符串中''的位置 while (*str1 != '') { str1++; } //2、追加字符串(其实就是拷贝字符串) while (*str1++ = *str2++) { ; } return ret; }
2.3.2 注意事项
(1)源字符串必须以''结束
(2)目标空间必须有足够大的空间能容纳下源字符串的内容
(3)目标空间必须可修改
(4)不能自己给自己追加,那样程序会崩溃。
2.4 strcmp()
比较对应字符对应的ASCII码值的大小,而不是比较字符串的长度,逐个字符进行比较,如果所有字符都相同就返回一个等于0的数组,如果前面的小于后面的,就返回一个小于0的数字,大于后面的就返回一个大于0的数字。
2.4.1 模拟实现strcmp()
下面是VS2019的模拟实现方式
#include<stdio.h> #include<assert.h> int my_strcmp(const char* str1, const char* str2) { assert(str1 && str2); while (*str1 == *str2) { if (*str1 == '') { return 0;//相等 } str1++; str2++; } if (*str1 > *str2) { //大于 return 1; } else { //小于 return -1; } } int main() { char *str1 = "abcde"; char *str2 = "abcfg"; int ret = my_strcmp(str1, str2); printf("%d", ret); return 0; }
下面是linux的模拟实现方式
#include<stdio.h> #include<assert.h> int my_strcmp(const char* str1, const char* str2) { assert(str1 && str2); while (*str1 == *str2) { if (*str1 == '') { return 0;//相等 } str1++; str2++; } return (*str1-*str2); } int main() { char *str1 = "abcde"; char *str2 = "abcfg"; int ret = my_strcmp(str1, str2); printf("%d", ret); return 0; }
2.4.2 注意事项
标准规定
(1)第一个字符串大于第二个字符串,则返回大于0的数字
(2)第一个字符串等于第二个字符串,则返回0
(3)第一个字符串小于第二个字符串,则返回小于0的数字
注意
strcpy(),strcat(),strcmp()都是与长度无关的字符串函数,相对来说不是那么的安全,只受''的限制。
2.5 strncpy
2.5.1 strncpy()的模拟实现
#include<assert.h> char* my_strncpy(char* str1, const char* str2,int count) { assert(str1 && str2); char* ret = str1; int i = 0; for (i = 0; i < count; i++) { if (*str2 != '') { *str1++ = *str2++; } else { *str1++ = ''; } } return ret; }
2.5.2 注意事项
(1)拷贝num个字符从源字符串到目标空间。
(2)如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。
2.6 strncat()
2.6.1 模拟实现strncat()
#include<stdio.h> #include<assert.h> char* my_strncat(char* str1, const char* str2, int count) { assert(str1 && str2); char* ret = str1; while (*str1++ != ''); str1--; int i = 0; for (i = 0; i < count; i++) { if (*str2 != '') { *str1++ = *str2++; } else { *str1 = ''; return ret; } } *str1 = ''; return ret; }
2.7 strncmp()
2.7.1 模拟实现
#include<stdio.h> #include<assert.h> int my_strncmp(const char* str1, const char* str2, int count) { assert(str1 && str2); int i = 0; for (i = 0; i < count; i++) { if (*str1 < *str2) { return -1; } else if (*str1 > *str2) { return 1; } else { str1++; str2++; } } return 0; }
2.8 strstr()
注意:在读文档时出现NULL一般就是空指针,NUL或者Null就是''。
2.8.1 使用说明及示例
(1)前面是被查找的字符串,后面是要查找的字符串。
(2)如果能够找到,就返回后面的字符串在前面字符串中首次出现的地址,如果未出现,就返回空指针。
#include<stdio.h> #include<string.h> int main() { char* str1 = "abcdefghijk"; char* str2 = "def"; char*ret = strstr(str1, str2); if (ret == NULL) { printf("字串不存在n"); } else { printf("%sn",ret); } return 0; }
运行截图:
2.8.2 模拟实现strstr()
#include<stdio.h> #include<assert.h> char* my_strstr(const char* str1,const char* str2) { assert(str1 && str2); char* p1 = NULL; char* p2 = NULL; char* cur = str1; if (*str2 == '') { return (char*)str1; } while (*cur) { p1 = cur; p2 = (char*)str2; while ((*p1 != '') && (*p2 != '') && *p1 == *p2) { p1++; p2++; } if (*p2 == '') { return cur;//找到子串 } if(*p1=='') { return NULL; } cur++; } return NULL;//找不到子串 }
2.9 strtok()
2.9.1 使用说明及示例
char * strtok ( char * str, const char * sep );
(1)sep参数是个字符串,定义了用作分隔符的字符集合
(2)第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标 记。
(3)strtok函数找到str中的下一个标记,并将其用 结尾,返回一个指向这个标记的指针。(注: strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容 并且可修改。)
(4)strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串 中的位置。
(5)strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标 记。
(6)如果字符串中不存在更多的标记,则返回 NULL 指针。
#include<stdio.h> #include<string.h> int main() { char* arr = "[email protected]@abc.def.com"; char* p = "@."; char str[500] = { 0 }; strcpy(str, arr); char* ret = NULL; for (ret = strtok(str, p);ret != NULL;ret = strtok(NULL,p)) { printf("%sn", ret); } return 0; }
运行截图:
2.10 strerror()
2.10.1 使用说明及示例
返回错误码,所对应的错误信息。
示例:
#include<stdio.h> #include<string.h> int main() { char* str0 = strerror(0); printf("%sn", str0); return 0; }
运行截图:
0:No error
1:Operation not permitted
2:No such file or directory
3:No such process
……
下面是较为完整的用法:
#include<stdio.h> #include<errno.h> #include<string.h> int main() { //errno是一个全局的错误码的变量 //当C语言的库函数在执行过程中,发生了错误,就会把对应的错误码,赋值到errno这个变量中 char* str = strerror(errno); printf("%sn", str); return 0; }
2.11 其它函数
2.11.1 字符分类函数
注意引头文件<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
isalnum
字母或者数字,a~z,A~Z,0~9
ispunct
标点符号,任何不属于数字或者字母的图形字符(可打印)
isgraph
任何图形字符
isprint
任何可打印字符,包括图形字符和空白字符
判定结果为是的话返回一个非零值,如果不是返回一个零值。
2.11.2 字符转换函数
int tolower ( int c ); int toupper ( int c );
注意:只能一个字符一个字符的进行转换,不能一次转换一个字符串!
使用示例:
#include <stdio.h> #include <ctype.h> int main() { int i = 0; char str[] = "Test String.n"; char c; while (str[i]) { c = str[i]; if (isupper(c)) c = tolower(c); putchar(c); i++; } return 0; }
运行截图:
3. 内存函数
3.1 memcpy()
3.1.1 使用说明及示例
说明
(1)函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。
(2)这个函数在遇到 '' 的时候并不会停下来。
(3)如果source和destination有任何的重叠,复制的结果都是未定义的。
示例:
#include<stdio.h> #include<string.h> int main() { int arr1[] = { 1,2,3,4,5 }; int arr2[5] = {0}; memcpy(arr2, arr1, sizeof(arr1)); for (int i = 0; i < 5; i++) { printf("%d ", arr2[i]); } return 0; }
3.1.2 模拟实现
#include<stdio.h> #include<assert.h> void *my_memcpy(void* dest, const void* src, size_t num) { assert(dest && src); void* ret = dest; int i = 0; while(num--) { *(char*)dest = *(char*)src; ((char*)dest)++; ((char*)src)++; } return dest; }
3.2 memmove()
3.2.1 使用说明及示例
(1)和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。
(2)如果源空间和目标空间出现重叠,就得使用memmove函数处理。
示例:
#include<stdio.h> #include<string.h> int main() { int arr[] = { 1,2,3,4,5,6,7,8,9,10 }; memmove(arr + 2, arr, 20); for (int i = 0; i < 10; i++) { printf("%d ", arr[i]); } return 0; }
C语言标准:
memcpy()只要能处理不重叠的内存拷贝就可以了。
memmove处理重叠内存的拷贝。
3.2.2 模拟实现
#include<stdio.h>
#include<assert.h>
void *my_memmove(void* dest, void* src, size_t num)
{
//dest<src:从前向后拷贝
//dest>src&&dest<src+count:从后向前拷贝
//dest>src+count:从后向前和从前向后均可
assert(dest && src);
void* ret = dest;
if (dest < src)
{
while (num--)
{
*(char*)dest = *(char*)src;
((char*)dest)++;
((char*)src)++;
}
}
else
{
while (num--)
{
*((char*)dest+num )= *((char*)src+num);
}
}
return dest;
}
3.3 memcmp()
3.3.1 使用说明及示例
int memcmp ( const void * ptr1, const void * ptr2, size_t num );
示例:
#include<stdio.h>
#include<string.h>
int main()
{
int arr1[] = { 1,2,3,4,5 };
int arr2[] = { 1,2,4,5,6 };
int ret = memcmp(arr1, arr2, sizeof(arr1));
printf("%d", ret);
return 0;
}
3.4 memset()
3.4.1 使用说明及示例
void* memset(void* dest, int c, size_t count);
作用:设置缓冲区为一个特定的字符。
dest:目的地空间的起始位置。
c:要设定的字符(无论c是都看成字符)。
count:要设定字符的数目(注意是字符的数目。此处都是以字节为单位的,因为一个字符所占的内存空间是一个字节)
使用示例:
#include<stdio.h> #include<string.h> int main() { char arr[10] = ""; memset(arr, '#', 10); for (int i = 0; i < 10; i++) { printf("%c ", arr[i]); } return 0; }
运行截图: