指针是c语言的一个重要的数据类型,在C语言程序设计中,利用指针可以直接对内存中的各种不同的数据进行快速处理,同时也为函数之间各类数据的传递提供了便捷的方法,指针操作是与计算机系统内部资源密切相关的一种处理方式.
- 地址
我们知道,一个程序一旦被执行,程序中的指令,常量,变量等都要储存在计算机内存中.计算机的内存是以字节为单位的一片连续的存储空间.每个字节都有一个编号.这个编号就称为内存地址.没有地址,没有编号.系统则无法对内存进行管理.
内存的存储空间是连续的,所以其地址(编号)也是连续的.地址与储存单元一一对应.而且是存储单元的唯一标志.需要注意的是储存单元的地址与储存单元中的内容是两回事.
我们知道,在高级语言中,变量可以直接通过变量名,变量类型来开辟一个内存空间,储存我们的变量内容,并且我们可以直接通过变量名来对该变量的内容进行操作,这是因为高级语言中,变量在声明时,就为我们创建了一张对应的表,将变量名与开辟的内存空间地址联系起来.归根结底,还是通过变量的地址来对对应的内存空间进行访问与操作的.
- 指针变量
而c语言中则提供了这样一种变量.指针变量.指针类型的变量是用来存放地址的变量.从定义来看,指针变量是一个变量,它和普通变量一样,是占用一定内存空间的.但是它是用来存放地址的.这意味着它一般是用来对其他变量进行操作的.
当把某一个地址量赋予指针变量时,则该指针变量就指向了那个地址的内存区域.这样做的目的就是为了能够通过这种指向变量地址的方式,来实现间接对某一内存区域中存储的内容进行处理.
指针变量指向的内存区域中的数据称为指针的目标,如果它指向一个变量的内存空间,则该变量称为指针的目标变量.通过指针变量访问目标变量的方式叫间接访问方式.
指针不仅仅可以指向变量的地址,还可以指向内存中的其他任何数据结构,如数组,结构,和联合体,还可以指向函数.这将使指针能够做的事情更加强大.
在程序中参与处理的量不是指针,指针本身只是一个地址量,其指向的目标才是要处理的数据.
- 指针的定义:
类别名 数据类型 *指针名
int *px
char *pchar
static int *pa
指针的数据类型不是指针变量本身的数据类型,而是指向目标的数据类型,因为指针变量指示用来存放其指向目标的地址,所以其类型也应当与其保持一致.
- 指针的初始化:
int a;
int *p = &a;
&a就是一个地址常量.它就是变量a的内存地址.当将一个变量的地址赋值给一个指针时,它必须是已经声明过的(定义),因为变量的空间是动态分配的,只有预先声明了该变量.才会为其分配内存空间.
当然指针变量还可以被指向一个地址变量.也就是指针之间可以进行赋值操作.将一个指针变量赋值给另一个指正变量.
int n;
int *p = &n;
int *q = p;
看两个例子:
// 指针概念
void PointConcept(int b) {
int a = b;
int* pa = &a;
printf("a:%d\n",a);
printf("*pa:%d\n",*pa);
printf("&a:%x(HEX)\n",&a); // %x以十六进制输出.
printf("pa:%x(HEX)\n",pa);
printf("&pa:%x(HEX)\n",&pa);
}
这里可以看到,&a,pa指向的都是变量a的内存地址.而&pa指向是指针变量pa的内存地址.这说明指针变量具有与普通变量一样性质,只不过它的内存空间存放的是目标数据的地址.所以其本身的地址是不同的.
- 指针变量的长度:
// 指针长度
void PointerLen() {
char str[] = "abcdefg", * ps =str;
int i = 10, * pi = &i;
float f = 45.45, * pf = &f;
double d = 354.33, * pd = &d;
printf("size of str pointer is byte=%d,bit=%d\n",sizeof(ps),8*sizeof(ps));
printf("size of str pointer is byte=%d,bit=%d\n",sizeof(pi),8*sizeof(pi));
printf("size of str pointer is byte=%d,bit=%d\n",sizeof(pf),8*sizeof(pf));
printf("size of str pointer is byte=%d,bit=%d\n",sizeof(pd),8*sizeof(pd));
}
我们可以看到,四种类型的指针变量的长度都是相同的,所以指针变量的长度不是根据指针变量的类型来定的,指针变量的类型只是对应其指向目标的数据类型.
指针存放的是其指向目标的首地址,而不是其目标数据全部地址
为什么指针只需要指向目标数据的首地址,并且其类型必须要与其指向的目标数据类型保持一致呢.这就是因为内存单元的连续性,其地址都是连续,指针只需要知道指向目标的首地址,和指向目标的数据类型(相当于知道了数据长度).就可以实现完全读取到目标数据的所有内存地址了.
所以,指针变量在内存中的长度大小是不变的,4byte,32bit.只需要用来存放目标数据的首地址就可以了.
- &(取地址运算符)和*(指针运算符)
&运算符
我们在指针的定义与初始化中了解到了,我们需要使用到*号来声明一个指针变量,使用&获取到目标变量的地址,然后赋值给指针变量.
使用&运算符操作的对象必须是左值表达式(即变量或有名存储区).
int x; // 则&x的类型就是int*(整形指针)
char y; // 则&y的类型就是 char* (字符型指针)
double z; // &z的类型就是double* (双精度浮点型指针)
需要注意的一点是:数组,常量,不是左值表达式.而寄存器变量没有存储地址,所以他们都不能作为单目运算符&的操作对象.
int a[4];
register int k;
// 使用&a[0],&a[1]都是合法的,但是不能使用&a,&k.
指针运算符*
单目是间接访问运算符,它通过指针间接访问所指的对象.而不是通过名称访问的,所以称为间接访问.与访问对象组成的表达式,称为间接访问表达式:
* 操作对象
char a;
* (&a); // 地址表达式
*pa=&a; //地址表达式,将a变量的地址赋值给了pa,然后通过*指针符指向pa指向的本身.
操作对象必须是一个地址表达式.即指针(地址表达式,或地址常量),运算结果为指针所指的对象(变量本身).结果类型为指针所指对象的类型.
char c,*pc=&c; //变量声明且初始化了指针变量pc存放c的地址.
*pc='a'; // 通过指针符(间接访问指针变量pc所指向的c变量的内存区),赋值.
*(&c)='a'; // 直接通过指针符(间接访问c地址指向的数据内存区),赋值
c='a'; // 直接通过变量名赋值.
单目运算符*和&的运算关系
他们互为逆运算,即*指向的是指针变量的指向目标变量,&获取的是指向目标的内存地址.即:
*(&左值表达式)=左值表达式;
int c;
*(&c); //指向的就是c本身.
&(*左值表达式)=地址表达式;