首页 > 代码库 > 第五次课大纲

第五次课大纲

一、回顾6.3节字符数组中字符串的概念:

  1、字符串的定义和初始化

  在C语言中,没有字符串这个数据类型,字符串是以字符数组的形式存在的,并且字符串的末尾会有结束符‘\0’标志字符串的结束。所以定义以及初始化字符串时,可以使用下列方式:

char str[]={"hello world"};
char str[]="hello world";

  问题:按照上图方式定义字符数组str,那么str的长度为?

  答案是12,注意不要忽略末尾的结束符,验证方法:sizeof()输出长度

  2、字符串的输入与输出

  字符数组的输入输出有两种方式:一种是使用%c格式符输入或输出一个字符,另一种是使用%s格式符一次输入与输出整个字符串。

  问题:回忆并使用两种方式编程输出上图中str字符数组。

  答案:答案不唯一,只要输出字符串即可,下面代码只是其中的三种方法,结果会输出三次hello world。

技术分享
 1 #include <stdio.h>
 2 
 3 int main(int argc, char const *argv[]){
 4     int i;
 5     char a[]="hello world";
 6     printf("sizeof a=%lu\n",sizeof(a));
 7     
 8     i=0; 
 9     while(a[i]!=\0){ 
10         printf("%c",a[i]);
11         i++;
12     } 
13         printf("\n");
14     for(i=0;i<sizeof(a);i++){ 
15         printf("%c",a[i]);
16     } 
17     
18     printf("\na[]=%s\n",a);
19     
20     return 0;
21 }
View Code

二、字符串和指针

  1、字符串的指针形式定义

  通过数组和指针的学习,我们知道数组可通过指针进行访问(读写),且指针和数组具有天然的联系,可以以指针的形式去访问或者遍历一个数组,也可以以数组的形式去访问指针所代表的那一大片连续的地址空间。数组和指针有着这种联系,字符串在内存里的表达形式是数组,那么定义或访问字符串时可以使用指针也可以使用数组。因此字符串的定义可以使用下述两种方式:

 char *str = "hello";            //指针形式
 char str[] = "hello";           //数组形式 

  上述两种方式均可使用格式符%s和格式符%c输出字符串str的内容。

  问题:使用指针形式定义字符串,且使用%s和%c输出字符串str。

  答案:答案有多种形式,下面的代码可供参考,编译运行后输出两次hello wolrd。另外思考:在下述代码中若使用%c输出,再使用%s输出,也就是将printf ("%s\n",str);  放在第10行,结果是否不变?如果变化为什么?(这个思考题其实属于指针和数组内容)

技术分享
 1 #include <stdio.h>
 2 
 3 int main(void){
 4     char *str="hello world";
 5    
 6     printf ("%s\n",str);  
 7    
 8     while ((*str)!=\0)
 9         printf ("%c",*str++);
10         
11     return 0;
12 }
View Code

  若将printf ("%s\n",str)放在第10行再次编译时,只能输出一次hello,world。为什么哪?因为在使用格式符%c输出字符串时,str指针执行了++运算,那么当输出字符串后,str指向了最后一个字符”\0“,可通过%p输出str的值验证,具体代码如下:

技术分享
 1 #include <stdio.h>
 2 
 3 int main(void){
 4     char *str="hello world";
 5    
 6     printf ("%p\n",str);  
 7    
 8     while ((*str)!=\0)
 9         printf ("%c",*str++);
10 
11     printf ("\n%p\n",str);         
12     printf ("%s\n",str);     
13     return 0;
14 }
View Code

  2、数组形式和指针形式定义字符串的区别

  既然可以使用指针形式定义字符串,并且和数组形式定义字符串形式一样可使用格式符%s和%c输出字符串,那么两种定义方式是完全等价的吗?并不是,两种形式的不同体现在给字符串赋值时,如下述代码:

技术分享
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

  上述代码使用指针形式定义字符串,编译运行时会出现错误,但是当将*s改为s[]时,也就是使用数组形式定义字符串,编译运行没有错误,正常输出结果如下:

here!s[0]=B

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

  所以指针形式定义的字符串是不可修改的,若定义一个可修改的字符串则需要使用数组的形式定义字符串。那么为什么哪?为什么字符指针的形式不可修改,而数组的形式可修改。

  3、指针形式定义的字符串为什么不可修改

  为什么指针形式定义字符串,字符串不可修改,我们通过下面的这段代码来分析原因:

技术分享
 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、字符串s2,给s和s2同样的初值,输出变量i、s和s2的地址:编译可能还是会有警告,先不管警告,运行后发现结果如下:

&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是一个指针,初始化为指向一个字符串常量,由于这个常量在只读不可写的位置,所以实际上s是const char *s,但是由于历史的原因,编译器接收不带const的写法,或者是省略const的写法,但是试图对这个字符串常量做写入会导致严重的后果。

  4、数组形式定义的字符串为什么可修改

  数组形式定义的字符串为什么可修改?在上述代码的基础上再添加数组形式定义字符串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)如果需要动态分配空间,那就只能使用指针了。

  所以,选择指针和数组的基本原则:如果要构造一个字符串,使用数组形式定义字符串,如果要处理一个字符串,使用指针形式定义字符串

三、字符指针作为函数参数

  字符串存在形式是字符数组,而在上一节说过数组作为函数参数时,传递到函数内部的就是指针,那么练习分别使用字符指针和字符数组的形式传递两个字符串from和to,并且将字符串to的内容复制为字符串from的内容,在赋值的时候可选用指针(*运算符)和数组形式([]运算符)完成。详见课本P259,8.4.2 字符指针作函数参数。

第五次课大纲