字符串的数组形式和指针形式声明及其区别 整理版(摘自《C Primer Plus 中文版第六版》第11章及黑马程序员2018C语言提高深入浅出ch1-5 )
本节内容需要掌握内存分区的概念,可以参见:C程序的内存分区(节选自黑马训练营day1)
下面来看例程:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define sTring "I like C!" //定义一个宏字符串
1 void locationOfString() //不同声明形式下,字符串的地址 2 { 3 char arrString[]="I am a freshman!"; //数组形式声明字符串 4 char *p=arrString; //声明指针指向上面字符串 5 char*q="I am a freshman!"; 6 printf("字符串本身的地址是: %p 字符串的内容是: %s","I am a freshman!","I am a freshman!\n"); 7 printf("字符串数组的地址是: %p 字符串数组的内容是: %s\n",arrString,arrString); 8 printf("指向字符串数组的指针的地址是:%p 指向字符串数组的指针的内容是:%s\n",p,p); 9 printf("指向字符串的指针的指向地址是:%p 指向字符串的指针的内容是: %s\n\n",q,q); 10 }
程序运行后,输出如下:
字符串本身的地址是: 00405064 字符串的内容是: I am a freshman!
字符串数组的地址是: 0060FEF7 字符串数组的内容是: I am a freshman!
指向字符串数组的指针的地址是:0060FEF7 指向字符串数组的指针的内容是:I am a freshman!
指向字符串的指针的指向地址是:00405064 指向字符串的指针的内容是: I am a freshman!
从上面程序运行的结果可以推断出:
1、当编程者定义一个字符串的时候,编译器将字符串存放在程序数据区(静态存储区)。
2、以数组形式声明的字符串,字符串本身在程序数据区(全局区)。程序运行后,编译器拷贝字符串,并将其放在栈区,形成副本。
字符串数组的地址就是字符串栈区副本的地址,该地址的内存随着函数运行结束被释放。
3、以指针形式声明的字符串,则在栈上没有副本,指针指向字符串本身在程序数据区的地址。
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #define sTring "I like C!" //定义一个宏字符串 5 6 void outputArrayString() 7 { 8 char arrString[]=sTring; 9 printf("数组形式输出字符串数组的第4个字符是: arrString[3]=%c \n",arrString[3]); 10 printf("指针形式输出字符串数组的第4个字符是:*(arrString+3)=%c \n\n",*(arrString+3)); 11 printf("字符串数组的地址是 : arrString=%p \n",arrString); 12 printf("字符串数组的首地址是:&arrString[0]=%p \n",&arrString[0]); 13 printf("字符串本身的首地址是: &sTring=%p \n",&sTring); 14 printf("字符串本身的地址是 : sTring=%p \n\n",sTring); 15 } 16 void outputPointString() 17 { 18 char *p=sTring; 19 printf("数组形式输出字符串数组的第5个字符是: p[4]=%c \n",p[4]); 20 printf("指针形式输出字符串数组的第5个字符是:*(p+4)=%c \n\n",*(p+4)); 21 }
通过outputArrayString和outputPointString例程的运行,我们可以看出:
1、只要将字符串与数组或者指针联系起来(以赋值的形式),我们就可以输出字符串中的字符。
2、无论是用数组还是用指针与字符串联系起来,在定位字符串的位置,以输出字符串的某个字符时,
用指针偏移或者数组偏移的形式都可以。这时,数组形式和指针形式的定位是通用的,并且可以互换。
1 #define sTring "I like C!" 2 void editArrayString() 3 { 4 char arrString[] = sTring; 5 printf("数组字符串=%s 宏字符串=%s\n", arrString, sTring); 6 arrString[0] = 'W'; 7 printf("数组字符串=%s 宏字符串=%s\n", arrString, sTring); 8 *(arrString) = 'I'; 9 printf("数组字符串=%s 宏字符串=%s\n", arrString, sTring); 10 //++arrString='h'; 当程序运行到这条语句时,提示错误。 11 printf("数组字符串=%s 宏字符串=%s\n\n", arrString, sTring); 12 }
通过上面例程可以看出:
1、声明一个数组,并将其与宏字符串关联后,字符串数组可以被修改。
2、被修改的仅仅是字符串数组,也就是宏字符串在栈上的副本,宏字符串本身并没有改变。
3、字符串的地址不能被修改,也就是说,字符串数组变量名不能做左值。
1 void editArrayString2() 2 { 3 char arrString[] = "I believe tomorrow to be a better day!"; 4 printf("程序数据区字符串地址=%p\t", &("I believe tomorrow to be a better day!")); 5 printf("程序数据区字符串=%s\n", *(&("I believe tomorrow to be a better day!"))); 6 printf("字符串数组地址 =%p \t 字符串数组 =%s\n", arrString, arrString); 7 arrString[0] = 'W'; 8 //*((char*)("I believe tomorrow to be a better day!")) = 'W'; 程序运行至此,系统报错! 9 printf("程序数据区字符串地址=%p\t", &("I believe tomorrow to be a better day!")); 10 printf("程序数据区字符串=%s\n", *(&("I believe tomorrow to be a better day!"))); 11 printf("字符串数组地址 =%p \t 字符串数组 =%s \n\n", arrString, arrString); 12 }
通过上面例程可以看出:
1、声明一个字符串数组后,编译器在内存的程序数据区写入一个字符串常量。
2、声明字符串数组的函数被调用后,编译器在栈区写入一个字符串常量副本作为字符串数组。
3、字符串数组可以被修改,但被修改的仅仅是字符串数组,也就是字符串在栈上的副本,
存放在程序数据区的字符串常量并没有改变。
4、字符串本身不可以被修改,被当作常量处理。
1 void editPonintString() 2 { 3 char *p="persevere in!"; 4 printf("指针声明字符串=%s \n",p); 5 //*(p+1)='E'; //程序运行至此,系统报错! 6 printf("指针声明字符串=%s \n",p); 7 p=p+2; 8 printf("指针声明字符串的第3个字符=%c \n",*(p)); 9 }
通过上面例程可以看出:
1、以指针形式声明一个字符串变量后,该字符串不能被修改。
2、指针的地址可以被修改,也就是说,指针变量名能做左值。