什么是指针?
从根本上看,指针是一个值为内存地址的变量。正如char类型变量的值是字符,int类型变量的值是整数,指针变量的值是地址。
因为计算机或者嵌入式设备的硬件指令非常依赖地址,指针在某种程度上把程序员想要表达的指令以更接近机器的方式表达,因此,使用指针的程序更有效率。尤其是指针能够有效地处理数组,而数组表示法其实是在变相的使用指针,比如:数组名是数组首元素的地址。
要创建指针变量,首先要声明指针变量的类型。假如想把ptr声明为储存int类型变量地址的指针,就要使用间接运算符*来声明。假设已知ptr指向bah,如下表示:
ptr = &bah;
然后使用间接运算符*找出储存在bah中的值:value = *ptr;此运算符有时也被称为解引用运算符。语句ptr=&bah;value=*ptr;放在一起的效果等效于:value=bah;那么该如何声明一个指针变量呢?是这样吗:
pointer ptr; // 不能这样声明一个指针变量
为什么不能这样声明一个指针变量呢?因为声明指针变量时必须指定指针所指向变量的类型,不同的变量类型所占据的储存空间是不同的,一些指针操作需要知道操作对象的大小。另外程序必须知道储存在指定地址的数据类型。例如:
int *pi; // pi 是指向 int 类型变量的指针 char *str; // str 是指向 char 类型变量的指针 float *pf, *pg; // pf, pg 都是只想 float 类型变量的指针
类型说明符表明了指针所指向对象的类型,解引用符号*表明声明的变量是一个指针。int *pi声明的意
思是pi是一个指针,*pi是int类型。
声明并使用指针(图片来源百问网)
这仅仅是指针的简单使用,实际指针的世界千变万化,丰富多彩,纵使多年C语言开发的老手,有时在面对指针的使用也会出错,后继者更应谨慎求索,后面将会对指针常见的应用和注意事项进行介绍。
1. 指针与数组
前面提到可以使用地址运算符&获取变量所在的地址,而在数组中同样可以使用取地址运算符获取数组成员中任意成员的地址,例如:
int week[7] = {1, 2, 3, 4, 5, 6, 7}; int *pw; pw = &week[2]; printf("week is: %d", *pw);
输出的结果是:week is 3
2. 指针与函数
int sum(int *pdata) { int i = 0; int temp = 0; for(i=0;i<10;i++) { temp = temp + (*pdata); pdata++; } return temp; }
这个例子有几点值得讲解的地方,第1点指针pdata是作为函数的形参存在,指向一个储存int类型变量的地址;第2点指针pdata++;语句执行后,pdata只想的地址自增的不是1,而是int类型所占的大小,加入pdata最初的值是0,int类型占2个字节,那么pdata++;语句执行后,pdata的值就变成了2,而不是1,而*pdata的值是地址2所在的值不是地址1所在的值;第3点这个函数有个危险,即函数实现的是从pdata最初指向的地址开始往后的10个int类型变量的和,假如我们这样使用:
int data[5] = {1, 2, 3, -1, -2}; int x = sum(data);
可以看到数组data的数组名即数组的首地址作为参数输入到函数sum里,而数组的大小只有5个int,函数sum计算的却是10个数的和,因而就会出现地址溢出,得不到正确的结果甚至于程序跑飞。为了避免这个问题,通常的解决方法是加一个数量形参:
int sum(int *pdata, int length) { int i = 0; int temp = 0; for(i=0;i<length;i++) { temp = temp + (*pdata); pdata++; } return temp; } x = sum(data, 5);
或者给出指针范围:
int sum(int *pStart, int *pEnd) { int i = 0; int temp = 0; int length = (pEnd - pStart)/2; // 假设一个 int 占 2 个字节 for(i=0;i<length;i++) { temp = temp + (*pdata); pdata++; } return temp; } x = sum(data, &data[4]);
指针与函数的关系除了指针作为函数形参外还有另一个重要的应用,那边是函数指针,比如在typedef用法章节的那个例子:
typedef void (*pFunction)(void);
在这个例子中,首先*表明pFunction是一个指针变量,其次前面的void表示这个指针变量返回一个void类型的值,最后括号里面的void表明这个函数指针的形参是void类型的。如何使用函数指针调用函数呢?看下面这个例子:
int max(int a, int b) { return ((a>b)?a:b); } int main(void) { int (*pfun)(int, int); int a=-1, b=2, c=0; pfun = max; c=pfun(a, b); printf("max: %d", c); return 0; }
输出的结果是:2
3. 指针与硬件地址
指针与硬件地址的联系在volatile用法章节的例子中惊鸿一现,没有详细介绍,下面做详细说明。比如在STM32F103C8T6中内部SRAM的基地址是0x20000000,我们想对这片空间的前256个字节写入数据,就可以使用指针指向这个基地址,然后开始写:
volatile unsigned char *pData = (volatile unsigned char *)(0x20000000); int main(void) { int i = 0; for(i=0; i<256; i++) { pData[i] = i+10; } return 0; }
除了内存地址,还可以指向硬件外设的寄存器地址,操作方式与上述例子类似。指针应用的基本原则:
- 首先必须要指定指针的类型;
- 如果是普通指针变量,非函数形参或者函数指针,必须要给指针变量指定地址,避免成为一个“野 指针”;
内容来源:百问网