大家好,欢迎来到IT知识分享网。
C语言中有很多库函数,其中我们有必须知道一些函数的使用,其中比较典型是字符串函数,内存函数,和动态内存函数。这篇文章我们就来了解字符串函数。使用字符串函数要引用string.h头文件。
目录
字符串函数
1.strlen函数
这是我们最熟悉的一个求字符串长度的函数。我们先看C语言里面它的标准声明。
//size_t == unsigned int int my_strlen(const char* str) {//const修饰指针里面的变量,指针指向的内容不能通过解引用改变 int count = 0; assert(str != NULL); while (*str != 0)//==while(*str) { count++; str++; } return count; } int main() { //int len = my_strlen("abcdef"); //printf("%d\n", len); // 3 - 6=-3(无符号数运算结果还是无符号数) if (strlen("abc") - strlen("abcdef") > 0) {//strlen返回类型是unsignde int printf("hehe\n"); } else { printf("haha\n"); } return 0; }
这里打印的是“hehe”因为无符号运算无论如何结果都是大于0的,所以打印hehe.
我们还可以通过递归的方法来实现:
int my_strlen(const char* str) { if (*str == '\0') { return 0; } else { return 1 + my_strlen(str + 1); } } int main() { //递归的方式实现 strlen char arr[] = "abcd"; size_t len = my_strlen(arr); printf("len = %zd\n", len); return 0; }
当读到最后一个字符‘\0’时返回0。这里不再做过多解释。
2.strcpy函数
拷贝字符串函数,先传入要改变的字符串,再传入要拷贝的内容。返回的值是改变字符串的首元素地址。一定牢记,字符串结束的标志是’\0’。所以只需要将复制的字符串拷贝到’\0’即可,后面即使没有变也不需要读取。
我们先来看标准定义:
int main() { char arr1[20] = { 0 }; char arr2[] = "hello"; strcpy(arr1, arr2); printf("%s\n", arr1); return 0; }
目标空间必须确保足够大,能放的下拷贝的字符串。但是有一种情况,就是注意指针可能指向常量字符串,这样空间是不能被修改的。
接下来我们模拟实现strcpy.
char* my_strcpy(char* dest,const char* src) {//使src是常量字符串 assert(dest != NULL); assert(src != NULL); char* ret = dest; //ret指向dest的首位置 //while (*src) //{ // *dest++ = *src++; //} //*dest = *src; while (*dest++ = *src++) {//最后直接把'\0'赋给dest ; } //返回目的空间的起始地址 return ret; } int main() { //错误示范 //char*p="abcdef"是常量字符串内容不可改 //arr1[]="ab"会崩 char arr1[] = "abcdefghi"; char arr2[] = "bit"; my_strcpy(arr1, arr2); printf("%s\n", arr1); return 0; }
3.strcat函数
追加字符串函数,先传入在前面的字符串,再传入在后面的字符串,返回的是最前面元素的首地址。
int main() { char a1[] = "hello"; char a2[] = "world"; strcat(a1, a2); printf("%s\n", a1); return 0; }
所以空间一定要够。
int main() { char a1[30] = "hello\0xxxxxxx"; char a2[] = "world"; strcat(a1, a2); printf("%s\n", a1); return 0; }
使用库函数strcat不要给自己追加,否则会崩溃。
那这个情况该如何解决?放心,C语言考虑的很全面,会有办法,我们一会再讲。
我们再来模拟实现一下:
char*my_strcat(char* dest, const char* src) { assert(dest && src); char* ret = dest; //1.找到目的字符串的'\0' while (*dest != '\0') { dest++; } //2.追加 while (*dest++ = *src++) { ; } return ret; } int main() { char a1[30] = "hello"; char a2[] = "world"; my_strcat(a1, a2); printf("%s\n", a1); return 0; }
先在原字符串找到‘\0’,之后令它们++相等。
4.strcmp函数
比较字符串函数,从第个开始比较,之后比较下一个,一个一个的比较,直到比较完,如果都相同就返回0;前者>后者返回1(不同编译器返回值不同,可能是两个数的ASCII值的差);前者<后者返回-1(可能是两个值的ASCII值的差)。
int main() { //VS 2020 //> 1 //== 0 //< -1 //linux-gcc //> >0 //== 0 //< <0 char* p1 = "abcdef"; char* p2 = "sqwer"; //strcmp比较的不是不是字符串长度 //一对一对往后比较,不一样的话直接返回 //int ret = strcmp(p1, p2); if (strcmp(p1, p2) >0) { printf("p1>p2\n"); } else if (strcmp(p1, p2) == 0) { printf("p1=p2\n"); } else if (strcmp(p1, p2) <0) { printf("p1<p2\n"); } //printf("%d\n", ret); return 0; }
我们依旧来模拟实现一下:
int my_strcmp(const char* s1, const char* s2) { while (*s1 == *s2) { if (*s1 == '\0') { return 0; } s1++; s2++; } return *s1 - *s2; } int main() { int ret = my_strcmp("bbq", "abcdef"); if (ret > 0) { printf("大于\n"); } else if (ret == 0) { printf("等于\n"); } else { printf("小于\n"); } return 0; }

strcpy、strcat、strcmp长度不受限制的字符串函数。那么刚才strcat无法追加自己,我们总要解决,是不是加上指定追加的长度就可以完成?所以库函数又提供了一些长度受限的字符串函数。
strncpy、strncat、strncmp长度受限制的字符串函数。
5.strncat函数
传入在前的字符串,之后传入在后面的字符串,在之后传入要追加的元素个数,返回的是最前面元素的地址。
int main() { char arr1[30] = "hello"; char arr2[] = "world"; strncat(arr1, arr2, 8); //最后拷贝'\0';即使追加个数大于的原字符串长度 //就把最后的元素追加上去,之后补一个'\0' printf("%s\n", arr1); return 0; }
即使传入的整数>要拷贝的元素个数,最后会用‘\0’来代替。
我们来模拟实现strncat。
void my_strncat(char* dest, const char* str, int sz) { assert(str); char* cur = dest; while (*cur) { cur++; } int i = 0; for (i = 0; i < sz; i++) { *cur++ = *dest++; } } int main() { char arr1[20] = "abcd"; strncat(arr1, arr1, sizeof(char) * 7); printf("%s\n", arr1); return 0; }
6.strncpy函数
和strcpy函数很像,多传入一个要拷贝字符的数量。返回的类型是整数,和strcpy返回的规律是一样的。
int main() { char arr1[20] = "xxxxxxxxx"; char arr2[] = "hello"; strncpy(arr1, arr2, 7); printf("%s\n", arr1); return 0; }
我们来模拟一下:
void my_strncpy(char* dest, const char* str, size_t sz) { int i = 0; char* cur = str; for (i = 0; i < sz; i++) { if (i < strlen(cur)) { *dest++ = *str++; } else { *dest++ = '\0'; } } } int main() { char arr1[20] = "xxxxxxxxxxx"; char arr2[] = "abcd"; my_strncpy(arr1, arr2, sizeof(char) * 6); printf("%s\n", arr1); return 0; }
7.strncmp函数
和strcmp函数很像。返回的类型是整数,和strcmp返回的规律是一样的。
int main() { //strncmp - 字符串比较 const char* p1 = "abcdef";//p1指向内容不能修改 const char* p2 = "abcqwer"; //int ret = strcmp(p1, p2); int ret = strncmp(p1, p2, 3);//比较前n个字符串 printf("%d\n", ret); return 0; }
8.strstr函数
判断字符串(子串)是否为另一个字符串(父串)的子串。返回类型为指针,如果不是返回NULL(空指针)。可以理解为字符串匹配函数。
int main() { //strstr查找子字符串函数 //char* strstr(const char* string,const char* strCharSet); //NULL -- 空指针 //NUL/Null - '\0' //string //Null-terminated string to search 从'\0'开始查找原字符串 //strCharSet //Null-terminated string to search for 要查找的字符串 char* p1 = "abcdefghi"; char* p2 = "def"; char* ret = strstr(p1, p2); //如果没找到就返回空指针 //如果找到了返回的是源字符串中找到的比较字符串中首元素的地址 if (ret == NULL) { printf("子串不存在\n"); } else { printf("%s\n", ret); } return 0; }
我们来模拟实现strstr函数,通过暴力求解法BF算法。
//模拟实现strstr char* my_strstr(const char* p1, const char* p2) { assert(p1 && p2); char* s1 = p1; char* s2 = p2; char* cur = p1; if (*p2 == '\0') {//如果p2是空字符串 return p1; } while (*cur) { s1 = cur;//如果碰到两个相同,第三个不同,则下次到第二个相同的开始 //如:abcdddef // ddef s2 = p2;//比较的字符串每次从头开始 while ((*s1 != '\0')&&(*s2 != '\0')&&(*s1 == *s2)) { s1++; s2++; } if (*s2 == '\0') { return cur;//找到子串 } cur++; } return NULL;//找不到子串 } int main() { char* p1 = "abcdddefg"; char* p2 = "ddef"; char* ret = my_strstr(p1, p2); //如果没找到就返回空指针 //如果找到了返回的是源字符串中找到的比较字符串中首元素的地址 if (ret == NULL) { printf("子串不存在\n"); } else { printf("%s\n", ret); } return 0; }
因为指向主串的指针不会回溯,但是指向要匹配的字符串(子串)如果不匹配就要回溯。所以我们多定义两个变量来记录原位置。
这是最暴力的解决方式,当然有一种高效的KMP算法,以后我会发布博客,大家下去也可以探索探索。
9.strtok函数
这个函数很有意思,你可以理解为字符串分割函数。相当于分隔字符串函数,要传入原字符串,之后在创建一个字符串记录原字符串的分隔符,调用一次返回的是原字符串首元素的地址,这个函数会把原字符串中的分隔符改为‘\0’,所以为了不让它修改原字符串,我们先复制一份,在修改。第一次调用后会把分隔符改为‘\0’,下一次要传入一个空指针(NULL),返回的是空指针后面元素的地址。
int main() { //192.168.31.121 //192 168 31 121 - strtok //char* strtok(char* str,char* sep) //sep参数是个字符串,定义了用作分隔符的字符集合 char arr[] = ""; char* p = "@."; //strtok(arr,p); char buf[200] = { 0 }; strcpy(buf, arr); char*ret= strtok(arr, p); printf("%s\n", ret); ret= strtok(NULL, p); printf("%s\n", ret); ret= strtok(NULL, p); printf("%s\n", ret); return 0; }
strtok函数找到str中的下一个标记,并将其用’\0’结尾,返回一个指向这个标记的指针(就是返回前面的地址)。
上面这个函数写的很多余,很复杂。strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。也就是说,平常我们会先拷贝源字符串,之后对拷贝字符串进行修改。
这里我们没有看其内部的细节,也没有模拟实现(真的懒了),但我们大致可以猜到它里面可能使用了static修饰变量使其生命周期延长。感兴趣的同学可以下去看看源码里面是如何定义的。
int main() { char arr[] = ""; char* p = "@."; char tmp[20] = { 0 }; strcpy(tmp, arr); //zpw\0bitedu\0tech\0 char* ret = NULL; for (ret = strtok(tmp, p); ret != NULL; ret = strtok(NULL, p)) { printf("%s\n", ret); } return 0; }
10.strerror函数
这个函数会自动检查你是否使用错了别的函数,如果你使用函数错了,它会自动传一个数值进入这个函数(前提是你要调用它)。要引入errno.h的头文件。
errno是一个全局错误码的变量,如果库函数执行错误,就会把对应错误码赋值到errno中,默认0就是没有错误。
int main() { //C语言库函数 // 错误码 错误信息 //传0 - No error //传1 - Operation not permitted //传2 - No such file or directory //... //errno 是一个全局的错误码的变量 //当C语言的库函数在执行过程中发生了错误,就会把对应的错误码赋值到errno中 int i = 0; for (i = 0; i < 3; i++) { printf("%d: %s\n", i, strerror(i)); } char* str = strerror(errno);//要引入errno.h的头文件 return 0; }
perror就是printf + strerror函数。
11.其他字符传函数
islower判断字符是否为小写。
isdigit判断字符是否为数字。
tolower大写字符转小写字符,若还是小写字符,则就是小写字符。
toupper小写字符转大写字符若,还是大写字符,则就是大写字符。
int main() { char ch = 'w'; //int ret=islower(ch);//判断是不是小写字母 int ret = isdigit(ch);//判断是不是数字 char c= tolower('q');//大写转小写字母,如果本身就是小写字母就不动 char b= toupper('Q');//小写转大写字母 char arr[] = "I Am A Student"; //大写字母转小写字母 int i = 0; while (arr[i]) { if (isupper(arr[i])) { arr[i]=tolower(arr[i]); } i++; } printf("%s\n", arr); return 0; }
还有很多关于字符串的函数我们不可能一一去详细举例,我们也没有必要全部记住,我们需要的时候查找一下就可以。如果感觉写的还不错,请点点赞吧。
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/118841.html























