对于<string.h>库,有如下常用函数:
strlen
strcmp
strcpy
strcat
strchr
strstr
strlen
size_t strlen(const char *s);
返回s的字符串长度(不包括结尾的\0)
代码实现:
#include<stdio.h>
#include<string.h>
int main(int argc, char const *argv[]){
char line[] = "hello";
printf("strlen = %lu\n",strlen(line));
printf("sizeof = %lu\n",sizeof(line));
return 0;
}
strlen = 5
sizeof = 6
背后的实现方法是:遍历这个字符串,当遍历到\0的时候返回结果
代码实现:
size_t mylen(const char *s){
int idx = 0;
while(s[idx] != '\0'){
idx++;
}
return idx;
}
对于size_t,可以百度查看,当然这个换成int也是可以的
详细解释:
size_t在C语言中就有了。
它是一种“整型”类型,里面保存的是一个整数,就像int, long那样。这种整数用来记录一个大小(size)。size_t的全称应该是size type,就是说“一种用来记录大小的数据类型”。
通常我们用sizeof(XXX)操作,这个操作所得到的结果就是size_t类型。
因为size_t类型的数据其实是保存了一个整数,所以它也可以做加减乘除,也可以转化为int并赋值给int类型的变量。
类似的还有wchar_t, ptrdiff_t。
wchar_t就是wide char type,“一种用来记录一个宽字符的数据类型”。
ptrdiff_t就是pointer difference type,“一种用来记录两个指针之间的距离的数据类型”。
通常,size_t和ptrdiff_t都是用typedef来实现的。你可能在某个头文件里面找到类似的语句:
typedef unsigned int size_t;
而wchar_t则稍有不同。在一些旧的编译器中,wchar_t也可能是用typedef来实现,但是新的标准中wchar_t已经是C/C++语言的关键字,wchar_t类型的地位已经和char, int的地位等同了。
在标准C/C++的语法中,只有int float char bool等基本的数据类型,至于size_t,或size_type都是以后的编程人员为了方便记忆所定义的一些便于理解的由基本数据类型的变体类型。
例如:typedef int size_t;定义了size_t为整型。
by百度百科
strcmp
int strcmp(const char *s1,const char *s2);
作用是比较两个字符串,返回值为:
0:s1==s2
正数:s1>s2
负数:s1<s2
数值的大小为他们的差值s1 - s2,这个在翁恺的视频中能实现,但是我自己的只能返回-1或1。。。
姑且根据翁恺的视频继续走:
代码用例:
#include<stdio.h>
#include<string.h>
int main(int argc, char const *argv[]){
char s1[] = "abc";
char s2[] = "abc";
printf("%d\n",strcmp(s1,s2));
return 0;
}
都是abc的时候,输出结果为0,当将s2的a改为大写的A,此时输出是32,这个32是
a - 'A’的差
在此,解释一下strcmp的原理:
当我们有两个字符串:
s1:h e l l o \0
s2:h e l l o \0
系统会将这两个字符串同时从头遍历并比较大小,直到比较到最后的\0,并且两者相同,返回0
如果是
s1:h e l l _ \0(注:_ 是空格)
s2:h e l l o \0
同样的系统会进行遍历,遍历到第五项的时候发现两者不同,那么就进行相减
‘_’ - ‘o’ = 32 - 127 = -95然后输出
根据strcmp的原理,可以自己写代码实现:
注释部分原理和非注释部分相同,都可以用,后者是直接用指针++将位置往前移
int mycmp(const char *s1,const char *s2){
//while退出条件:s1与s2不相同,或者是其中一个遍历到\0(翁恺老师视频部分没有s2!=\0,这是我个人补充)
// int idx = 0;
// while(s1[idx] == s2[idx] && s1[idx] != '\0' && s2[idx] != '\0'){
// idx++;
// }
while(*s1 == *s2 && *s1 != '\0' && *s2 != '\0'){
s1++;
s2++;
}
return *s1 - *s2
// return s1[idx] - s2[idx];
}
strcpy(copy)
char *strcpy(char *restrict dst, const char *restrict src);
把src的字符串拷贝到dst
resrtict标明src和dst不重叠(C99)
返回dst
为了能链起代码来
重叠的意思是:
假如src的字符串内容是“hello\0”
dst的字符串是“aaahello\0”
dst的后面部分就是src的内容,因此这叫做重叠,不能使用strcpy
strcpy多用于复制一个字符串,使用方法一般为:
char *dst = (char*)malloc(sizeof(src));
strcpy(dst,src);
原视频中为strlen(src)+1,结果一样,+1的目的就是加上\0
对于src和dst的长度,最后两者长度都会变得一样,就是强行将src赋值到src上
strcpy的粗略原理(更具体需要涉及到CPU的多核之类)就是将src的字符逐一按顺序加到同一位置的dst中
粗略实现:
char *mycpy(char *dst,const char *src){
// int idx = 0;
// while(src[idx]){
// dst[idx] = src[idx];
// idx++
// }
// dst[idx] = '\0';
// return dst;
char *ret = dst;
while(*src != '\0'){
*dst = *src;
dst++;
src++;
}
return ret;
}
同样的,注释的是数组实现,下面的是指针实现方式,选择其中一个谈一下优化的方式:
*dst = *src;
dst++;
src++;
这个可以合并成一整条
*dst++ = *src++;
对于
while(*src != '\0')
主要是对于’\0’,其实其本身值也是0、null的意思(可见字符串I),所以只要读取src,到最后返回为0,自然能跳出while循环,也就是
while( *src )
那就优化成了:
while( *src ){
*dst++ = *src++;
}
这里先补充一下赋值运算符,它也是有返回值的,可以用一条简单的代码查看一下它的返回值是什么:
printf("%d\n",i=3);
此时输出结果是3,就是说你赋值什么值,它返回的也就是什么值。
所以说对于上述代码,我们可以在进行优化:
while( *dst++ = *src++; ){
}
内部就是一个空循环,每次返回值都是src位置上的值
(不用担心返回为0的情况,因为字符串操作返回的那个是’0’,和0还是不一样的)
strcat
char *strcat(char *restrict s1,const char *restrict s2);
把s2拷贝到s1的后面,接成一个长的字符串
返回s1
s1必须具有足够的空间
具体实现方法:
首先dst要具有足够的空间容纳,然后src[0]传入dst[strlen(dst)]中,也就是dst的\0位置,然后pos逐步增加
具体实现:
#include<stdio.h>
#include<string.h>
char *mycat(char *dst,const char *src);
int main(int argc, char const *argv[]){
char s1[100] = "abcaaaaaaa";
char s2[] = "Abcc";
printf("%s",strcat(s1,s2));
return 0;
}
char *mycat(char *dst,const char *src){
while(dst[strlen(dst)] = *src++){
}
return dst;
}
strcpy和strcat的安全问题
strcpy和strcat都可能出现安全问题,如:
如果目的地没有足够的空间?
这样会造成数组越界等很多不敢想象的问题发生,因此后来有了一个安全版本:
char *strncpy(char *restrict s1, const char *restrict src, size_t n);
char *strncat(char *restrict s1, const char *restrict s2, size_t n);
int strncmp(const char *s1, const char *s2, size_t n);
相比原函数,命名上就是str后面多了一个n
在参数上,多了一个n,这个n代表的是用户能够拷贝(cat是连上,下略)过去最多多少个字符,对于cmp的n,指的是判断的前n位
字符串搜索函数
字符串中找字符:
char *strchr(const char *s, int c);
char *strrchr(const char *s, int c);
int c就是字符的ascii码,问题不大。返回类型是字符串,它会返回要寻找的字符之后的字符串,比方说"hello",寻找’l’,那就会返回"llo"。
返回null表示没有找到
int main(int argc, char const *argv[]){
char s[] = "hello";
char *p = strchr(s,'l');
printf("%s\n",p);
}
如何寻找第二个?
此时已经给p赋予了后续字符串的内容,那么我们再对p进行strchr就可以了,注意要将p的位置+1,不然会包含第一个’l’进去
int main(int argc, char const *argv[]){
char s[] = "hello";
char *p = strchr(s,'l');
p = strchr(p+1,'l');
printf("%s\n",p);
}
同理,第n个也会了吧
若想将找到的东西给另一个指针保存,可以用
int main(int argc, char const *argv[]){
char s[] = "hello";
char *p = strchr(s,'l');
char *t = (char*)malloc(sizeof(p));
strcpy(t,p);
printf("%s\n",t);
free(t);
}
若想保存字符串前面部分(也就是he),先给出代码如下:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int main(int argc, char const *argv[]){
char s[] = "hello";
char *p = strchr(s,'l');
char c = *p;
*p = '\0';
char *t = (char*)malloc(sizeof(p));
strcpy(t,s);
*p = c;
printf("%s\n",t);
free(t);
}
这个过程可以用下图解释:
首先str的字符串是
h e l l o \0
1.通过strchr将p的位置指向了第一个l处
2.这时候我们用一个c保存一下*p(也就是p[0],也就是l所在的位置)
3.然后我们将这个位置置零(\0)
4.再使用strcpy将str赋到t上,由于系统检测到第三位是\0了,就赋值结束返回。
5.最后再
*p = c
变回来就可以了
(这个对指针的操作真是太强大了!)
字符串中找字符串
char *strstr(const char *s1, const char *s2);
char *strcasestr(const char *s1, const char *s2);
顾名思义,就是在字符串中寻找字符串,方法类似strchr,后者寻找方式不区分大小写