05 - 指针 联系客服

发布时间 : 星期二 文章05 - 指针更新完毕开始阅读729d372c69dc5022abea0006

本章主要内容: 指针的定义及初始化 指针的指针 指针的运算 指针与数组 指针数组和数组指针 指针与字符串 指针与函数

1.1 指针的概述

指针是C语言中广泛使用的一种数据类型。运用指针编程是C语言最主要的风格之一。利用指针变量可以表示多种数据结构,能方便地使用数组和字符串,并能像汇编一样处理内存地址。正因为它的灵活多变,初学者往往会感到很困惑。通过本章的学习,我们要玩转指针。利用指针编写程序,跟数组结合使用等等。

1.2 地址与指针

在计算机中,所有的数据都是存放在存储器中的,一般把存储器的一个字节称为一个内存单元。不同的数据类型占用的内存单元是不同的。为了快速、准确的访问这些内存单元,计算机为每一个内存单元进行编号,内存单元的编号也就是内存单元的地址。C语言中,把这个地址叫做指针。

1.3 指针的定义

指针是一种数据类型,基于该类型声明的变量称为指针变量,该变量存放的是内存中的某个地址,和普通的变量一样,在使用指针变量之前应先对指针变量进行声明: 类型 * 指针变量名;//如 int* pNum;

“*”表示语句声明的是一个指针变量,类型指定了指针所指的内存单元的数据类型。 可以将int*理解成一种复合类型,是指向int型数据的指针。 应当注意,下面的语句: int * pNum1,pNum2;

声明了一个指针(pNum1)和一个int型变量(pNum2),在一次性声明多个指针时,每个指针变量名前都要加*,int *pNum1,*pNum2;便一次性声明了两个指针变量。 通过sizeof可以计算出指针变量所占用的内存单元始终是4个字节。也就是说任何类型的指针变量都只占用4个字节的内存空间。如:

int *pi; char *pch; double *pf; 则:sizeof(pi)=sizeof(pch)=sizeof(pf)=4 (参见代码Example1)

另外,修改指针变量的值并不会影响到指针所指向的变量的值。如: int i = 8; int j = 9; int *pi = &i; pi = &j; i的值始终是8没有改变。

1.4 初始化指针

声明指针变量时,C并不会自动对其进行初始化,这时,指针变量的值是随机的,在内存中乱指一气,此时,通过指针间接访问所指的内存区域是十分危险的,因为你完全不知道自己在做些什么。通过取地址符(&)给指针变量赋值是个有效的手段,实际上,可以在声明一个指针变量的同时完成其初始化。 int num=1; int *pN=#

上述语句声明了一个指向int型变量的指针pN,并用num在内存中的地址对其赋值。 在使用指针前,一般要对其进行初始化(在声明的同时初始化或赋值),使其有一个确定合适的值,对于无处可指的指针变量,也要将其初始化为NULL(即0,空指针)。 如:int *pa = NULL;

1.5 指针的指针

指针变量也是变量,占据一定的内存空间,有地址,因此可以用一个指针指向它,这称为指向指针的指针,或二级指针。可已通过“**”声明一个二级指针,如 double num; double* pN=# double** ppN=&pN;

上面的指针可以看成指向double*变量类型的指针,若有需要,还可以定义三级、甚至更高级的指针。

1.6 指针之间相互赋值

C允许同类型的指针间的赋值,如图4.2所示,pN1和pN2是两个相同类型的指针,执行“pN2=pN1;”这样一个赋值操作后,pN1和pN2指向同样的地址,也就是说,两个指针指向同一个内存单元,对*pN2的任何改动都会影响*pN1的值,反之亦然

(参见代码Example2)

1.7 指针的运算

C语言中指针所能进行的运算是十分有限的,通常有以下几种:指针与整数的加减(包括指针的自增和自减)、同类型指针间的比较、同类型的指针相减。

指针与整数的加减:指针与整数的加减表示指针在内存中向下或向上移动整数个单位。该单位是多少个内存字节取决于指针所指变量的类型。如:short类型的每次移动2个字节,double类型的每次移动8个字节。将指针变量+1,其地址增加的值等于所指向的类型占用的内存字节数。公式:指针变量新值=指针变量当前值+N*指针所指类型占用的内存字节数。 (参见代码Example3)

同类型指针的比较:两个指针的比较是两个指针保存的地址数值大小的比较,如:p1的值为0x12345678,p2的值为0x12345679,则p1

同类型的指针相减:两个相同类型指针相减,返回值是个整数,其值为:(指针1的值-指针2的值)/ 指针所指向变量的类型所占内存字节数。(参见代码Example4)

1.8 指针与数组

指针与一维数组:

