首先介绍下指针和指针变量的区别
1.内存地址──内存中存储单元的编号
(1)计算机硬件系统的内存储器中,拥有大量的存储单元(容量为1字节)。
为了方便管理,必须为每一个存储单元编号,这个编号就是存储单元的“地址”。每个存储单元都有一个惟一的地址。
(2)在地址所标识的存储单元中存放数据。
注意:内存单元的地址与内存单元中的数据是两个完全不同的概念。
2.变量地址──系统分配给变量的内存单元的起始地址
假设有这样一个程序:
main()
{ int num;
scanf("%d",&num);
printf("num=%d\n", num);
}
C编译程序编译到该变量定义语句时,将变量num登录到“符号表”中。符号表的关键属性有两个:
一是“标识符名(id)”,
二是该标识符在内存空间中的“地址(addr)”。
为描述方便,假设系统分配给变量num的2字节存储单元为3000和3001,则起始地址3000就是变量num在内存中的地址。
3.变量值的存取──通过变量在内存中的地址进行系统执行“scanf(”%d“,&num);”和“printf(”num=%d\n“, num);”时,存取变量num值的方式可以有两种:
(1)直接访问──直接利用变量的地址进行存取
1)上例中scanf(“%d”,&num)的执行过程是这样的:
用变量名num作为索引值,检索符号表,找到变量num的起始地址3000;然后将键盘输入的值(假设为3)送到内存单元3000和3001中。
此时,变量num在内存中的地址和值,如图9-1所示。
2)printf("num=%d\n",num)的执行过程,与scanf()很相似:
首先找到变量num的起始地址3000,然后从3000和3001中取出其值,最后将它输出。
(2)间接访问──通过另一变量访问该变量的值
C语言规定:在程序中可以定义一种特殊的变量(称为指针变量),用来存放其它变量的地址。
例如,假设定义了这样一个指针变量num_pointer,它被分配到4000、4001单元,其值可通过赋值语句“num_pointer=#”得到。
此时,指针变量num_pointer的值就是变量num在内存中的起始地址3000,如图9-1所示。
通过指针变量num_pointer存取变量num值的过程如下:
首先找到指针变量num_pointer的地址(4000),取出其值3000(正好是变量num的起始地址);然后从3000、3001中取出变量num的值(3)。
(3)两种访问方式的比较
两种访问方式之间的关系,可以用某人甲(系统)要找某人乙(变量)来类比。一种情况是,甲知道乙在何处,直接去找就是(即直接访问)。
另一种情况是,甲不知道乙在哪,但丙(指针变量)知道,此时甲可以这么做:先找丙,从丙处获得乙的去向,然后再找乙(即间接访问)。
4.指针与指针变量
(1)指针──即地址
一个变量的地址称为该变量的指针。通过变量的指针能够找到该变量。
(2)指针变量──专门用于存储其它变量地址的变量
指针变量num_pointer的值就是变量num的地址。指针与指针变量的区别,就是变量值与变量的区别。
(3)为表示指针变量和它指向的变量之间的关系,用指针运算符“*”表示。
接下来介绍字符串指针
1、 字符串的表示形式
在C语言中,我们可以用两种方式访问字符串
(1)用字符数组存放一个字符串,然后输出该字符串。
main(){
char string[]="I love China!";
printf("%s\n", string);
}
(2)用字符指针指向一个字符串。可以不定义字符数组,而定义一个字符指针。用字符指针指向字符串中的字符。
main(){
char *string="I love China!";
printf("%s\n", string);
}
在这里,我们没有定义字符数组,而是在程序中定义了一个字符指针变量string,用字符串常量"I love China!",对它进行初始化。C语言对字符串常量是按字符数组处理的,在内存中开辟了一个字符数组用来存放该字符串常量。对字符指针变量初始化,实际上是把字符串第1个元素的地址(即存放字符串的字符数组的首元素地址)赋给string。有人认为string是一个字符串变量,以为在定义时把"I love China!"这几个字符赋给该字符串变量,这是不对的!!
实际上,char *string="I love China!"; 等价于:
Char *string;
String = “I love China!”;
可以看到,string被定义为一个指针变量,指向字符型数据,请注意它只是指向了一个字符变量或其他字符类型数据,不能同时指向多个字符数据,更不是把“I love China!”这些字符存放到string中(指针变量只能存放地址),也不是把字符串赋给*string。只是把“I love China!”的第一个字符的地址赋给指针变量string。在输出时,要用:printf(“%s\n”, string);,其中“%s”是输出字符串时所用的格式符,在输出项中给出字符指针变量名,则系统先输出它所指向的一个字符数据,然后自动是string加1,使之指向下一个字符,然后再输出一个字符……如此知道遇到字符串结束标志“\0”为止。
注意:可以通过字符数组名或者字符指针变量输出一个字符串。而对一个数值型数组,是不能企图用数组名输出它的全部元素的。例如:
Int i[10];
………
Printf(”%d\n”, i);
是不行的,只能逐个输出!!!!!!显然%s可以对一个字符串进行整体的输入和输出
最后针对自己写的一个程序中错误进行解析
...........//省略部分
char *test = 'a';
printf("%c\n",test); //此处能正常输出字符 a
//printf("%c\n",*test); //段错误
char b = 'b';
char *ptr = &b;
printf("%c\n",*ptr); //正常输出b
绕了半天才理解其中的原理:
char *test = 'a';写这句话原意是想将字符a的地址赋值给字符指针变量test,但是问题就出现在此,地址是整形,而字符是可以和整形相互转换的,因此
此处实际是存储了字符a转换成整形97的值给了test变量,从而导致*test根本不能读取地址为:97上的内容,但是printf("%c\n",test); 却正常输出,因为test里面保存的地址值为97,可以转换成字符。
或许通过下面这个例子你也能稍微理解点
如果:int *p;
*p = 7;
则编译器(vs2008)会提示The variable 'p' is being used without being initialized.即使用了未初始化的变量p。
因为p是指向7所在的地址,*p = 7给p所指向的内存赋值,p没有赋值,所以p所指向的内存位置是随机的,没有初始化的。