C语言(九)字符串

这篇博客详细介绍了C语言中的字符串概念,包括字符数组和字符串的区别,字符串的定义和表示,以及字符串变量的特性。强调了字符串以字符数组形式存在,以''作为结束标志,并提到了字符串常量的自动连接。博客还讨论了字符串输入输出的安全问题,如scanf()的潜在风险,并介绍了如何正确处理字符串拷贝。最后,提到了字符串数组和程序参数的处理,特别是main()函数中argv[]数组的用法。

10.1.1 字符串

char word[]={'H','e','l','l','o','!'};

得到的字符数组应该是这样子的

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

字符串常量
"Hello"双引号括起来的叫做字符串的字面量/常量,我们在scanf和printf里已经见过很多次了。"Hello"会被编译器变成一个(长度为6的)字符数组放在某处,结尾自动添上个0。
两个相邻的字符串常量会被自动连接起来。
比如printf里出现连续两个" " " ",会接起来输出。

printf("123456789"
"10111213");

另一种连接方法:

printf("123456789\
10111213");

会输出12345678910111213,回车可以用反斜杠的方式搞定。不过这种方法连换行开头的Tab也会输出,要小心使用。
人的眼睛横向是有极限的hh。所以程序员喜欢把显示器转过来
C语言的字符串是以字符数组的形态存在的,因为是个数组,所以不能用运算符对字符串做运算。但可以通过数组的方式遍历字符串。(后面会有很多应用)
字符串唯一特殊的地方是字符串字面量可以用来初始化字符数组。

10.1.2 字符串变量

char *s="Hello";
s[0]='B';//尝试把H替换为B,然后输出s[0]

编译无问题,但是运行时没法输出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);

比较得到的地址结果我们可以得出的结论:

1.i的地址相对很大,s和s1的地址相对很小。
2.s和s1的指向了同一个地址。

其实s和s1指向的地址是程序的代码端,他是只读的
因此,实际上s是const char*s;由于历史的原因,编译器接受不带const的写法(所以编译通过了)。但试图对s所指的字符串做写入会导致严重的后果。
如果需要修改字符串,应该用数组:

char s[]="Hello!";

尝试输出s的地址会发现,s的地址和上例中的i的地址一样较大(也就是说在本地变量那里),而且s可以修改。
需要使用字符串的时候,我们要把它写成指针的形式还是数组的形式呢?
数组:

  1. 字符串有确定的地址;
  2. 作为本地变量,空间会自动被回收。

指针:这个字符串不知道在哪;所以通常用来

  1. 只读的,不可写的字符串
  2. 处理函数的参数(前面知道如果数组作为指针,函数的参数实际上和指针是一样的);
  3. 动态分配空间malloc。

因此,如果要构造一个字符串:数组
如果要处理一个字符串:指针。

因此,字符串可以表示为char*的形式,而char*不一定是字符串。
他可能是指向字符的指针,可能指向的是字符的数组。只有当char*所指的字符数组有结尾的0,才能说他所指的是字符串。

10.1.3 字符串输入输出

所以字符串的地位如此。尽管相较之前的语言,C语言有很多处理字符串的方法,但比起现在的新语言,C语言对字符串的处理还是不足。

char *t="title";
char *s;
s=t;

只是让指针s指向指针t所指的字符串,并没有产生新的字符串。
至于如何真的复制一个字符串给s,等到以后字符串函数会学到的。
字符串的输入输出:%s

char string[8];
scanf("%s",string);
printf("%s",string);

输入不再是简单的整数、一个字符,这时候,我们就要考虑下scanf的停止条件了。
scanf()只能读入一个单词,因为碰到空格、tab键或回车就会终止。空格、回车是分隔符,是用来分隔两个单词的。
如果用这种方法读入两个单词可以连续两次scanf:

scanf("%s",str1);
scanf("%s",str2);
printf("%s%s",str1,str2);

空格不会被读入,输出是没有中间的分隔符(空格)的。
scanf()是不安全的,因为不知道读入的长度。

char str1[8],str2[8];
scanf("%s",str1);
scanf("%s",str2);

输入12345678 12345678,结果第一个字符串为空,第二个字符串却是12345678读了八个字符。(运气好没有崩溃)
为什么?涉及到在内存中是怎么排列的。
安全的方法:scanf("%7s%7s",str,str1);意思是:最多只能读七个字符。多出的部分会分配给下一个字符串。
这样输入12345678,8就会分配给下一个字符串。
加入的这个数字,最多应该比定义字符串的个数小1( 比如上例最多为7)
C语言中常见的错误:误以为char*就是定义了字符串类型。其实是一个需要初始化的指针。不初始化可能会出错。(可能会出现:程序在一个地方没问题,到了其他地方就出错了)

char zero[100]="";

空的字符串,zero[0]=’\0’,不过仍然是有效的字符串。
但是如果写成:char zero[]="";长度就是1了。

10.1.4 字符串数组,以及程序参数

如果我们想写一个数组来表示很多个字符串?
char **a:a是个指针,指向另一个指针,另一个指针指向一个字符串
char a[][]:a是个二维数组的变量,第二维(后面的括号)一定要有确定的大小,否则编译不能通过。

char a[][10]={
	"hello",
	"world"
};//每个字符串长度不要超过n,即10

还有一种写法: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]);
}

然后./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是怎么做的。(蒙……)等做快捷方式的时候会更理解。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

灰海宽松

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值