单字符输入输出
putchar(int c)函数:向标准输出写一个字符,但是输入是int类型,返回类型也是int 表示写了几个字符,一般为1,EOF(即:值-1)表示失败。(end of fail)
getchar():从标准输入读入一个字符,返回类型也是int(因为要返回EOF表示输入结束了)
写一个程序进一步理解。
int ch;
while((ch=getchar())!=EOF){
putchar(ch);
}
printf("EOF\n");//这样来看读入什么会EOF
return 0;
不管怎么输入数字串、字符串都不停止,直到输入Ctrl-C,程序直接结束但没有看到EOF,这表示我们将程序强制结束了而不是正确的输入EOF。
第二次尝试输入Ctrl-D,得到输出EOF(Windows要输入Ctrl-Z)。而且另一件奇怪的事情是,即便输入12435435数字串,敲下回车之前都不会有回应,敲下回车后才原封不动地输出这一串。为什么?getchar()不是一个个读的吗?
原因:之前提到过中介shell,shell先处理输入的东西再给程序,输出的东西也先经过处理再呈现给我们。(当时在讲\b会变成什么样子)
用户(键盘等输入)→shell→程序
用户←shell←程序
shell对输入的东西做了行编辑,也就是敲下回车前输入的部分都在shell里处理,放在缓冲区里,按下回车后才送到程序那里。如:输入123,shell缓冲区里为“1,2,3,回车”。然后getchar()再读缓冲区。
如果按下Ctrl-D,shell会生成一个EOF的标志。而按下Ctrl-C,shell直接关闭了程序。
所以用户的输入是让shell填写缓冲区,而scanf()和getchar()都是在缓冲区内读。
字符串函数strlen
string.h
头文件中处理字符串的函数比如:strlen
、strcmp
、strcpy
、strcat
、strchr
、strstr
等strlen(const char*s)
:返回s的字符串长度(不包括结尾0)由参数表中的const可知该函数不会修改传入的数组。
char line[]="Hello";
printf("%lu\n%lu",strlen(line),sizeof(line));
输出结果:strlen=5,sizeof=6(结尾的0)
我们能不能写出一个strlen函数呢?
sizeof不行,因为我们得到的是指针所占据的大小。
我们需要遍历数组:H,e,l,l,o,\0
因为不知道数组有多大,用while循环。
int cnt=0;
while(s[cnt]!='\0'){
cnt++;
}
return cnt;
字符串数组strcmp
int strcmp(const char*s1,const char*s2)
:比较两个字符串。
返回值 | 意义 |
---|---|
0 | 相等 |
1 | s1大 |
-1 | s2大 |
大小是怎么定义的? | |
我们做一个尝试: | |
printf("%d\n",s1==s2); 来判断s1和s2是否相等。 |
|
然而即便s1和s2内容相同还是输出了0.因为实际上s1==s2 比较的是s1和s2的地址,所以数组之间的这种比较永远是0。 |
s1[]="abc";
s2[]="bbc";
再用strcmp
比较两者输出了-1.这很合理,因为ASCII码b>a.
s1[]="abc";
s2[]="Abc";
输出32?32是'a'-'A'
的结果。所以这回给出的结果是不相等的字符的差值。
s1[]="abc";
s2[]="abc ";//多了个空格
输出-32,是空格位\0-' '
造成的。
接下来我们自己尝试写strcmp。我们需要下标idx,s1[idx]与s2[idx]比较,当s1[idx]和s2[idx]都=='\0'时停止(假设长度相等)。
while(s1[idx]!='\0'&&s1[idx]==s2[idx])//当出现不相等的字符或者字符串到了末尾时,返回差值
idx++;
return s1[idx]-s2[idx];
改进:idx可不可以不用?
用指针:
*s1==*s2
s1++;
s2++;
return *s1-*s2;
这是处理字符串的两种手段,数组和指针。看个人喜好。
字符串函数strcpy
char*strcpy(char* restrict dst,char* restrict src);
把src拷贝到dst的空间里,包括结尾的\0.
restrict表示src和dst不能重叠。比如src是H E L L O \0
,dst的内容是:空 空 空 H E L L O \0
也就是说想把HELLO挪到第一位开始,这是不行的。因为strcpy对于多核计算机,为了提高效率,拷贝可能是交给不同的核不同的段,分别拷贝。
函数参数中的第一个参数是目的,而第二个参数是源。而且这个函数是有返回值的,返回dst
char *dst=(char*)malloc(strlen(src)+1);//不包含结尾的\0,所以+1
strcpy (dst,src);
+1是重点。
接下来尝试自己写strcpy()函数。
char *mycpy(char*dst,const char*src)
{
int idx=0;
while(src[idx]!='\0')
{
dst[idx]=src[idx];
idx++;
}
dst[idx+1]='\0';
return dst;
指针的做法是:
char *ret=dst;
while(*src!='\0')
{
*dst=*src;
*dst++;
*src++;
}
*dst='\0';
return ret;
当然do-while也行.
也可以这样优化:
while(*src)*dst++ = *src++;
更艹的是,*dst++ = *src++;
的结果就是*src,所以我们可以直接写
while(*dst++ = *src++);
字符串函数strcat
char*strcat(char* restrict s1,const char* restrict s2);
把s2拷贝到s1后面,接成一个长字符串;返回s1.(s1结尾的\0被s2开头替换掉)
如:s1: H E L L O \0
s2:W O R L D\0
结果:s1: H E L L O W O R L D \0
s1必须要有足够的空间。
cpy和cat有可能没有足够的空间,因此这两个函数是不够安全的,不建议使用。
安全版本:strncpy
和strncat
char*strncpy(char* restrict s1,const char* restrict s2,size_t n);
char*strncat(char* restrict s1,const char* restrict s2,size_t n);
char*strncat(const char* s1,const char* s2,size_t n);
会拷贝能拷贝的最多的字符,多的部分掐掉。而strncmp则是只判断前几个字符。
字符串搜索函数
char* strchr(const char* s,int c);
char* strrchr(const char* s,int c);//从右边开始找
返回的是指针,返回NULL表示没找到。
那如果像HELLO出现两个L,怎样寻找第二个呢?
char *s="hello";
char *p=strchr(s,'l');
printf("%s",p);
输出llo。
想找第二个的方法是:
p=strchr(p+1,'l');
如果想把l后面的东西cpy到另一个字符串中去:
char *t=(char*)malloc(strlen(p)+1);
strcpy(t,p);
free(t);
如果我们想要l前面的部分?
char c=*p;
*p='\0';//把l的位置改成'\0'
char *t=(char*)malloc(strlen(s)+1);
strcpy(t,s);//只拷贝了第一个l前面的部分。
t就是想要的结果,即he
在字符串中找字符串:char* strchr(const char* s1,const char*s2);
不分大小写寻找:char* strcasestr(const char* s1,const char*s2);