指针
基本概念
C程序中使用的任何数据都会在计算机存储区域中分配一定长度的内存空间。内存空间被划分成一个个存储单元,存储单元以字节为单位。
为正确访问到这些内存单元,必须为每个内存单元编号。
内存单元的编号通常称为地址,内存单元中存放的内容称为值。
一个数据所占用的内存空间的首地址又称为该数据的指针,存放该数据首地址的变量称为指针变量。
约定:指针是常量,指某个对象所占内存空间的首地址,指针变量是指取值为指针值的变量。
指针变量的定义
类型标识符 *指针变量名;
/* 必须指明该指针变量所指向的数据类型,这样通过指针变量间接引用数据时才能确定其数据存储空间的大小 */
/* “*”表示将要定义的变量是一个指针变量 */
指针变量的引用
取地址运算(&)
&变量名; /* 通过取地址运算,可以得到某变量的地址,进而可以将其存放到指针变量中 */ int *px,x; px=&x;
指针运算(*)
*指针变量; /* 该运算返回符号右边指针变量所指向的存储单元存储的数据的值 */ printf("%d ",*px);
多重指针
指针变量存放的是数据在内存空间中的首地址,这个指针变量的地址同样也可以存储在另一个指针变量中。
如果一个指针变量p指向的是一个指针类型的数据,我们就称p是一个多重指针变量。
在实际编程中,很少用到三重及以上的指针变量。
二重指针变量的定义
类型标识符 **指针变量名; int **p; /* 由于指针运算符是按右至左的顺序结合的,上面的定义相当于int *(*p) */
空指针和void类型的指针
值0是唯一能够直接赋给指针变量的整数值。
如果指针变量的值为0,表示该指针变量不指向任何值。
C语言在<stdio.h>中定义了一个符号常量NULL,与0等价,称为空指针。
指针变量取空指针与指针变量未赋值是不同的。
void类型的指针通常用于程序中为动态数据结构(链表、堆栈、队列、树等)申请内存空间。
一维数组与指针
数组在内存中按顺序存放在一段连续的存储区域中,数组名是这片内存区域的首地址。
通过指针引用数组元素
int *p,a=[0,1,2,3,4,5,6,7,8,9]; p=a;/* 或者p=&a[0] */
若指针变量p指向数组a的首元素地址,则可以使用以下几种方式访问数组的第i个元素:
下标方式:a[i]或p[i]。
偏移量方式:*(a+i) 或 *(p+i)。
使用指针变量引用数组元素时,还可以改变指针变量的值,让指针指向需要访问的数组元素:
p=p+i;p=p-i; p++;p--; /* 然后使用*p访问指针指向的数组元素 */
在数组和指针结合后,指向同一数组的两个指针变量之间还可以进行如下运算:
- 比较运算,比较指针值的大小确定数组元素的前后顺序。
- 减法运算,差值为它们之间的数组元素的个数。
数组名与数组指针变量的区别
数组名时数组的首地址,其值是不变的。而指针变量的值可以在执行过程中改变。
使用指针变量处理字符串
char *指针变量名="字符串"; char *p="hello world";
动态分配一段存储区域
指针的主要作用体现在通过动态内存分配,表示复杂的数据结构。
C语言的通用实用库提供了一组用于动态内存分配的函数,供用户在程序执行时,申请内存空间及释放不需要的空间。
malloc函数
void *malloc(unsigned size); /* 该函数用于在动态存储区分配大小为size字节的连续空间。如果分配成功,函数返回指向被分配内存的void *类型的执行,否则返回NULL。 */
calloc函数
void *calloc(unsigned n,unsigned size); /* 该分配n个大小为size字节的连续内存空间,并把数组元素初始化为0。如果分配成功,函数返回指向被分配内存的void *类型的执行,否则返回NULL。 */
realloc函数
void *realloc(void *ptr,unsigned size); /* 该函数将ptr指向的由malloc、calloc或realloc所分配的连续的空间的大小修改为size字节。分配不成就不修改,分配成功则返回指向新分配内存空间的指针或NULL指针 */
free函数
free(ptr); /* 用于释放指针ptr所指向的内存空间。只有通过动态内存分配的内存空间才能通过free函数释放。*/
二维数组和指针
int a[4][3]={{0,1,2},{3,4,5},{6,7,8},{9,10,11}};
二维数组和数组元素的地址
*(a+i)等于a[i]。
a[i]是二维数组第i行0列元素的首地址,即"&a[i] [0]"。
a[i]+j或*(a+i)+j表示二维数组a第i行第j列元素的地址,即"&a[i] [j]"。
a[0]、*(a+0)、或 a、&a[0] [0]是等价的。尽管它们与a的值是相等的,但其类型不一样的。在对这些指针进行数值加减运算时,其单位大小时不一样的。a的单位是行,a+1表示下一行的地址,而a[0]、(a+0)、或 *a、&a[0] [0]的单位是元素。因此不能通过 *a来得到二维数组a的首元素a[0] [0]的值。
通过指向数组元素的指针引用二维数组
假设有一m✖️n的二维数组a,那么第i行第j列的元素a[i] [j]在数组中的位置可以用公式i*n+j进行计算。如a[2] [2]在4✖️3的数组中的位置为 2*3+2=8,即它是数组a中的第8个元素。
通过行指针引用二维数组
int a[4][3]={{0,1,2},{3,4,5},{6,7,8},{9,10,11}}; int (*pp)[3]=a; /* pp+i将指向二维数组中的第i行,*(pp+i)则是第i行第0列元素的地址。 相应地,*(pp+i)+j就是第i行第j列元素的地址,而*(*(pp+i)+j)则是第i行第j列元素的值,即a[i][j] */
指针数组
若干有序的同一类型指针也可以构成指针数组。
类型标识符 *指针数组名[长度]; int *p[5]; /* 定义了一个指针数组,它有5个元素组成,每个元素的值都是指向int型数据的指针 */
函数和指针
C函数被装入内存时,其代码占用一段连续的内存区,函数名就代表该内存区域的首地址。
函数指针的定义
类型标识符 (*指针变量名)(); int (*pf)(); /* 定义了一个指向整型函数的指针变量pf */ pf=max; /* pf指向max函数 */
函数指针变量不能进行算术运算。
用函数指针变量调用函数
(*函数指针变量名)(实参表); *(pf)(x,y); /* 用函数指针变量调用函数,好像是多此一举。实际上,当我们需要根据程序执行不同的情况调用不同的函数时,这种方式非常方便。我们将函数的指针存储在一个指向函数的指针数组中,可以将用户的选择作用数组的下标,通过使用数组中的函数指针来调用函数 */
用指向函数的指针作函数参数
如果某个函数要调用其他的函数,而每次调用的函数需要随情况的不同而变化,则可以利用指向函数的指针来作为函数的参数。