【基础C语言】五、字符串补充
五、字符串补充
基础补充
字符数组和字符串
字符数组和字符串不同,在C语言中二者是不同的
这是一个字符数组:char word[] = {'H','e','l','l','o','!'}
而这是一个字符串:char word[] = {'H','e','l','l','o','!','\0'}
二者的区别在于,我们在初始化该数组的过程中,用一个 \0 结尾
如此一来,字符串的单元总数会比字符数多一,也就是多了一个0
字符串是一个”字符数组”,而因为末尾的”\0”,它变成了C语言中的字符串
字符串以数组的形式存在,以数组或者指针的形式访问(更多时候是以指针的形式)
字符串变量
表达一个变量是字符串,有以下写法:
1 | char *str = "Hello" ; |
用双引号括起来的部分,被称为字符串的字面量
两个相连的字符串常量会被自动连起来
1 | printf("这里是一个例子," |
字符串变量
地址
当我们定义了两个完全一样的字符串是s1和s2时,它们实际上位于同一地址
事实上,s1和s2指向的地方位于程序的代码段,而且这一部分的内容都是只读的
也就是说,我们是无法在此修改字符串的
char* s = "Hello , world!" ;
此时s是一个指针,其初始化为指向一个字符串常量——而这个常量所在的地方是只读的区域
实际上s是const char* s ,由于历史原因,编译器接受不带const的写法。但是试图对s指向的字符串做写入则会导致严重后果
修改数组
如果想修改字符串,则应该应用数组来定义char s[] = "Hello , world!" ;
而此时字符串的意义变为”所要的字符串就在此处”,而非char* s 表示的”指向了某个地方的字符串”
1 | int main(void){ |
区别总结
指针: 不知道字符串具体位置,可以处理参数(数组作为参数时是和指针等同的),可以动态分配空间
数组: 字符串就在当前位置,可以动态修改,作为本地变量空间自动被回收
如果要处理一个字符串,就用指针
如果要构造一个字符串,就用数组
字符串可以表达为char*
的形式,而char*
不一定是字符串
其本意是指向字符串的指针,可能指向的是字符的数组
只有其所指的字符数组有结尾的0,才能认为它指向的是字符串
字符串的输入输出
字符串赋值
字符串的赋值实际上并没有产生新的字符串,只是让被赋值的指针指向了对应的区域
1 | char *t = "title" ; |
此处并没有产生新的字符串,而只是让指针s指向了t所指的字符串,对s的任何操作都是对t做的
输入输出
1 | char string[8] ; |
scanf会读入一个单词,到空格,tab,回车为止
scanf是不安全的,因为其不知道要读入的内容的长度
我们可以在%
和s
中间限定读取的数量scanf("%7s" , word) ;
这个数字表示了最多允许读入的字符的数量,这个数字应该比数组大小小1,因为字符串数组的末尾固定为0
另外,在声明字符串的时候,需要初始化为0以免出错
char buffer[100] = “” ; 此时这是一个空字符串,而buffer[0] == ‘\0’
char buffer[] = “” ; 此时这个数组的长度只有1
字符串数组与程序参数
字符串数组
有两种方式表示字符串数组
char[][X]
代表的是一个字符串数组,每一个字符串的大小至多为X(有X-1字符)
可以认为是把一个个“数组型的字符串”放置到了数组里面,比如
a[0] == world\0
a[1] == Hello\0
char* a[]
表示数组a[]里面存放了各个字符串(的地址)
a[0] ->地址1 ->指向”Hello\0”
a[1] ->地址2 ->指向”World\0”
两种方式是不一样的
程序参数
可以作为main
函数的参数,比如int main(int argc , char const *argv[])
其中argv[0]是命令本身
当使用Unix的符号链接时,反映符号链接的名字
字符串函数
putchar
int puchar(int c) ;
向标准输出写一个字符
返回写了几个字符,EOF(-1)表示写失败
getchar
int getchar(void) ;
从标准输入读入一个字符
返回类型是int是为了可以返回EOF(-1)
Window Ctrl-Z Unix Ctrl-D
string.h
在string.h中,有许多帮助处理字符串的函数
常用函数
strlen
size_t strlen(const char *s) ;
返回s的字符串长度,不包括结尾的0
strcpy
char * strcpy(char *restrict dst , const char *restrict src) ;
可以把src
的字符串拷贝到dst
在c99下,restrict表名src和dst不重叠
最终返回的是dst
另外,参数中,注意第一个参数是目的地,第二个是源
目的地需要有足够空间
1 | char *dst = (char*) malloc(strlen(src)+1) ; |
自主实现:
1 | char* myCpy(char* dst , const char* src){ |
strcat
char * strcat(char *restric s1 , const char *restrict s2) ;
把s2拷贝到s1的后面,接触成一个长的字符串
返回s1
s1需要有足够的空间
尽可能不要使用strcpy和strcat因为有安全问题,可以使用下面的安全版本
char * strncpy(char *restrict dst , const char *restrict src , sizr_t n) ;
chat * strcat(char *restrict s1 , const char *restrict s2 , size_t n) ;
int strncmp(const char *s1 , const char *s2 , size_t n );
size_t n 参数表示了最多可以运输多少字符
int strncmp :判断前n个字符是否是xxx
字符串搜索函数
char * strchr(const char *s , int c);
char * strrchr(const char *s , int c);
返回NULL表示没有找到
(前者是从左往右,后者是从右往左)
因为返回的是对应位置的指针,所以也可以尝试打印接下来的部分:
1 | char s[] = "hello" ; |
最后结果会是”llo”
因此我们可以利用这个特性寻找第n个字符,比如:
1 | char s[] = "hello" ; |
在字符串中寻找字符串
char * strstr(const char *s1 , const char *s2);
char * strcasestr(const char *s1 , const char *s2); //相较前者,忽略大小写