10.1.1 字符串
char word[]={'H','e','l','l','o','!'};
AI 代码解读
得到的字符数组应该是这样子的
word[0] | H |
word[1] | e |
word[2] | l |
word[3] | l |
word[4] | o |
word[5] | ! |
不过这只是字符数组而不是字符串。因为他不能用字符串的方式做运算。
定义一个C语言的字符串,结尾要加上word[6]='\0'。
对C语言来说,字符串就是以0(整数0)结尾的一串字符数组。
0和'\0'是一样的,但是和'0'不同。0标志着字符串的结束,但他自己不是字符串的一部分,计算字符串长度的时候不包含这个0。
字符串以数组的形式存在,以数组或指针的形式访问(更多以指针的形式)
PS:string.h里有很多处理字符串的函数。
char *str="Hello";//一个指针指向了一个字符数组
char word[]="Hello";//字符数组,结尾0
char line[10]="Hello";//字符数组,结尾0
AI 代码解读
字符串常量
"Hello"双引号括起来的叫做字符串的字面量/常量,我们在scanf和printf里已经见过很多次了。"Hello"会被编译器变成一个(长度为6的)字符数组放在某处,结尾自动添上个0。
两个相邻的字符串常量会被自动连接起来。
比如printf
里出现连续两个" "
" "
,会接起来输出。
printf("123456789"
"10111213");
AI 代码解读
另一种连接方法:
printf("123456789\
10111213");
AI 代码解读
会输出12345678910111213,回车可以用反斜杠的方式搞定。不过这种方法连换行开头的Tab也会输出,要小心使用。
人的眼睛横向是有极限的hh。所以程序员喜欢把显示器转过来
C语言的字符串是以字符数组的形态存在的,因为是个数组,所以不能用运算符对字符串做运算。但可以通过数组的方式遍历字符串。(后面会有很多应用)
字符串唯一特殊的地方是字符串字面量可以用来初始化字符数组。
10.1.2 字符串变量
char *s="Hello";
s[0]='B';//尝试把H替换为B,然后输出s[0]
AI 代码解读
编译无问题,但是运行时没法输出s[0],出错了。
再做另一个尝试
int i=0;
char *s="Hello";
char*s1="Hello";//两个一样的字符串变量
printf("i的地址是%p\n",&i);
printf("s的地址是%p\n",s);
printf("s1的地址是%p\n",s1);
AI 代码解读
比较得到的地址结果我们可以得出的结论:
1.i的地址相对很大,s和s1的地址相对很小。
2.s和s1的指向了同一个地址。
其实s和s1指向的地址是程序的代码端,他是只读的。
因此,实际上s是const char*s;
由于历史的原因,编译器接受不带const
的写法(所以编译通过了)。但试图对s所指的字符串做写入会导致严重的后果。
如果需要修改字符串,应该用数组:
char s[]="Hello!";
AI 代码解读
尝试输出s的地址会发现,s的地址和上例中的i的地址一样较大(也就是说在本地变量那里),而且s可以修改。
需要使用字符串的时候,我们要把它写成指针的形式还是数组的形式呢?
数组:
- 字符串有确定的地址;
- 作为本地变量,空间会自动被回收。
指针:这个字符串不知道在哪;所以通常用来
- 只读的,不可写的字符串
- 处理函数的参数(前面知道如果数组作为指针,函数的参数实际上和指针是一样的);
- 动态分配空间malloc。
因此,如果要构造一个字符串:数组
如果要处理一个字符串:指针。
因此,字符串可以表示为char*
的形式,而char*
不一定是字符串。
他可能是指向字符的指针,可能指向的是字符的数组。只有当char*
所指的字符数组有结尾的0,才能说他所指的是字符串。
10.1.3 字符串输入输出
所以字符串的地位如此。尽管相较之前的语言,C语言有很多处理字符串的方法,但比起现在的新语言,C语言对字符串的处理还是不足。
char *t="title";
char *s;
s=t;
AI 代码解读
只是让指针s指向指针t所指的字符串,并没有产生新的字符串。
至于如何真的复制一个字符串给s,等到以后字符串函数会学到的。
字符串的输入输出:%s
char string[8];
scanf("%s",string);
printf("%s",string);
AI 代码解读
输入不再是简单的整数、一个字符,这时候,我们就要考虑下scanf的停止条件了。scanf()
只能读入一个单词,因为碰到空格、tab键或回车就会终止。空格、回车是分隔符,是用来分隔两个单词的。
如果用这种方法读入两个单词可以连续两次scanf:
scanf("%s",str1);
scanf("%s",str2);
printf("%s%s",str1,str2);
AI 代码解读
空格不会被读入,输出是没有中间的分隔符(空格)的。scanf()
是不安全的,因为不知道读入的长度。
char str1[8],str2[8];
scanf("%s",str1);
scanf("%s",str2);
AI 代码解读
输入12345678 12345678,结果第一个字符串为空,第二个字符串却是12345678读了八个字符。(运气好没有崩溃)
为什么?涉及到在内存中是怎么排列的。
安全的方法:scanf("%7s%7s",str,str1);
意思是:最多只能读七个字符。多出的部分会分配给下一个字符串。
这样输入12345678,8就会分配给下一个字符串。
加入的这个数字,最多应该比定义字符串的个数小1( 比如上例最多为7)
C语言中常见的错误:误以为char*
就是定义了字符串类型。其实是一个需要初始化的指针。不初始化可能会出错。(可能会出现:程序在一个地方没问题,到了其他地方就出错了)
char zero[100]="";
AI 代码解读
空的字符串,zero[0]='\0',不过仍然是有效的字符串。
但是如果写成:char zero[]="";
长度就是1了。
10.1.4 字符串数组,以及程序参数
如果我们想写一个数组来表示很多个字符串?char **a
:a是个指针,指向另一个指针,另一个指针指向一个字符串char a[][]
:a是个二维数组的变量,第二维(后面的括号)一定要有确定的大小,否则编译不能通过。
char a[][10]={
"hello",
"world"
};//每个字符串长度不要超过n,即10
AI 代码解读
还有一种写法:char *a[]
,a[0]相当于char*,像矩阵的排列方法想的话,a[i]就是指向每一行的字符串的指针。
这和二维数组不一样,二维数组a[0]就是第一行的字符串。
填坑:以前做的一道题:输入数字月份,输出对应的月份英文单词。现在可以用数组做。
现在回归到int main()主函数,之前说过括号里面什么都不用写,要写也就写个void
其实里面是是int main(int argc,char const*argv[])
argc告诉我们,后面的数组有多少个字符串。
然后我们试着输出后面字符串的内容
for(i=0;i<argc;i++){
printf("%d:%s\n",i,argv[i]);
}
AI 代码解读
然后./a.out
运行,只输出了0:./a.out
第二次输入./a.out 123
,输出了
`0:./a.out
1:123`
然后随便输入,./a.out 123 asd asd asd asd
这些字符串都会被一个个记录下来。
它从第2个字符串开始记录所有你输入的字符串,而第一个参数,即argv[0],则是输入的这个文件的名字(./a.out
)即可执行程序的名字。
如果将a.out 称之为my,ln -s a.out my
,然后我们看my,ls -l my
发现my是个指向a.out 的链接。如果执行my ./my 123
输出的字符串argv[0]也是./my
而不是./a.out
了。
关于到底是怎样运行程序的,建议搜索busybox,看看别的box是怎么做的。(蒙……)等做快捷方式的时候会更理解。