10.1.1 Strings
char word[]={'h','e','l','l','o','!'};//不是字符串
char word[]={'h','e','l','l','o','!','\0'};//是C语言的字符串 又是数组
\0: There is \0 in it, and its meaning is 0. You can directly put a single 0 in this position without adding single quotes and backslashes.
This 0 makes the word variable a string, which is essentially still a character array and has dual functions .
String (actually an array)
Backslash 0 in single quotes: [mark the end of the string] directly writing 0 may be an int or larger type
0 in single quotes: 0 in ASCII, 48 in decimal
Define string (essentially an array)
There are several ways to define strings:
The first type: a pointer named str points to a character array Hello, where is the content, and I will talk about it later
The second type: Now there is a character array named word, the content is Hello
The third type: there is a character array named line, the size is 10 , and the first 6 (0~5) are Hello+0
string constant
Things enclosed in double quotes like "Hello" are called string constants (literals)
"Hello" will be turned into a character array by the compiler and placed somewhere (length 6, with 0 at the end ) [Length as seen by eyes + 1]
Can be seen in various statements, scanf, printf, etc.
Two adjacent character constants will be automatically linked together , and can also be linked with a slash
summary
10.1.2 String constants and variables
String pointer: try to change its value
char* s="Hello World!";
This means that s is a string (verbally) whose value is HelloWorld.
For example, I change the first letter H to B and output it:
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]);
//得到的结果竟然是相同的,而这两个的地址值都远小于正常变量的地址(定义一个int i,其地址为0xbff1fd6c),说明这两个字符串存放在很远的地方。
那么实际上,这样定义的字符串是将HelloWorld放到了程序的代码段,是 只读 的(也就是字符串常量),在编译时候就已经定义好了,然后让指针指向它。
由于这个常量位置特殊,实际上s是const char* s,但编译器也接受现在的写法,试图对一个字符串常量做变更和写入会导致出错。
赋值一样的字符串?
//学习了字符串函数后 可以重新开辟一个新的字符串并且使它等于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 字符串数组
一个数组内包含多个字符串
char **a
a是一个指针,指向另一个指针,另一个指针指向一个字符/字符串。
char [] []
二维数组 ,然而二维数组的第二维必须要有明确的大小
如a[][10],相当于a[ ]是一个数组,数组a[ ]的每一个单元都是char[10],如a[0] = char [10]。然而每个数组内的字符长度不能超过10。
若超过10,编译器报错
char *a[] , 此时的a[0] 是个指针 相当于 char*(外面某处)
ex曾经的switch case 改进:
//我们可以用数组下标代替月份的数字,在其中包含具体的字符串
#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]保存的是命令符,起一个快捷方式的作用。