目录
字符串
char word []={'H','e','l','l','o','\0'}
- 一定要有 ‘\0’
字符串
- 以 0(整数0)结尾的一串字符
0 或 ‘\0’ 是一样的,但是和 ‘0’ 不同
- 0标志字符串的结束,但不是这个字符串的一部分
计算字符串长度的时候不包含这个0
- 字符串以数组的形式存在,以数组或指针的形式访问
更多是以指针形式
- < string.h > 里有很多处理字符串的函数
- C语言的字符串是以字符数组的形态存在的
不能用运算符对字符串做运算
通过数组的方式可以遍历字符串
- 字符串字面量可以用来初始化字符数组
字符串变量
char *str = "Hello" ;
char word [] = "Hello" ;
char line [10] = "Hello" ;
字符串常量
- “Hello”会被编译器变成一个字符数组放在某处,这个数组的长度是 6 ,结尾有表示结束的 0 .
- 两个相邻的字符串常量会被自动连接起来
printf("你好,"
"你过得好吗");
// ---------> 你好,你过得好吗
printf("你好,\
你过得好吗");
// ---------> 你好,你过得好吗
printf("你好,\
你过得好吗");
// ---------> 你好, 你过得好吗
- 如果程序里有两处相同的东西,这两个指针会指向同一个地方:
char* p1 = "Hello world" ;
char* p2 = "Hello world" ;
printf ( "%p",p ) ;
printf ( "%p",p1 ) ;
//两者结果相同
这两个地址指针指向同一个地址,且地址很小,位于程序的代码段, 只读
是一个const
char* s = "Hello world" ;
- s 是一个指针,初始化为一个字符串常量
- s 实际上是 const char* s,只能读不能修改
如果想修改字符串,应该用数组:
char s[] = "Hello world" ;
指针还是数组?
- 数组:构造了一个字符串,这个字符串就在这里,作为本地变量空间自动被回收
- 指针:不知道字符串在哪,用于 (1) 处理函数参数 ; (2) 动态分配空间 ; (3)不需要重写
要构造一个字符串 --------> 数组
要处理一个字符串 ---------> 指针
char* 与字符串
- 字符串可以表达为 char* 的形式
- char* 不一定是字符串
本意是指向字符的指针,也可能是指向字符的数组的指针
只有它所指的字符数组结尾有 0 ,才能说它指向的是字符串
字符串输入输出
字符串赋值
char* i = "title" ;
char j ;
j = i ;
结果是让 j 也指向 i 所指向的内容
字符串输入输出
scanf 读入一个单词(到空格、tab或回车为止)
scanf 是不安全的,因为不知道要读入的内容的长度
#include <stdio.h>
int main()
{
char word[8];
scanf ("%s", word ) ;
printf ("%s##\n", word ) ;
return 0;
}
//输入 Hello world
//输出 Hello##
#include <stdio.h>
int main()
{
char word[8];
char word2[8];
scanf ("%s", word ) ;
scanf ("%s", word2 ) ;
printf ("%s##%s##\n", word,word2 ) ;
return 0;
}
//输入 Hello world
//输出 Hello##world##
字符如果过长,超出了所定义 char [ i ] 的值,那么程序会出错,这种情况可以这样:
char word[8] ;
scanf ("%7s",word) ;
scanf ("%7s",word) ;
(这个数字应该比数组的大小小1,因为字符串结尾有 '\0' )
表示最多读 7 个字符,不会出错但不一定完整
#include <stdio.h>
int main()
{
char word[8];
char word2[8];
scanf ("%7s", word ) ;
scanf ("%7s", word2 ) ;
printf ("%s##%s##\n", word,word2 ) ;
return 0;
}
如果第一次输入的字符数超出定义中的数,那么就是按照字符个数来读入,而不是空格
常见错误
- 以为 char* 是字符串类型,定义了一个字符串类型的变量 string 就可以直接使用
char *string 只是一个指针变量,未指向某块内存空间时不能直接用
- (由于没有对 string 初始化为 0 ,所以不一定每次运行都出错)
空字符串
char buffer [100] = "" ;
- 这是一个空的字符串,buffer [ 0 ] == ' \0 '
char buffer [] = "" ;
- 这个数组的长度只有 1 ,buffer [ 0 ] == ' \0 '------> 放不下任何字符串
字符串数组
两种表示的区别
char word[]="hello";
char word[]={'h','e','l','l','o','\0'};
如上,“ ” 型会自动在字符串后加 ‘\0’ ,而 { } 型需要自己加上
指针表示
char **a
- a 是一个指针,指向另一个指针,那个指针指向一个字符(串)
char a[][] //no
char a[][10] //a[0] --> char [10]
程序参数
int main ( int argc, char const *argv[] )
argc 告诉后面的字符串数组一共有几个字符串
argv [ 0 ] 是命令本身,当使用 Unix 的符号链接时,反映符号链接的名字
单字符输入输出——putchar, getchar
putchar
int putchar ( int c ) ;
- 向标准输出写一个字符
- 返回类型是 int
- 返回写了几个字符,EOF(-1)表示写失败
getchar
int getchar ( void ) ;
- 从标准输入读入一个字符
- 返回类型 int ,是为了返回 EOF (-1)
CTRL-C ---> 强制程序结束
CTRL-Z ---> 程序得到了EOF状态 (windows)
CTRL-D ---> 程序得到了EOF状态 (linux)
以下一个简单的代码
#include<stdio.h>
int main(int argc,char const *argv[])
{
int ch;
while((ch=getchar())!=EOF){
putchar(ch);
}
printf("EOF\n");
return 0;
}
在按下回车之后,才会有字符输出
- 当输入CTRL-Z,shell 内出现 -1 表示 EOF
- 当输入CTRL-C,shell 直接将程序关闭
getchar 是一个个读入1,2,3
scanf ( "%d" ,) 是将123作为整体的一个数读入
与 printf 和 scanf
scanf和printf函数需要通过格式化字符串指定输入输出的格式,并使用特定的格式指示符(如"%d"表示整数)。
与getchar和putchar不同,scanf和printf可以处理各种不同类型的数据,并具有更强大的格式化输出功能。
字符串函数——头文件 string.h
strlen —— lenth
size_t strlen(const char *s) ;
返回 s 的字符串长度(不包括结尾的 ' \0 ' )
#include<stdio.h>
#include<string.h>
int main(int argc,const char *argv[])
{
char line[]="Hello";
printf("strlen=%lu\n",strlen(line)); ——> 5
printf("sizeof=%lu\n",sizeof(line)); ——> 6
return 0;
}
自己实现 strlen
#include<stdio.h>
mylen(const char *s)
{
int idx=0;
while(s[idx]!='\0'){
idx++;
}
return idx;
}
int main(int argc,const char *argv[])
{
char line[]="hello";
printf("mylen=%lu\n",mylen(line));
return 0;
}
strcmp —— compare
int strcmp(const char *s1,const char *s2) ;
比较两个字符串
- 返回值 = 0 : s1 == s2 ;
- 返回值 > 0 : s1 > s2 ;
- 返回值 < 0 : s1 < s2 ;
#include<stdio.h>
#include<string.h>
int main()
{
char s1[]="abc";
char s2[]="abc";
printf("%d",strcmp(s1,s2));
return 0;
}
输出0
#include<stdio.h>
#include<string.h>
int main()
{
char s1[]="abc";
char s2[]="Abc";
printf("%d",strcmp(s1,s2));
return 0;
}
输出32 ——> 大于 0 说明 s1 > s2 ,
32 是 'a' 和 'A' 的差值
自己实现 strcmp
#include<stdio.h>
int mycmp(const char *s1,const char *s2)
{
int idx=0;
while(s1[idx]==s2[idx]&&s1[idx]!='\0'){
//if (s1[idx]!=s2[idx]){
// break;
//}else if(s1[idx]=='\0'){
// break;
//}优化
idx++;
}
return s1[idx]-s2[idx];
}
//或者下面这种写法(优化)
int mycmp2(const char *s1,const char *s2)
{
while(*s1 ==*s2 && *s1!='\0'){
s1++;
s2++;
}
return *s1-*s2;
}
int main()
{
char s1[]="abc";
char s2[]="abc ";
printf("%d\n",mycmp(s1,s2));
printf("%d\n",mycmp2(s1,s2));
return 0;
}
strcpy —— copy
char *strcpy(char *restrict dst,const char *restrict src) ;
把 src 的字符串拷贝到 dst(包括 ' \0 ' )
- 注意是把后面的拷贝到前面
restrict 表明 src 和 dst 不重叠 (C99)
- 返回 dst
为了让 strcpy 的结果能够投入给其他函数的参数,参与其他运算
复制一个字符串
当从函数参数得到一个字符串,这是一个指针,指向一个我不知道的地方,通过 strcpy 把他复制在我这里
char *dst =(char)malloc(strlen(src)+1) ;
//首先用 malloc 动态申请空间,而申请的空间长度为
//strlen(src)+1 ——> 包括结尾的'\0'
strcpy(dst,src) ;
自己实现strcpy
#include<stdio.h>
#include<string.h>
char *mycpy(char*dst,const char *src)
{
int idx=0;
while(src[idx]){
dst[idx]=src[idx];
idx++;
}//这里没有把结尾的'\0'拷贝进去
dst[idx]='\0';
return dst;
}
//或者指针形式
char *mycpy(char*dst,const char *src)
{
char *ret=dst;
while(*src){
*dst=*src;//或直接写成 *dst++=*src++
dst++;
src++;
}
*dst='\0';
return ret; //对dst进行了++
}
//优化
char *mycpy(char*dst,const char *src)
{
char *ret=dst;
while(*dst++=*src++){
}
*dst='\0';
return ret;
}
strcat —— catenate
char *strcat (char *restrict s1,const char *restrict s2) ;
把 s2 拷贝到 s1 后面,接成一个长字符串
- 返回 s1
- s1 必须有足够空间
问题
strcpy 和 strcat 都可能出现安全问题,因为目的地可能没有足够空间
安全版本
char *strncpy(char *restrict dst,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) ;
n 对 cpy 表示能够拷贝多少个字符
对 cat 表示能够连上多少字符
对 cmp 表示要比较两串字符的前几个字符
字符串搜索函数
字符串中找字符
strchr —— 从左向右
char *strchr(const char *s,int c) ;
strrchr —— 从右向左
char *strrchr(const char *s,int c) ;
- 从左向右(从右向左)在字符串中寻找 c 第一次出现的位置
- 返回 指针
- 返回NULL,表示没有找到
如何找第二个?
#include<stdio.h>
#include<string.h>
int main()
{
char s[]="Hello";
char *p=strchr(s,'l');
p=strchr(p+1,'l'); //在 p 已经找出来之后,在 p 的基础上再进行寻找
//printf("%s\n",p); //仅用作提示
return 0;
}
拷贝练习
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main()
{
char s[]="Hello";
char *p=strchr(s,'l');
char *t=(char*)malloc(strlen(p)+1);
strcpy(t,p);
printf("%s\n",t);
free(t);
return 0;
}
输出找到的 l 之前的字符
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main()
{
char s[]="Hello";
char *p=strchr(s,'l');
char c=*p; //需要恢复则可以恢复
*p='\0';
char *t=(char*)malloc(strlen(p)+1);
strcpy(t,s);
printf("%s\n",t);
free(t);
return 0;
}
字符串中找字符串
strstr —— 字符串中找字符串
char *strstr(const char*s1,const char *s2) ;
strcasestr —— 在寻找过程中忽略大小写
char strcasestr(const char *s1,const char *s2) ;