指针是C语言中一个重要概念,是一种数据类型,也是从语言的精华所在!利用指针可以有效地表示复杂的结构;实现内存的动态分布;方便使用字符串;有效而方便的使用数组;在调用函数时能返回多个值;能直接处理内存单元地址;对变量的间接访问等。
1.指针与指针变量
1.1指针和指针变量的概念
为了说清楚什么是指针,首先必须弄清楚“数据在内存中是如何存储”这一问题。CPU只能访问内存中程序指令和数据,也即是程序运行时的指令和数据必须事先装入内存。因此在程序中定义的变量,程序在运行时系统会为每一个变量分配一定的内存空间,内存空间的的大小取决于变量的类型,单位字节,eg:char类型变量分配一个字节,int和float类型变量分配4个字节。
内存是以字节为单位的一片连续的存储空间,为了方便访问,CPU给每个字节一个编号即地址,利用地址来区分不同的内存单元。
上面的图解意思就是把22FE44到22FE47之间四个字节分配给c,把22FE48到22FE4B之间的四个字节分配给b,把22FE4C到22FE4F之间的四个字节分配给a。
在程序中一般是通过变量名来访问内存单元,如输入scanf("%d",&a);执行该语句时,通过键盘输入数据到输入缓冲区,程序从输入缓冲区读入一个数据送入变量a所分配的内存空间中即22FE4C到22FE4F,如有语句printf("%d",a);执行时根据变量名找到对应地址(22FE4C到22FE4F),然后取出数据,送到显示器上输出。
1.1.1间接访问
如下面的代码就是通过间接访问改变变量a的值。
int a, * p = &a;
a = 12;
*p = 24;
printf("%d %d", a, *p);//a=24,*p=24
特殊变量p用来存放a的地址,假如a的地址为004000,内存也为p分配空间地址为005000,通过p访问a的值,先从p的空间005000中读出p的值004000,即a的地址,然后再从004000-004003中取出a的值,通过p变量访问a的值就是间接访问。
1.1.2直接访问
例如上面的scanf和printf就是直接访问a的地址。
ps:按变量名访问内存,实际上仍是通过变量的地址进行访问的,因此我们前面编程时,对变量的操作,实际上是对变量的存储单元进行操作的。
1.2指针变量的定义与初始化
为了表示指针变量和它所指向的变量之间的联系,在程序中用“*”符号来表示“指向”,也即是间接访问。
1.2.1指针变量的定义
与其他变量一样,指针变量仍遵循先定义后使用的原则,其形式为:
数据类型标识符 *指针变量名
说明:
(1)“*”表示其后面的变量名为指针类型。
(2)“数据类型标识符 ”是定义指针变量所指向的目标变量的数据类型,也称指针变量的类型。
eg:int *p;char *p1;第一条语句表示p为指针变量,类型为int型;第二条语句表示p1为指针变量,类型为char型。
因此我们可以知道指针变量只能指向此类型的目标变量即只能将此类型的变量的地址赋给该指针变量。
ps:在定义指针变量时,系统为指针变量分配空间,但是不论指针变量是什么类型,指针变量都是用来存放内存地址的,因此所有的指针变量,系统给它分配的内存空间都是相同的,和定义指针的类型无关.
1.2.2指针变量的引用
要牢记指针变量中只能存地址,虽然地址是整形的值,但是仍然不能将一个整数的值赋给指针变量。
两个与指针变量有关的运算符
(1)&:取地址运算符
(2)*:指针运算符(间接访问运算符)取其指向的变量的值。
ps:*与&具有相同的优先级,结合方向从右到左,因此*&a=a,&*p=p
1.2.3指针变量的初始化
指针变量必须先赋值后使用,因为指针变量被定义后,它的值是不确定的(称这时的指针变量为悬挂(空)指针),它将会指向一个未知的目标(内存空间)。若此时对指针变量间接访问,有可能改变了一些不该(不能)改动的数据,从而产生一系列错误。若指向的是操作系统代码空间,则改变的是操作系统,会对系统造成危险。因此使用指针编程时,为了安全使用指针变量,在定义时,必须赋给它一个确定的地址,即指针的初始化。如果在编程时,不能确定指针指向哪一个变量,在定义时可以初始化指针为“空”,即“空”(NULL,在stdio.h中定义了其值为0,地址也为0,但是这个地址是不可以使用的,读取该地址会报错)赋给指针变量。
1.3指针的运算
1.3.1指针的赋值运算
(1)将一个变量的地址赋给指针变量,是指针变量指向该变量。
int a, b, * p1, * p2;
a = 10;
b = 20;
p1 = &a;
p2 = &b;
(2)相同类型指针变量间的赋值
int a = 5, * p1 = &a, * p2;
p2 = p1;
这种赋值后,指针变量所指向的空间关系如下图:
(3)给指针变量赋空值
pa=NULL;这里指针pa并非指向0单元,而是有一个具体的空值,表示pa不指向任何变量。
ps:即使知道变量a的地址是2000,仍不能将2000赋给指针变量pa(pa=2000),而只能将&a赋给整形的指针变量pa(pa=&a)。但是对于全局变量和局部静态指针变量,在定义时若为初始化,则自动初始化为“空指针”。
1.3.2指针的运算
指针的乘除运算时无意义的,且指针与指针相加也是无意义的,因此指针只能加(减)一个整数n,加减运算的结果并不是指针变量的值(地址)加减n,而是与指针变量所指对象的数据类型有关。指针变量的值(地址)应增加(减少)“n*sizeof(指针类型)”
指针的运算有下列几种:
1.3.3指针的关系运算
指针变量与其他变量一样,可以进行关系比较运算。允许两个指针变量进行所有的关系运算。
说明:
(1)指针的关系运算是将指针变量的值(地址)进行比较,所以将将两个指针变量进行比较时,一定要给指针变量赋值。
(2)指针变量与一个整型数据比较是没有意义的。
(3)只有相同类型的两个指针变量才可以进行比较(关系运算),NULL可以与任何类型的指针进行==,!=运算,用来判断指针变量是否为空。
1.4多级指针
多级指针是用来保存一个指针变量的地址的,下面以二级指针为例,多级指针也是如此。
1.4.1二级指针变量的定义形式
数据类型标识符 **指针变量名
说明:
(1).其中“** 指针变量名”相当于*(*指针变量名),在括号内定义了一个指针变量,括号外的*,说明指针变量(二级指针)的目标变量是一级指针。
(2).数据类型标识符说明目标变量(一级指针)指向的变量的数据类型。
1.4.2二级指针变量初始化
int a,*p,**pp;
1.4.3二级指针应用
变量a的访问方式(直接访问,一次间接访问,二次间接访问)
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
int a = 100, * p, ** pp;
p = &a;
pp = &p;
printf("%d\n", a);
printf("%d\n", *p);
printf("%d\n", **pp);
return 0;
}
*p代表其所指向的变量a,所以输出100,**pp可以看作*(*pp),*pp代表p,所以*(*pp)等价于*p,也指向a,所以输出也为100.
指针上的内容就到这里啦,咱们下次见哦!