![](https://img-blog.csdnimg.cn/img_convert/7a62d1d24f31620be3b2b07134192147.png)
10.1.1 字符串
char word[]={'h','e','l','l','o','!'};//不是字符串
char word[]={'h','e','l','l','o','!','\0'};//是C语言的字符串 又是数组
\0:其中就有\0的存在,其意义就是0,可以在这个位置直接放单独的一个0,不加单引号和反斜杠。
这个0就让这word这个变量成为字符串,本质上仍然是一个字符数组,具有了双重功能。
字符串(实际上是数组)
![](https://img-blog.csdnimg.cn/img_convert/9028536828168a6e5ba0a3cca9021c3f.png)
单引号的反斜杠0:【标志字符串的结束】直接写0可能是int或者更大的类型
单引号里的0:ASCII码里的0,十进制为48
定义字符串(本质是数组)
有以下几种定义字符串的方法:
![](https://img-blog.csdnimg.cn/img_convert/9778e7b27bac9a52be4e0ca31e400556.png)
第一种:一个名为str的指针指向了一个字符数组Hello这个内容在哪,稍后再说
第二种:现在有一个字符数组名为word,内容为Hello
第三种:有名为line的字符数组,大小为10,其中有前6个(0~5)为Hello+0
字符串常量
像“Hello”这样用双引号括起来的东西叫字符串常量(字面量)
“Hello”会被编译器变成一个字符数组放在某处(长度为6,结尾有0)【眼睛所看到的长度+1】
在各种语句中都可以见到,scanf、printf等
两个相邻的字符常量会被自动连接在一起,也可以用斜杠链接
![](https://img-blog.csdnimg.cn/img_convert/1f7b60f61f63b878efa3645b8ea9ed69.png)
![](https://img-blog.csdnimg.cn/img_convert/1c192b625e46cccd748017fe0b9e264c.png)
小结
![](https://img-blog.csdnimg.cn/img_convert/d700e63a0c83ea631324fcc2539a4edc.png)
10.1.2 字符串常量与变量
字符串 指针:尝试改变其值
char* s="Hello World!";
这表示s是一个字符串(口头上),其值是HelloWorld。
比如我将第一个字母H改为B并将其输出:
char* s="Hello World!";
s[0]='B';
printf("s[0] = %c\n",s[0]);
//编译,没有问题,而运行会报错,而且printf没有执行,说明是在s[0]这一句这里出错了。【代码段 只读】
char* s ="Hello World!";
char* s2="Hello World!";
printf("s = %p\n",s);
printf("s2 = %p\n",s2);
printf("s[0] = %c\n",s[0]);
![](https://img-blog.csdnimg.cn/img_convert/a0d775f97d6e98f43e3fc36dbdba53ad.png)
![](https://img-blog.csdnimg.cn/img_convert/ffb8e29fafe203b7ffb666747fbe8cd4.jpeg)
//得到的结果竟然是相同的,而这两个的地址值都远小于正常变量的地址(定义一个int i,其地址为0xbff1fd6c),说明这两个字符串存放在很远的地方。
那么实际上,这样定义的字符串是将HelloWorld放到了程序的代码段,是 只读 的(也就是字符串常量),在编译时候就已经定义好了,然后让指针指向它。
由于这个常量位置特殊,实际上s是const char* s,但编译器也接受现在的写法,试图对一个字符串常量做变更和写入会导致出错。
赋值一样的字符串?
![](https://img-blog.csdnimg.cn/img_convert/a46fdf72fbfb632a71a5d6ff556f09a1.jpeg)
//学习了字符串函数后 可以重新开辟一个新的字符串并且使它等于title
如何修改字符串:用数组定义
//所以如果想要修改字符串,应该用数组定义
char s[]="Hello World!";
这样定义的内部过程是这样:在内存中给数组开辟空间之后,把只读部分的那个字符串拷贝到数组内。于是就可以修改其值
char s[] = "Hello World";
s[0] = 'B';
printf(s[0] = %c\n, s[0]);
这样就完成了之前做不到的事情。
二者的区别
我们在定义字符串时,到底用指针还是数组呢?
char *str = "Hello";
char s[] = "Hello";
1. 指针:(不知道这个字符串在哪)只想定义一个常量;用来表达函数参数,因为数组用来做函数参数时候和指针是一样的;分配动态空间malloc;【不可写】
2. 数组:字符串就在数组所在地址,由于是本地变量所以空间在使用后会被回收【是本地变量】
因此想要构造一个字符串,用数组;想要处理一个字符串,用指针。
思考:char*
字符串可以表达为char *
char* 不一定是字符串
char * 表达这里有一个指针,指针指向一个字节or一串连续的字节,但不一定是字符串
可能指向单个,可能指向数组
只有char* 所指的字符数组有结尾的0 ——char* 所指的是个字符串
10.1.3字符串的输入输出
对于字符、整型数据等我们可以用scanf、printf等通过格式化字符进行输入输出,那么对于字符串其实也有特殊字符,%s
输入scanf(什么时候为止)
scanf不接收空格、回车、Tab等,会将其视为输入终止。
:scanf读入单词到空格、回车、制表符\t为止。
//因此当用户输入 Hello World(两个单词中间有空格),输出结果是Hello
char string[8];
scanf("%s",string);//字符串不需要用& 直接指向第一个地址string[0]
printf("%s##\n",string);
~~~~~~~~~~~~~~~~~~~~~~
char word1[8];
char word2[8];
scanf("%s",word1);
scanf("%s",word2);
printf("%s##%s##\n",word1,word2);
容易溢出的scanf
scanf是不安全的读入:
因为不知道要读入的内容长度。比如在上方的代码运行时输入:
12345678(回车)12345678(回车)
此时根据版本的不同,可能会报错。根本原因是字符串虽然开了长度8的空间,但最后一位被0占据,因此最多只能接收7个字符。
想要安全输入
需要在scanf("%s",word1);的%s前面加上一位数字且这个数字要比数组长度小1
char word1[8];
scanf("%7s",word1)
//在这种情况下,如果输入超过7个字符,剩下的会被下一个数组接收。此时不是通过空格等分割。
常见错误
把char* string当作字符串。
//实际上这只是定义了一个指针变量,要指向的东西还不知道在哪。如果没有进行初始化,不一定会报错,这次运行也许通过了,下次就不一定了。。
使用指针定义字符串的时候,需要初始化,使它指向一个明确的地址
空字符串
char string[100]=""; //空字符串(有效的) char string[0]=‘\0’
char string[]=""; //这个数组实际上是没用的(无效的),0占据了第一位,因此它的长度是1,这里面放不下任何其他字符。
10.1.4 字符串数组
一个数组内包含多个字符串
![](https://img-blog.csdnimg.cn/img_convert/7d4f4426bf1ebf3fb6222b91f2ab7ee0.png)
char **a
a是一个指针,指向另一个指针,另一个指针指向一个字符/字符串。
char [] []
二维数组 ,然而二维数组的第二维必须要有明确的大小
如a[][10],相当于a[ ]是一个数组,数组a[ ]的每一个单元都是char[10],如a[0] = char [10]。然而每个数组内的字符长度不能超过10。
若超过10,编译器报错
char *a[] , 此时的a[0] 是个指针 相当于 char*(外面某处)
![](https://img-blog.csdnimg.cn/img_convert/56b4a65119e536e6e1a918c7dc42e4ce.png)
ex曾经的switch case 改进:
![](https://img-blog.csdnimg.cn/img_convert/fd74d8989ed5dc6ebdbef22d48521aa5.png)
//我们可以用数组下标代替月份的数字,在其中包含具体的字符串
#include <stdio.h>
int main()
{
int m;
char *a[] = {
"January",
"Febrary",
"March",
"Aprial",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
};
scanf("%d", &m);
printf("%s\n", a[m - 1]);
return 0;
}
另一个应用:main的参数(底层)
之前都是直接int mian( )
后来建议 int main(void) //在C语言中,对主函数的参数和返回类型检查并不严格,当不需要命令行参数时,就可以将参数列表设置为void。
现在:
main函数称为主函数,是C语言约定的程序执行入口
其标准的定义格式为: int main(int argc, char *argv[]);
其中,参数的含义为
argc: 执行程序时输入的参数个数,包括可执行程序文件名。
argv:前argc个元素(argv[0]到argv[argc-1]),分别为执行程序时的各项参数值,以字符串方式表达。第argc+1个参数(argv[argc])值为NULL。
返回值为int型,会将返回的值回传给主调进程。
在C语言中,对主函数的参数和返回类型检查并不严格,当不需要命令行参数时,就可以将参数列表设置为void。类似的,如果不需要返回信息给主调进程,返回值也可以设定为void类型。
main函数的参数,也可以看到字符串数组
int main(int argc,char const *argv[])
这个数组多大呢?我们需要另一个参数来告诉我们
#include<stdio.h>
int main(int argc,char const *argv[])
{
int i;
for(i=0;i<argv;i++)
{
printf("%d:%s\n",i,argv[i]);
}
return 0;
}
//程序会将你在命令行输入的东西保存到这个数组中,且argv[0]保存的是命令符,起一个快捷方式的作用。
![](https://img-blog.csdnimg.cn/img_convert/ec85c8198f25270e2efe1b1675e96048.png)