赞
踩
C语言中有很多库函数,其中我们有必须知道一些函数的使用,其中比较典型是字符串函数,内存函数,和动态内存函数。这篇文章我们就来了解字符串函数。使用字符串函数要引用string.h头文件。
目录
这是我们最熟悉的一个求字符串长度的函数。我们先看C语言里面它的标准声明。 C定义size_t是无符号整数(unsigned int),我们会找字符串结束的标志'\0',之后返回无符号整数。我们来模拟实现这个函数。
- //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。这里不再做过多解释。
拷贝字符串函数,先传入要改变的字符串,再传入要拷贝的内容。返回的值是改变字符串的首元素地址。一定牢记,字符串结束的标志是'\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;
- }
追加字符串函数,先传入在前面的字符串,再传入在后面的字符串,返回的是最前面元素的首地址。 一样的,也是要确保目标字符串内存够大。我们先来看看目标空间不足的情况:
- 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’,之后令它们++相等。
比较字符串函数,从第个开始比较,之后比较下一个,一个一个的比较,直到比较完,如果都相同就返回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长度受限制的字符串函数。
传入在前的字符串,之后传入在后面的字符串,在之后传入要追加的元素个数,返回的是最前面元素的地址。
-
- 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;
- }
和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;
- }
和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;
- }
判断字符串(子串)是否为另一个字符串(父串)的子串。返回类型为指针,如果不是返回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算法,以后我会发布博客,大家下去也可以探索探索。
这个函数很有意思,你可以理解为字符串分割函数。相当于分隔字符串函数,要传入原字符串,之后在创建一个字符串记录原字符串的分隔符,调用一次返回的是原字符串首元素的地址,这个函数会把原字符串中的分隔符改为‘\0’,所以为了不让它修改原字符串,我们先复制一份,在修改。第一次调用后会把分隔符改为‘\0’,下一次要传入一个空指针(NULL),返回的是空指针后面元素的地址。
-
- int main()
- {
- //192.168.31.121
- //192 168 31 121 - strtok
- //char* strtok(char* str,char* sep)
- //sep参数是个字符串,定义了用作分隔符的字符集合
- char arr[] = "zpw@bitedu.tech";
- 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[] = "zpw@bitedu.tech";
- 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;
- }
这个函数会自动检查你是否使用错了别的函数,如果你使用函数错了,它会自动传一个数值进入这个函数(前提是你要调用它)。要引入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函数。
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;
- }
还有很多关于字符串的函数我们不可能一一去详细举例,我们也没有必要全部记住,我们需要的时候查找一下就可以。如果感觉写的还不错,请点点赞吧。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。