首页 > 技术文章 > 听翁恺老师mooc笔记(7)--字符串1

c-programing-language 2017-03-10 16:50 原文

C语言中字符串的定义

  如果定义一个字符数组word,并使用大括号对其初始化,如下图所示:

  但是这个不是C语言的字符串,只是字符数组,不是字符串,因为不能使用字符串的方式进行计算。那么C语言的字符串长什么样呢?如下图所示:

  那么上述两个定义有何区别呢?区别就是C语言字符数组初始化时的最后一个字符加入了一个特殊的东西,就是‘\0’,这个符号的意思就是整数0,这里去掉单引号和斜杠,只放0也是可以的。这个字符数组总共有7个元素,最后的这个0使得这个数组就是C语言的字符串,那么这个字符串就可以使用c语言字符串的运算。

  所以,对于C语言来说,字符串是指以0结尾的一串字符,结尾0和'\0'是一样的,某些情况下会强调使用'\0',因为整数0认为是int,一般占用4个字节,而'\0'一定是一个字节。但是不可以使用'0','0'是一个字符,这个字符是ascii码中的0,\0中的0是整数0,这两个是不一样的。

  字符串末尾的0有什么含义呢,0标志字符串的结束,表示字符串到此为止,接下来的就不属于该字符串的内容,但是0本身并不是字符串的一部分。再接下来对字符串进行操作的时候就可以看到这个0的作用,字符串需要一个标志来表示说字符串结束了,这样程序才能写得出来。既然0不是字符串的一部分,那么计算字符串长度的时候不包含这个0。

C语言中字符串的存在形式

  在C语言中字符串是以数组的形式存在的,而进行访问(读写)的时候可以是数组也可以是指针。在上一节课说数组和指针的关系时说过,指针和数组具有天然的联系,可以以指针的形式去访问或者遍历一个数组,也可以以数组的形式去访问指针所代表的那一大片连续的地址空间。数组和指针有着这种联系,字符串在内存里的表达形式是数组,那么访问字符串时可以使用指针也可以使用数组,指针居多一些。

C语言中字符串变量

  定义字符串变量有下列几种不同的写法,但是首先要理解的是字符串是数组,那么这些变量都是字符数组的变量,只不过有不同的表现形式:

1 char *Str = ”hello“;
2 char word[] = "hello";
3 char line[10] = "hello";
View Code

  char *str=“hello";表达的是一个名为str的指针,该指针指向的了一个字符数组,这个字符数组的内容是hello,char word[]="hello"很直白是说字符数组,内容是hello。char line[10]=”hello“定义了一个字符数组,字符数组的大小是10个字节,该数组初始化为hello,hello总共5个字符,这5个字符在line里占用6个字节的空间,包括最后有一个0的位置,当你在定义字符串的时候,编译器会默认在最后加一个结尾的0.

C语言中字符串常量

  在C语言中使用双引号括起来的东西是字符串常量,编译器看到它就会编译成一个字符数组,放在内存的某一片存储空间中,并且编译器还会分配一个0放在该字符数组的末尾。那么如果写了两个相邻的字符串常量,结果会被自动连接起来,比如printf("hello""world"),如果两个字符串中间没有任何东西,那么c语言编译器会将这两个字符串连接起来。所以我们说C语言中字符串是以字符数组的形态存在的,可以通过数组的方式访问遍历字符串,唯一特殊的地方是字符串可以使用双引号括起来的字符串字面量来初始化字符数组

字符串定义使用指针形式和数组形式的区别:

  如果定义char *s=”hello,world“,通常我们的理解是定义了一个字符串s,这个字符串的初值是”hello,world“,既然是字符串,那么访问(读或者写)这个字符串试一下,我们说过定义一个字符串,实际上就是定义了一个字符数组,那么就可以做s[0]=‘B’,将字符串的第一个字符换为B,然后试一下输出s[0]查看是否替换成功,编译下上述代码有一个警告:

1 #include <stdio.h>
2 
3 int main(void){
4     char *s={"hello,world"};
5     s[0]='B';
6     
7     printf("here!s[0]=%c\n",s[0]);
8 }
View Code

 

  先不管警告运行发现结果出错误,printf函数完全没输出,在printf函数之前程序就错误了,那么错误肯定出在前两行代码,那么试着注释掉任意一行看是否运行成功,第一行是定义应该不能注释,那么注释掉第二行s[0]='B',再次编辑运行,没有出现错误,结果正常输出字符串首字符号”h“。

   再对当前程序进行修改,新建一个整数变量i、字符串s2,给s和s2同样的初值,输出变量i、s和s2的地址:

 1 #include <stdio.h>
 2 
 3 int main(void){
 4     int i;
 5     char *s = "hello,world";
 6     //s[0]='B';
 7     char *s2 = "hello,world";
 8     printf("&i=%p\n",&i);    
 9     printf("&s=%p\n",&s);
10     printf("&s2=%p\n",&s2);    
11     
12     printf("s =%p\n",s);
13     printf("s2=%p\n",s2);
14     printf("s[0]=%c\n",s[0]);
15 } 
View Code

  编译可能还是会有警告,先不管警告,运行后发现结果如下:

