目录
对于这样一个字符数组:
char word[] = {'H','e' 'l','l','o','!'};
这不是C语言的字符串,因为不能用字符串的方式做计算。如果在数组初始化的最后加上'\0'或者0,word还是字符数组,但其变成了C语言的字符串,可进行字符串运算了。接下来介绍字符串。
字符串
简介
是以0(整数0)结尾的一串字符。
0或'\0'是一样的,但是和'0'(ASII码中的0,是人可以读到的0)不同。0表达的是int 整数的0,int可能是四个字节或更大,而'\0'一定是一个字节的东西。
0标志着字符串的结束,但它不是字符串的部分,计算字符串长度的时候不包含这个0;
字符串以数组的形式存在,以数组或指针的形式访问(更多的时候是以指针的形式);
string.h里有很多处理字符串的函数。
字符串变量的几种形式:
char *str = “Hello”; \\定义了一个指针,指针指向的内容是Hello
char word[] = “Hello”;\\定义了一个字符数组,数组里的内容是Hello
char line[10] = “Hello”;\\有一个字符数组叫做line,line有十个字节那么大,往里面放了Hello,占了六个字节的位置(别忘了结尾的0);
字符串常量
像“Hello”这样的就是字符串常量,会被编译器变成一个字符数组放在某处,这个数组的长度是6,结尾还有表示结束的0。他们的长度=所看见的长度+1;
两个相邻的字符串常量会被自动连接起来,如下图试验:
C语言的字符串是以字符数组的形态存在的:不能用运算符对字符串做运算,通过数组的方式可以遍历字符串。
唯一特殊的地方是字符串字面量可以用来初始化字符数组
标准库提供了一系列字符串函数。
字符串常量试验
做以下一系列试验:
图1
图2
图3
i位置的输出结果表明,i这个本地变量与s和s2所指向的那个位置相距很远。
图3中s和s2输出的位置表明,s和s2指向了某个地方的一个字符串,内容是一个字符数组,而且那个地方的地址很小,位于程序的代码段,是只读的。
char* s = "Hello,world!";
s是一个指针,初始化为一个字符串常量
因为这个字符串在编译时刻就已经有值了,所以编译器会把它放在一个只能读不能写的地方,然后让你的指针指向它,并且如果你的程序中有两处相同的东西,指针会指向同一个地方,所以实际上s是const char* s,只不过编译器接受不带const的写法而已。但是,试图对s所指的字符串做写入会导致严重的后果,如图1。
如果需要修改字符串,应该用数组:
char s[] = "Hello,world!";如下:
#include<stdio.h>
int main(void)
{
int i = 0;
char *s = "Hello World";
//s[0] = 'B';
char *s2= "Hello World";
char s3[]="Hello World";
printf("&i=%p\n",&i);
printf("s =%p\n",s);
printf("s2=%p\n",s2);
printf("s3=%p\n",s3);
s3[0] = 'R';
printf("Here!s[0]=%c\n",s[0]);
printf("Here!s3[0]=%c\n",s3[0]);
return 0;
}
如上图,s3的位置输出与i的输出位置很相近,说明,s3的数组存放在本地变量这。s3[0]位置上的数也可以被修改。
指针or数组?
所以,当我们需要程序中有个字符串的时候,我们应该把它写成指针还是数组的形式呢?
数组:这个字符串在这里。可作为本地变量空间自动被回收。
指针:这个字符串不知道在哪里。当只需要读那个字符串时,处理参数时(数组作为函数的参数实际上与指针是一样的,所以不妨用指针表达这个函数的参数),做动态分配空间时。
简单来说:如果要构造一个字符串---->数组
如果要处理一个字符串---->指针
字符串可以表达为char*的形式
但char*不一定是字符串,可能指向字符的数组,也可能指向单个的数组。只有当它所指的字符数组有结尾的0时,才能说它所指的是字符串。
字符串赋值
char *t="title";
char *s;
s = t;
以上操作并没有产生新的字符串,只是让指针s指向了t所指的字符串,对s的任何操作就是对t做的
字符串输入与输出
输入与输出
char string[8];
scanf("%s",string);
printf("%s",string);
看来只识别到了一个单词
输入时两个单词中间有空格,输出时一个空格也没有。这是因为空格只是用来区分这两个单词的,是一个分隔符。如果在输入两个单词时中间用回车分隔,输出也是不变的。
所以,scanf读入一个单词(到空格、tab、或回车为止)
但是这样的scanf是不安全的,因为不知道要读入的内容的长度,容易发生越界。
安全输入是在%和s之间放一个数字(比如说x),表示最多允许读入的字符的数量,这个数字应该比数组的大小小1。如果正好读到了x个,后面有没有空格已经不影响了,不再使用空格来分隔单词,而是根据个数来划定单词,下面的内容会由后面的scanf来阅读。
常见错误
如:char *string;
scanf("%s",string);
错以为char*是字符串类型,定义了一个字符串类型变量string就可以直接使用了。
由于没有对string初始化为0,所以不一定每次运行都出错
空字符串
char buffer[100]="";这是一个空的字符串,buffer[0] == '\0'
char buffer[]="";这个数组的长度只有1!只有buffer[0]是有效的,为0;
字符串函数
C语言标准库中的函数:
strlen strcmp strcpy strcat strchr strstr
#include<string.h> (这些函数的原型在头文件string.h中)
strlen
size_t strlen(const char *s) ;
返回s的字符串长度(不包括结尾的0)
strcmp
int strcmp(const char *s1,const char *s2);
比较两个字符串
在Dev C++编译环境中返回:
0: 若s1==s2
1: 若s1>s2
-1: 若s1<s2
在一些其他的编译环境中:
如果两个字符串相同,相等或相同,则返回“ 0”
如果第一个不匹配字符的ASCII值小于第二个字符,则为“负整数”,str1-str2。
如果第一个不匹配字符的ASCII值大于第二个,则为“正整数”,str1-str2
上图是在Dev c++中,返回的是1,若在其他环境,返回32。
当在s2字符数组中加了一个空格后,输出的是-1;
如果在其他编译环境,输出-32,原因如上图
strcpy
char *strcpy(char *restrict dst,const char *restrict src);
把第二个参数里面所表达的字符串拷贝到第一个字符串所表达的空间里去(如把src的字符串拷贝到dst)
restrict表明src和dst不重叠(C99)
返回dst是为了能链起代码来
strcat
char * strcat(char *restrict s1,const char *restrict s2);
把s2拷贝到s1的后面,接成一个长的字符串
返回s1,且s1必须具有足够的空间
安全问题:
strcpy和strcat都可能出现安全问题,如果目的地没有足够的空间怎么办?
安全版本(带n):
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);
字符串中找字符
char *strchr(const char *s,int c);从左向右寻找c第一次出现的位置,返回该字符位置的指针
char *strrchr(const char *s, int c);从右向左寻找
返回NULL表示没有找到
至此,通过慕课网入门c语言的学习已经完成。
部分整理自慕课网“程序设计入门——C语言”课程,部分文字及图片来自原课程
如有错误,欢迎纠正
如有侵权,请联系删除