字符数组
word[0] | H |
---|---|
word[1] | e |
word[2] | l |
word[3] | l |
word[4] | o |
word[5] | ! |
字符串
word[0] | H |
---|---|
word[1] | e |
word[2] | l |
word[3] | l |
word[4] | o |
word[5] | ! |
word[6] |
- 以0(整数0)结尾的一串字符
- 0或’\0’是一样的,但是和’0’不同
- 0标志字符串的结束,但不是字符串的一部分
- 计算字符串长度的时候不包含这个0
- 字符串以数组的形式存在,以数组或者指针的形式访问
- 更多的是以指针的形式
- string.h里面有很多处理字符串的函数
字符串常量&&变量
用指针的方式定义一个字符串
-
"Hello, World!"会被编译器变成一个字符数组放在某处,这个数组的长度比字面量大1,结尾还有表示结束的0.
我们来看个有意思的事情
我们定义了一个字符串,尝试更改其中的一个字符。
#include<stdlib.h>
int main(){
char *s = "Hello, World!";
s[0] = 'B';
printf("Here is s[0] = %c\n", s[0]);
return 0;
}
发现会在s[0]='B’的地方报错
Exception has occurred.
Segmentation fault
我们尝试定义两个内容相同的字符串变量,并分别输出两个字符串变量的地址
#include<stdio.h>
#include<string.h>
int main(){
int i;
char *s = "Hello, World!";
char *s1 = "Hello, World!";
printf("&i = %p\n",&i);
printf(" s = %p\n",s);
printf(" s1 = %p\n",s1);
return 0;
}
&i = 0061FF14
s = 00405044
s1 = 00405044
我们可以发现指针s和s1存放的地址是一样的。也就是指向相同的位置。
另外我们可以看到本地变量的地址比字符串所存储的地址大很多
本地变量大致在相同的位置,而字符串所存放的较小地址的位置叫做代码段,只读
这也就是我们为什么不能直接修改字符串的内容的原因
- s是一个指针,初始化为指向一个字符串常量
- 由于这个常量所在的地方,所以实际上s是const char* s(也就是不能通过指针修改),但是由于历史原因,编译器接受不带const的写法
- 但是尝试对s所指字符串做写入会导致严重的后果
- 如果要修改字符串,应用数组来定义
-
char s[] = “Hello, World!”;
用数组来定义字符串
我们用数组来定义一个字符串,内容跟指针定义的相同。首先我们输出一下数组的地址,并且尝试更换第一个字符。
#include<stdio.h>
#include<string.h>
int main(){
int i = 0;
char *s = "Hello, World!";
char *s1 = "Hello, World!";
char s2[] = "Hello, World!";
printf("&i = %p\n",&i);
printf(" s = %p\n",s);
printf(" s1 = %p\n",s1);
printf(" s2 = %p\n",s2);
s2[0] = 'B';
printf("Here is s[0] = %c\n", s2[0]);
return 0;
}
&i = 0061FF14
s = 00405044
s1 = 00405044
s2 = 0061FF06
Here is s[0] = B
我们可以发现数组s2的地址和指针中所存放的地址是不一样的,并且和本地变量i的地址是相近的。这说明数组定义的字符串和指针定义的字符串存放的位置是不一样的,通过数组定义的字符串也可以进行修改。
字符串输入输出
char string[8];
scanf("%s",string);
scanf("%7s",string);
printf("%s", string)
- scanf读入一个单词(到空格、TAB、或者回车结束)
- scanf是不安全,因为不知道要读入的内容的长度
- 在%和s之间的数字表示最多允许读入的字符的数量,这个数字应该比数组的大小小1
常见错误
char *string;
scanf("%s", string);
以为char*就是字符串类型,定义一个字符串类型的变量string就可以直接使用了。
- char*并不是字符串类型,它只是定义了一个指针,这个指针是本地变量,它将来会指向某一地址空间,如果没有进行初始化,我们知道本地变量没有初始化系统会自动赋值,也就意味着它可能会指向一些有风险的地方。所以养成好习惯记得初始化。
空字符串
这是一个空的字符串,buffer[0]=’\0’
这个数组的长度只有1,不能存放任何东西
字符串数组
如果我们要存放多个字符串怎么办呢?
char a[][n];//n表示每个字符串的大小
char* a[];//每个数组单元表示一个指针指向一个字符串
我们可以写个小栗子,输入月份,输出对应的英语
#include<stdio.h>
#include<string.h>
int main(){
printf("请输入月份:");
int month;
scanf("%d", &month);
switch (month)
{
case 1:
printf("January\n");
break;
case 2:
printf("February\n");
break;
case 3:
printf("March\n");
break;
case 4:
printf("April\n");
break;
case 5:
printf("May\n");
break;
case 6:
printf("June\n");
break;
case 7:
printf("July\n");
break;
case 8:
printf("August\n");
break;
case 9:
printf("September\n");
break;
case 10:
printf("October\n");
break;
case 11:
printf("November\n");
break;
case 12:
printf("December\n");
break;
}
return 0;
}
我们尝试用字符串数组来实现
#include<stdio.h>
#include<string.h>
int main(){
printf("请输入月份:");
int month;
scanf("%d", &month);
char *a[12] = {"January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"};
printf("%s\n", a[month-1]);
return 0;
}
注意:
我们用char *a[]来定义一个字符串数组时,每个数组单元a[n]都是一个指针,也就是说a[n]里面存放的都是地址。在输出位置printf("%s\n",…)这个位置,我们要明白与类型说明符%s相匹配的后续参数中的值应该是一个地址,从那个地址开始,按照存储的数值转换成ASCII码输出字符,直到遇到一个字节中存储‘\0’。在一开始我犯了个错误我输出写成了 printf("%s\n", *a[month-1]),我想a[month-1]不是一个指针吗,要输出的指针的内容应该是加上‘ * ’ ,其实就是没有弄清%s后面要匹配的就是一个地址。如果写成printf("%s\n", *a[month-1])相当于输出a[month-1][0],就是char型的了。