&i=000000000062FE4C
&s=000000000062FE40
&s2=000000000062FE38
s =0000000000404000
s2=0000000000404000
s[0]=h

--------------------------------
Process exited after 0.01247 seconds with return value 0
请按任意键继续. . .

  从结果可以看出,作为变量i、s、s2地址是相邻的,且先定义地址大,后定义的变量小。而s和s2的结果是一样的,s和&s结果不一致,变量i、s、s2地址是一个很大的数,而s和s2的值比较小,主要是因为“hello,world”在程序的代码段,地址一般比较小且所在区域一般不允许修改,如果程序修改该区域,那么操作系统会有个保护机制,会让你的代码运行错误,说你在做坏事不让你写,如果该操作系统允许你写,那么这个操作系统不够安全。

  写好程序编译的时候,编译器会对编译时就已经有值(hello,world)的东西,将该值放在一个只读不能写的位置(也就是代码区),然后让你的指针指向这个值的位置,如果有两个指针的值都是这个值,那么就需要将两个指针都指向这个值的位置,也就因为允许多个指针指向这个位置,所以才不允许修改,如果允许修改s[0]为B,那么s2怎么办?

  实际上,s是一个指针,初始化为指向一个字符串常量,由于这个常量在只读不可写的位置,所以实际上s是const char *s,但是由于历史的原因,编译器接收不带const的写法,或者是省略const的写法,但是试图对这个字符串常量做写入会导致严重的后果。所以如果你想要定义一个可修改的字符串,那么就应该用字符数组来定义字符串,例如:char s[] = "hello,world"。

  指针形式定义字符串和数组形式定义字符串区别在什么地方呢,区别就是指针形式定义字符串时,编译后指针指向某个区域,而使用数组形式定义字符串,那么字符串就在当前位置,也就是字符串不会在地址比较小的代码区,而是就在定义当前变量的位置。为了验证这一点,在上述代码的基础上添加字符串s3:

 1 #include <stdio.h>
 2 
 3 int main(void){
 4     int i;
 5     char *s = "hello,world";
 6     char *s2 = "hello,world";
 7     char s3[] = "hello,world";
 8     printf("&i=%p\n",&i);    
 9     printf("&s=%p\n",&s);
10     printf("&s2=%p\n",&s2);    
11     printf("&s3=%p\n",&s3);    
12     printf("s3=%p\n",s3);    
13             
14     printf("s =%p\n",s);
15     printf("s2=%p\n",s2);
16     
17     s3[0]='B';
18     printf("s3[0]=%c\n",s3[0]);
19 } 
View Code

  结果如下,发现s3的地址和s3是同一个位置,和i、s、s2都在同一个位置:

&i=000000000062FE4C
&s=000000000062FE40
&s2=000000000062FE38
&s3=000000000062FE20
s3=000000000062FE20
s =0000000000404000
s2=0000000000404000
s3[0]=B

--------------------------------
Process exited after 0.03045 seconds with return value 0
请按任意键继续. . .

  那么当程序需要一个字符串的时候,那么我们是将字符串写成数组的形式还是指针的形式呢?如果是作为数组,那么表示这个字符串就在这里,作为本地变量空间自动被释放。而如果作为指针,那么这个字符串不知道在哪里,所以通常我们使用指针定义字符串完成下列事情:1)如果需要一个只读不写的字符串;2)如果该字符串需要作为函数参数时,在学指针和数组时我们知道,将数组作为函数参数时,传递到函数内的就是指针;3)如果需要动态分配空间,那就只能使用指针了。综上所述:选择指针和数组的基本原则:如果要构造一个字符串,使用数组形式定义字符串,如果要处理一个字符串,使用指针形式定义字符串

  另外有些书上说“char*是字符串”,这种观点是不对的,字符串可以表达为char*的形式,但char*不一定是字符串,char*是指向字符的指针,可能指向的是字符的数组(就像int*一样),只有当字符所指的字符数组有结尾的/0,才能说它所指的是字符串。

推荐阅读