在C语言中,指针和数组的关系非常密切,其原因在于凡是能用数组下标完成的操作都可以用指针来实现。可以通过数组的下标唯一确定数组中的某个元素,这种访问方式称为下标法。由于数组的每个元素都相当于一个相应类型的变量,指针变量可以指向一般的变量,当然也可以指向数组中的元素。而且数组中的各个元素是按顺序连续的存放在内存中,因此,我们只要知道一个数组的首地址(即第一个元素的地址),然后依次往下移动,就能找到该数组的所有元素。(参见代码Example5)C语言中,获取数组首地址的方法有两种: 第一个元素的地址:p = &a[0];

数组名,数组名也代表数组的首地址:p = a; P指向数组的首地址之后,p+1表示数组元素a[1]的地址,…p+i表示数组元素a[i]的地址,因此我们可以通过*(p+i)来访问数组元素a[i];这种访问方法叫指针法。 假设:int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; int *p = a; 那么获得数组下标为i的元素的地址:a+i p+i &a[i] 访问数组下标为i的元素:a[i] *(a+i) p[i] *(p+i)

注意点:数组名和指针变量本身并不完全等同,数组名代表的是一个地址常量,是数组的首地址,是不能改变的,而指针变量的值是可以改变的,它可以指向数组的任意一个元素的地址。因此语句p++; ++p; p+=5;等等都是合法的,而a++; ++a; a+=5;等都是非法的。(参见代码Example6) 指针与二维数组: 在C语言中,二维数组是按照行优先的规律转换成一维数组存放在内存中。例如: int a[4][3]; int *p = &a[0][0];或int *p = a[0] 则二维数组在内存中的存储顺序及地址关系如图:

a表示二维数组的首地址,a[0]表示第0行元素的首地址,a[1]表示第1行元素的首地址,a[2]表示第二行元素的首地址,a[3]表示第3行元素的首地址。由此归纳: a+i:二维数组第i行的首地址。 ?a[i]

*(a+i):二维数组第i行第0列的地址。?&a[i][0] 或 a[i] *(a+i)+j:二维数组第i行第j列的地址 ?&a[i][j] 或 a[i] + j

*(*(a+i):二维数组第i行第0列的值。?a[i][0] 或 *a[i] *(*(a+i) + j):二维数组第i行第j列的值。?a[i][j]或 *(a[i] + j)

由a[i]?*(a+i)可以将二维数组进行转换,将数组的一个[]拆开就是a[i][j] ? (*(a+i))[j]?*(*(a+i) +j)

如果一个指针*p = a[0].则可以通过*(p + (i*每行列数+j))获得第i行、第j列的值(即a[i][j]),这可以根据上面的对照存储表得出。 (参见代码Example7)

1.9 数组指针与指针数组

数组指针:指向由n个元素组成的一维数组的指针。(趋向于指针)

定义格式:数据类型 (*指针变量)[n]; ()不能缺少。n必须与二维数组a[i][j]的j相同。 赋值方式:int a[3][4]; int (*p)[4]; p = a; 或 int (*p)[4] = a;定义的时候初始化。数组指针通常和二维数组名等价。

访问方式:p[i][j] 或 *(p+i)[j] 或 *(*(p+i) + j) 指针数组:数组的每一个元素都是一个指针变量。(趋向于数组)

定义格式:数据类型 *数组名[n]; 所有元素都必须是同种存储类型和指向相同数据类型。 赋值方式:int a[3][4]; int *pb[3] = {a[0], a[1], a[2]}; 指针数组通常和指针的指针等价。 访问方式:p[i][j] 或 *(p+i)[j] 或 *(*(p+i) + j) 指针数组主要用于处理若干个字符串。如:

char * name[] = {“Sunday”, “Monday”, “Tuesday”, “Wednesday”, “Thursday”, “Friday”, “Saturday”};这样name[0]就指向”Sunday”…… (参见代码Example8)

1.10 指针与字符串

C语言存储访问一个字符串有两种方式:字符数组、字符指针。 char str[] = “how are you?”; char *pStr = “how are you?”; 其中后者为文字常量区,内容不能被修改。

1.11 指针与函数

前面我们讲过C语言中函数传递有2中方式:值传递和地址传递。其中地址传递就是指针。此时形参的改变会影响是实参的变化。

数组名作为函数的参数,它可以作为函数的形参和实参,本质上还是地址传递。以下四种情况都会引起形参和实参一起变化 形参和实参都是数组名

形参是数组名,实参是指针变量 形参是指针变量,实参是数组名 形参和实参都是指针变量 (参见代码Example9)

1.12 动态内存分配

C语言中动态分配和释放内存是利用函数malloc()和free()来实现的.在#include 头文件中 函数原型:

void* malloc(int size); //表示动态分配size个字节的内存空间