前言
以例子为导向来分析字符指针的中存的是什么,以及常量字符串在内存中是如何存储的。
一、指针概念简述
想要详细地了解指针的概念可以戳此链接(详细的万字讲解):http://t.csdnimg.cn/QL5kk
本文就简单地回顾指针的一些基础知识:
1、指针本身就是地址,内存单元的编号为地址,而地址又称为指针;指针在口语上认为是指针变量、是用来存放地址的变量,每个地址都唯一地标识内存中一块内存空间;
2、指针的大小是固定的4byte\8byte (32位平台或者64位平台)
3、指针是有类型的,其类型不会决定其在内存中占据的空间,而是决定了当解引用此指针变量时有多少字节的访问内存的权限;以及指针变量+、-整数的步长
4、指针的运算
二、字符指针
顾名思义,字符指针就是指针变量指向的对象是字符类型,即指针变量的类型为 char* ;
单个字符的指针创建,代码如下:
#include<stdio.h>
int main()
{
char ch = 'a';
char* p = &ch;//此*代表着变量p 为指针变量
*p = 'b';
return 0;
}
字符指针可以存放一个字符变量的地址,那么字符串的地址该如何存放呢?
先来看一下整型的例子:
int a = 10 ;
int b = a + 2;
"a+2" 是一个表达式的话,那么此表达式就一定有自己的值,然后再将此值赋给b ;
注:表达式有两个属性:值属性 和 类型属性
即 "a+2" 的结果为12,那么12就为其值属性;2 为int 类型,变量a 为int 类型,那么 "a+2"此表达式 的类型为int 类型。
eg.char * p = "abcdefg" ;
所以若将字符串"abcdefg"当作表达式,那么将它赋给指针变量p 的是什么呢?在x86环境下,指针p 中只能放大小为4byte 的地址;显然整个字符串在内存中是连续存放的,即不止占用一个内存单元空间,所以整个字符串会有多个地址;那么指针p是存放不下整个字符串的地址‘
字符串"abcdefg"赋给指针p是其首字符 'a '的地址。
而函数printf 的占位符%s 会通过字符串的首字符地址而找到整个字符串;
我们来看一下打印字符串,代码如下:
#include<stdio.h>
int main()
{
char ch[10] = "abcdefg";
char* p = "abcdefg";
char* p1 = ch; //将数组的首元素地址存放起来
printf("%s\n", "abcdefg");
printf("%s\n", ch);
printf("%s\n", p);
printf("%s\n", p1);
printf("字符串地址=%p\n", "abcdefg");
printf("指针p中存放的地址=%p\n", p);
printf("指针p1中存放的地址=%p\n", p1);
return 0;
}
代码运行结果如下:
分析:字符串的创建是用字符数组创建的,我们打印字符串一般采用 printf("%s\n", "abcdefg"); 这种方式,或者将字符串存在数组中,然后对此数组进行打印,即printf("%s\n",ch);
这时候你可能就会有疑问了,数组名可以理解为整体的数组也可以理解为其首元素的地址,于是我们就不清楚是printf函数的占位符%s 只要得到字符串的首字符地址就会一直往后访问直到遇到 '\0' ;还是根据的整体字符串"abcdefg"或者整个数组而打印出来整个字符串;
于是我们尝试将数组的首元素地址存放到指针p1中,我们会发现 printf("%s\n", p1);也可以实现打印字符串 "abcdefg" ,那么此时似乎就可以感受到打印字符串只要有其首字符的地址即可;那么将字符串的地址存放到指针p中,指针p中存放的是整个字符串的地址吗?我们知道一个字符在内存中占 1 byte 的空间,而一个内存单元有一个地址,如果是想存放整个字符串的地址,就会存多个地址,但是指针变量在x86环境下只向内存申请了4byte 来存放该地址,所以就无法存放整个字符串的地址;printf("指针p中存放的地址=%p\n", p);的结果看以上代码运行结果,可知p中存放的为一个地址,即字符串首元素的地址;
我们将以上的代码又运行了一次:
我们不难发现,字符串的地址未改变,但是数组首元素的地址改变了;这是因为 "abcdefg" 是一个常量字符串,即不能被修改的字符串;此字符串是存放在内存中的常量区的,在编译期间编译器便指派了地址,常量区中的值只能读取而不能修改,所以 当将此存在常量区的字符串的地址存到指针变量中,为以防对此指针变量解引用而对此字符串进行修改,于是应该优化写作: const char* p = "abcdefg";
注:const 修饰指针变量:(eg. char* p = "abcdefg"; )
1、当 const 放在 * 的左边,有两中写法,一是 const char* p = "abcdefg"; 二是 char const * p = "abcdefg"; 表示 *p即指针p 指向的对象是常变量,也就是说指针p 指向的对象是常变量不可修改;
2、当const 放在* 的右边,写作:char* const p = "abcdefg"; 表示指针变量 p是个常变量即存放在指针p中的地址不可修改;
字符指针有两种的表示形式:
1、char* p = "abcdefg";
2、const char* p = "abcdefg"; \ char const * p = "abcdefg";
二、字符在内存中的存储
我们按照例子来分析:
#include<stdio.h>
int main()
{
const char* p1 = "abcdef";
const char* p2 = "abcdef";
char arr1[] = "abcdef";
char arr2[] = "abcdef";
if (p1 == p2)
printf("p1==p2\n");
else
printf("p1!=p2\n");
if (arr1 == arr2)
printf("arr1==arr2");
else
printf("arr1!=arr2");
return 0;
}
代码运行结果:
分析:常量字符串 ”abcdef“ 存放在内存中的常量区,常量区只能读取而不能对其进行修改;为什么常量字符串存在常量区?因为既然常量字符串不能被修改,那么这样的常量就没必要存多份,只要有一份,大家谁都不能去修改它;所以常量字符串在内存中存一份便足以够用了;所以指针p1 与指针 p2 中存放的地址相同,即p1==p2;
而当数组创建时会向内存申请相对的大小空间来存放数组中的数据;而数组arr1 和 数组arr2 都会向内存申请空间来存放其数据,而每个内存单元的编号都不相同,所以就会导致不同空间的地址在也不相同;再者,数组名代表着首元素的地址,所以 arr1 ! = arr2 ;
注:常量字符串不需要创建;当你写下此代码:const char* p1 = "abcdef"; 的时候,编译器便会在只读数据区中放 abcdef \0 ,便把" abcdef "当作常量字符来看待;而此时,只要将常量字符的起始地址交给一个指针去维护即可;
总结
1、字符指针就是指针变量指向的对象是字符类型,即指针变量的类型为 char* ;
2、const char* p = "abcdefg"; \ char const * p = "abcdefg"; 指针p中存放的是常量字符串在常量区存放的首字符 'a' 的地址
3、常量字符串存放在内存中的常量区,常量区中的数据只能读取而不能被修改。