制 作:方 斌
C语言程序设计教程郧阳师范高等专科学校计算机科学系方 斌 制作制 作:方 斌第 10章 指针
§ 10.1 指针的概念 § 10.6 返回指针的函数
§ 10.2 变量的指针 § 10.7 指针数组和指向指针的指针
§ 10.3 数组的指针 § 10.8 指针使用小结
§ 10.4 字符串的指针 本章要求和作业
§ 10.5 函数的指针制 作:方 斌
10.1 指针的概念指针( pointer):是一个变量的地址。
指针变量:是一个变量,其值是另一个变量的地址。
任何变量都在计算机内存中占有一块内存区域,变量的值就存放在这块内存区域之中,(寄存器变量不在内存中,而是在 CPU的寄存器中)。
1.内存地址 ──内存中存储单元的编号
( 1)计算机硬件系统的内存储器中,拥有大量的存储单元(容量以
1字节为单位)。
为了方便管理,必须为每一个存储单元编号,这个编号就是存储单元的“地址”。每个存储单元都有一个惟一的地址。
( 2)在地址所标识的存储单元中存放数据。
注意:内存单元的地址与内存单元中的数据是两个完全不同的概念。
2.变量地址 ──系统分配给变量的内存单元的起始地址假设有这样一个程序:
制 作:方 斌
main()
{ int num;
scanf("%d",&num);
printf("num=%d\n",num);
}
C编译程序编译到该变量定义语句时,将变量 num 登录到,符号表,
中 。 符号表的关键属性有两个:一是,标识符名 ( id),,二是该标识符在内存空间中的,地址 ( addr),。
为描述方便,假设系统分配给变量 num的 2字节存储单元为 3000 和
3001,则起始地址 3000就是变量 num在内存中的地址 。
3.变量值的存取 ──通过变量在内存中的地址进行系统执行,scanf(”%d“,&num);”和,printf(”num=%d\n“,num);”时,
存取变量 num值的方式可以有两种:
id Addr
num 3000
制 作:方 斌
(1)直接访问 ──直接利用变量的地址进行存取
1)上例中 scanf(“%d”,&num)的执行过程是这样的:
用变量名 num作为索引值,检索符号表,找到变量 num的起始地址
3000;然后将键盘输入的值 ( 假设为3 ) 送到内存单元 3000和 3001中 。 此时,变量 num在内存中的地址和值,如图所示 。
2) printf("num=%d\n",num)的执行过程,与 scanf()很相似:
首先找到变量 num的起始地址 3000,然后从 3000和 3001中取出其值,
最后将它输出 。
( 2) 间接访问 ──通过另一变量访问该变量的值
C语言规定:在程序中可以定义一种特殊的变量 ( 称为指针变量 ),
用来存放其它变量的地址 。
3
2998 3000 3002 3004
制 作:方 斌例如,假设定义了这样一个指针变量 num_pointer,它被分配到 4000,4001
单元,其值可通过赋值语句,num_pointer=& num;,得到 。 此时,指针变量
num_pointer的值就是变量 num在内存中的起始地址 3000,如图所示 。
通过指针变量 num_pointer存取变量 num值的过程如下:
首先找到指针变量 num_pointer的地址( 4000),取出其值 3000(正好是变量 num 的起始地址); 然后从 3000,3001中取出变量 num的值( 3)。
( 3)两种访问方式的比较两种访问方式之间的关系,可以用某人甲(系统)要找某人乙(变量)来类比。
一种情况是,甲知道乙在何处,直接去找就是(即直接访问)。
另一种情况是,甲不知道乙在哪,但丙(指针变量)知道,此时甲可以这么做:先找丙,从丙处获得乙的去向,然后再找乙(即间接访问)。
3
2998 3000 3002 3004
3000
4000 4002
制 作:方 斌再如:
整型变量 i占 2000,2001两个内存单元,
整型变量 j占 2002,2003两个内存单元,
整型变量 k占 2004,2005两个内存单元,
它们是整型变量,其内存单元中存放的是普通整型数据。
变量 i_pointer占 3010,3011两个内存单元。其中存放的数据是变量 i的地址
2000(一个变量占多个内存单元时,以首地址表示该变量的地址)。这种存放另一个变量的地址的变量称为指针变量。
称:变量 i_pointer指向变量 i,是变量 i的指针。
制 作:方 斌
4.指针与指针变量
( 1) 指针 ──即地址一个变量的地址称为该变量的指针 。 通过变量的指针能够找到该变量 。
( 2) 指针变量 ──专门用于存储其它变量地址的变量指针变量 num_pointer的值就是变量 num的地址 。 指针与指针变量的区别,就是变量值与变量的区别 。
( 3) 为表示指针变量和它指向的变量之间的关系,用指针运算符,*”
表示 。
例如,指针变量 num_pointer与它所指向的变量 num的关系,表示为:
*num_pointer,即 *num_pointer等价于变量 num。
因此,下面两个语句的作用相同:
num=3; /*将 3直接赋给变量 num*/
num_pointer=# /*使 num_pointer指向 num */
*num_pointer=3; /*将 3赋给指针变量 num_pointer所指向的变量 */
制 作:方 斌
10.2 变量的指针一、指针变量的定义指针变量有三个属性:
( 1)该指针变量指向的变量的类型。如
i_pointer指向的变量 i是整型。
( 2)该指针变量在内存中占多少内存单元。
如 i_pointer占两个内存单元,称为“近指针”,用 near表示。如果该变量在内存中占
4个内存单元,称为“远指针”,用 far表示。
如果未指定 near或 far,缺省是 near。(指针变量在内存中要么占 2个内存单元,要么占 4个内存单元)。
( 3)该指针变量指向哪一个变量,即该指针变量的值是多少。如 i_pointer的值是
2000。
制 作:方 斌指针变量定义的一般形式:
类型标识符 * 标识符
,*”表示定义指针变量
“标识符”是指针变量名
“类型标识符”表示该指针变量所指向的变量类型。
例、
int i,j; /* 定义两个整型变量 */
int *pointer_1,*pointer_2;
/*定义两个可以指向整型变量的指针 */
float *pointer_3; /*定义一个可以指向浮点型变量的指针 */
char *pointer_4; /*定义一个可以指向字符型变量的指针 */
void *pointer_5; /*定义一个可以指向类型变量的指针 */
char far *pointer_6;
/*定义一个可以指向字符型变量的远指针占用 4个字节 */
制 作:方 斌指针变量的赋值:例、
pointer_1 = &i;
pointer_2 = &j;
注意,指针变量中只能存放地址,不能将一个非地址类型的数据
(如常数等)赋给一个指针变量,如:
pointer_1 = 100;
也可以在定义指针变量的同时指定其初值,如、
int a;
int *p = &a;
制 作:方 斌二、指针变量的引用有两个运算符可以引用指针变量:
( 1) &:取地址运算符。如 pointer_1 = &i;
( 2) *:指针运算符。用于访问指针变量所指向的变量。
如果定义:
int i,j;
int *pointer_1;
pointer_1 = &i;
指针变量 pointer_1指向变量 i,现在,对变量 i有两种访问方式:
( 1)直接访问。如 i = 100; j = i。
( 2)通过指针变量间接访问。如,
*pointer_1 = 100;
j = *pointer_1;
制 作:方 斌到这里为止,用指针变量间接访问另一个变量,似乎显得多余,但是,在复杂程序设计中,这种间接访问可以大大提高程序的效率并使程序非常简洁。因为,只要改变指针变量的值,就可以访问其他变量。例、
int i,j;
int *p;
p = &i; (p指向 i )
*p = 100; (*p访问 i)
p = &j; (p指向 j)
*p = 200; (*p访问 j)
制 作:方 斌
[例 10.1]
void main(viod)
{
int a,b;
int *pointer_1,*pointer_2; /* 定义指针变量 */
a = 100; b = 10;
pointer_1 = &a;
pointer_2 = &b;
printf("%d,%d\n",a,b);
printf("%d,%d\n",*pointer_1,*pointer_2);
}
程序运行结果:
100,10
100,10
制 作:方 斌说明:
1、在定义指针变量时,还未规定它指向哪一个变量,此时不能用 *运算符访问指针。只有在程序中用赋值语句具体规定后,才能用 *运算符访问所指向的变量。例:
int a;
int *p; (未规定指向哪个变量 )
*p = 100;
这种错误称为访问悬挂指针
( suspeded pointer)
int a;
int *p; (未规定指向哪个变量 )
p = &a; (规定指向 a)
*p = 100;
正确制 作:方 斌
2、区分,*运算符在不同场合的作用,编译器能够根据上下文环境判别 *的作用。
int a,b,c;
int * p; ( *表示定义指针)
p = &a;
*p = 100; ( *表示指针运算符)
c = a * b; ( *表示乘法运算符)
3、区分 *运算符的以下用法:
int a ;
int *p = &a; /*定义指针变量时指定初值,是为 p指定初值 */
*p = 100; /*给指针 p所指向的变量赋值,这里是给变量 a赋值 */
关于 *和 &运算符的其它细节,自学教材制 作:方 斌
[例 10.2] 输入 a和 b两个整数,按先大后小的顺序输出 a和 b。
void main(void)
{
int *p1,*p2,*p,a,b;
scanf("%d,%d",&a,&b);
p1 = &a; p2 = &b;
if (a < b)
{ p = p1; p1 = p2; p2 = p; }
printf("a=%d,b=%d\n",a,b);
printf("max=%d,min=%d\n",*p1,*p2);
}
制 作:方 斌程序分析:
p1 = &a; p2 = &b;
if (a<b) {p=p1; p1=p2; p2=p;}
如果 a<b则交换 p1和 p2的值。
该例不交换变量 a,b的值,而是交换指针
p1,p2的值,即交换地址。
p1指向变量 a,p2指向变量 b
即 p1的值是变量 a的地址,p2
的值是变量 b的地址
printf("max=%d,min=%d\n",*p1,*p2);
/*输出 p1和 p2所指向变量的值 */
运行结果:假设输入 3 8
则输出为,a=3,b= 8
max=8,min=3
制 作:方 斌三、指针变量作为函数的参数
[例 10.3] 题目要求同 [例 10.2],输入 a和 b两个整数,按先大后小的顺序输出 a和 b。
void swap(int *p1,int *p2) /*交换指针 p1,p2所指向的变量的值 */
{ int p;
p = *p1;
*p1 = *p2;
*p2 = p;
}
void main(void)
{
int a,b;
int *pointer_1,*pointer_2;
scanf("%d%d",&a,&b);
pointer_1 = &a; pointer_2 = &b;
if(a<b) swap(pointer_1,pointer_2);
/*另一种调用 swap()的形式是,if(a<b) swap(&a,&b); */
printf("\n%d,%d\n",a,b);
}
运行结果:
假设输入 5 9
则输出为,9,5
制 作:方 斌
1、程序执行过程的说明,
执行
pointer_1 = &a;
pointer_2 = &b;后
pointer_1和 pointer_2分别指向 a和 b。
调用函数
swap(pointer_1,pointer_2),
生成两个形参 p1和 p2。实参 pointer_a的值传送给形参 p1,因此 p1也指向 a。
同理,p2指向 b。
制 作:方 斌在 swap()函数内,把 *p1和 *p2的值进行交换,*p1是变量 a,*p2是变量 b,即把 a和 b的值进行交换。
函数 swap()调用结束后,形参 p1、
p2被释放,main中得到的 a和 b是已经被交换的值。
制 作:方 斌
2、使用指针变量,应注意避免指针悬挂。
void swap(int *p1,int *p2)
{ int *p;
*p = *p1;
*p1 = *p2;
*p2 = *p;
}
原因是 p没有指向任何变量即 p没有初值制 作:方 斌
3、函数 swap()的形参是指针变量,两种调用方式:
swap(pointer_1,pointer_2); /*传值,这里的值是一个地址 */
swap(&a,&b); /*传地址 */
上述两种方式均把变量 a和 b的地址传送给形参,均能实现交换 a和 b的值。
只有函数 swap()知道变量 a和 b的地址,才能改变其值(交换)。如果把
swap()设计为下面的形式,不能实现 a和 b的值交换(函数不知道 a,b的地址)。
void swap(int x,int y) /* 该函数交换形参的值 */
{ int t;
t = x;
x = y;
y = t;
}
该函数交换形参的值,不能实现实参值的交换,因为在 C语言中,实参和形参之间使用“传值法”,数据只能单向由实参传到形参。形参值的变化不影响实参。
制 作:方 斌
4、以指针变量作函数的参数,实参和形参之间仍然使用“传值法”,数据只能单向由实参传到形参。形参值的变化不影响实参,即,不能改变指针变量本身的值。但可以改变指针变量所指向的变量的值,例、
swap(pointer_1,pointer_2);
swap(&a,&b);
因此,下面的程序也不能达到交换的目的。
不能达到交换的原因,swap()函数交换形参(指针变量)本身,而不是交换指向的变量。
void swap(int *p1,int *p2)
{ int *p;
p = p1;
p1 = p2;
p2 = p;
}
/*交换形参的值 */
void main(void)
{
int a,b;
int *pointer_1,*pointer_2;
scanf("%d%d",&a,&b);
pointer_1 = &a; pointer_2 = &b;
if (a<b) swap(pointer_1,pointer_2);
printf("\n%d,%d\n",*pointer_1,
*pointer_2);
}
制 作:方 斌
[例 10.4] 输入 a,b,c三个整数,按大小顺序输出。
void main(void)
{
void swap(int *,int *);
void exchange(int *,int *,int *);
int a,b,c,*p1,*p2,*p3;
scanf("%d,%d,%d",&a,&b,&c);
p1 = &a; p2 = &b; p3 = &c;
exchange(p1,p2,p3);
printf("\n%d,%d,%d\n",a,b,c);
}
void exchange(int *q1,int *q2,int *q3)
{ if (*q1 < *q2) swap(q1,q2);
if (*q1 < *q3) swap(q1,q3);
if (*q2 < *q3) swap(q2,q3);
}
void swap(int *pt1,int *pt2)
{ int p;
p = *pt1;
*pt1 = *pt2;
*pt2 = p;
}
制 作:方 斌
10.3 数组的指针指针可以指向数组和数组元素,当一个指针指向数组后,对数组元素的访问,既可以使用数组下标,也可以使用指针。并且,用指针访问数组元素,程序的效率更高(用下标访问数组元素程序更清晰)。
一、指向数组元素的指针变量指向数组元素的指针变量,其类型应与数组元素相同,例、
int a[10]; /* 元素为整型 */
float b[10] ; /* 元素为实型 */
int *p; /* 可以指向数组 a的元素 */
int *pf; /* 可以指向数组 b的元素 */
为了让指针 p指向数组 a,应把数组 a的地址赋给指针变量 p。
在前面我们已经学过:数组名 a表示该数组在内存的起始地址。可以用地址运算符 &获得某个元素的地址。如 &a[2]获得元素 a[2]的地址。第一个元素 a[0]的地址 &a[0]即为数组 a的起始地址。
因此,以下语句均使指针 p指向数组 a:
p = &a[0];
p = a; /* 把数组 a的起始地址赋给 p,不是把数组的全部元素赋给 p */
制 作:方 斌二、通过指针引用数组元素当使指针 p指向数组 a后,可以用指针 p访问数组的各个元素。
如果指针 p指向数组 a(指向数组的第一个元素
a[0]),则,p+1指向下一个元素 a[1],注意不是将 p值简单加 1。如果数组元素是整型,p+1表示 p的地址加 2;如果数组元素是实型,p+1表示
p的地址加 4;如果数组元素是字符型,p+1表示
p的地址加 1。 p+i指向元素 a[i]。可以使用 *(p+i)
访问元素 a[i]。另外:
1,p+i也可以记作 a+i。指向元素 a[i]。
2、指向数组的指针变量也可以带下标,如,p[i]
与 *(p+i)等价,表示元素 a[i]。
制 作:方 斌
[例 10.5] 输出数组的全部元素。(设 10个元素,整型)。
访问各元素有三种方法:
1、下标法(常用,很直观)
void main(void)
{
int a[10];
int i;
for(i=0;i<10;i++)
scanf("%d",&a[i]);
printf("\n");
for(i=0;i<10;i++)
printf("%d ",a[i]);
}
制 作:方 斌
2、用数组名计算数组元素的地址。效率与下标法相同,不常用
void main(void)
{
int a[10];
int i;
for(i=0;i<10;i++)
scanf("%d",&a[i]);
printf("\n");
for(i=0;i<10;i++)
printf("%d ",*(a+i));
}
注意,a是地址常量,不可以使用 a++等形式改变它的值制 作:方 斌
3、用指针访问各元素。(常用,效率高)
void main(void)
{
int a[10];
int *p,i;
for(i=0;i<10;i++)
scanf("%d",&a[i]);
printf("\n");
for(p=a;p<(a+10);p++) /* p++使 p指向下一个元素 */
printf("%d ",*p);
}
注意:在此 p是变量,可以使用 p++等形式改变它的值制 作:方 斌使用指针指向数组,应注意以下问题:
1、若指针 p指向数组 a,虽然 p+i与 a+i,*(p+i)与 *(a+i)意义相同,
但仍应注意 p与 a的区别 (a代表数组的首地址,是不变的; p是一个指针变量,可以指向数组中的任何元素 ),例
for(p=a; a<(p+10); a++) printf("%d",*a)
因为 a代表数组的首地址,是常量,不变的,所以 a++不合法
2、指针变量可以指向数组中的任何元素,注意指针变量的当前值。
制 作:方 斌
[例 10.6] 输出数组 a的 10个元素。
比较下面两个程序:
/*错误的程序 */
void main(void)
{
int *p,i,a[10];
p = a;
for(i=0;i<10;i++)
scanf("%d",p++);
printf("\n");
for(i=0;i<10; i++,p++)
printf("%d",*p);
}
/*正确的程序 */
void main(void)
{
int *p,i,a[10];
p = a;
for(i=0;i<10;i++)
scanf("%d",p++);
printf("\n");
p = a;
for(i=0;i<10; i++,p++)
printf("%d",*p);
}
制 作:方 斌错误原因分析:
3,使用指针时,应特别注意避免指针访问越界。在上例中,第二次
for循环,p已经越过数组的范围,但编译器不能发现该问题。避免指针访问越界是程序员自己的责任。
制 作:方 斌
4、指针使用的几个细节:
设指针 p指向数组 a(即 p=a),则:
① p++ (或 p += 1),则 p指向下一个元素。
② *p++,相当于 *(p++)。因为,*和 ++同优先级,而 ++是右结合运算符。
③ *(p++)与 *(++p)的作用不同。
*(p++):先取 *p,再使 p加 1。
*(++p):先使 p加 1,再取 *p。
④ (*p)++表示,p指向的元素值加 1。
⑤ 如果 p当前指向数组 a的第 i个元素,则:
*(p--)相当于 a[i--],先取 *p,再使 p减 1。
*(++p)相当于 a[++i],先使 p加 1,再取 *p。
*(--p)相当于 a[--i],先使 p减 1,再取 *p。
制 作:方 斌三、数组名作函数参数数组名代表数组首地址,因此,它作实参在函数调用时,是把数组首地址传送给形参。这样,实参数组和形参数组共占同一段内存区域。从而在函数调用后,实参数组的元素值可能会发生变化。
[例 9.7] 将数组 a中 n个元素按相反顺序存放。
算法,a[0]与 a[n-1]交换,a[1]与 a[n-2]交换,.....,a[(n-1)/2]与
a[n-int((n-1)/2)]交换。
实现:用 i,j作元素位置变量,开始
i=0,j=n-1。将 a[i]与 a[j]
交换,然后 i加 1,j减
1,直到 i=(n-1)/2。
制 作:方 斌
void main(void)
{
void inv(int [],int);
static int i,a[10] = {3,7,9,11,0,6,7,5,4,2};
printf("the original array:\n");
for(i=0; i<10; i++) printf("%d ",a[i]);
printf("\n");
inv(a,10);
printf("the array hans been inverted:\n");
for(i=0; i<10; i++) printf("%d ",a[i]);
printf("\n");
}
程序:
/* 形参是数组 */
void inv(int x[],int n)
{
int t,i,j,m=(n-1)/2;
for(i=0; i<=m; i++)
{ j = n - 1 - i;
t = x[i];
x[i] = x[j];
x[j] = t;
}
}
制 作:方 斌函数 inv()可以用指针作形参,运行情况与用数组作形参相同。
void inv(int *x,int n)
{
int *p,t,*i,*j,m=(n-1)/2;
i = x; /* 指针 i指向数组第一个元素 */
j = x + n - 1; /* 指针 j指向数组最后一个元素 */
p = x + m; /* 指针 p指向数组中间一个元素 */
for(; i<=p; i++,j--)
{ t = *i; *i = *j; *j = t; }
}
制 作:方 斌
[例 10.8] 从 10个数中找出其中最大值和最小值。只找出其中最大值和最小值,
不能改变元素的排列顺序)。
方法 1、实参和形参均用数组变量。
int max,min; /* 全局变量,最大值和最小值 */
void max_min_value(int array[],int n)
{
int *p,*array_end; /* p是数组元素指针 */
array_end = array + n; /* 指向数组尾 */
max = min = *array; /* 第一个元素 array[0] */
for(p=array+1; p<array_end; p++) /* p++指向下一个元素 */
if (*p > max) max = *p;
else if (*p < min) min = *p;
}
void main(void)
{
int i,number[10];
printf("enter 10 data\n");
for(i=0;i<10;i++) scanf("%d",&number[i]);
max_min_value(number,10);
printf("\nmax=%d,min=%d\n",max,min);
}
制 作:方 斌方法 2、形参和实参均使用指针变量。
int max,min; /* 全局变量,最大值和最小值 */
void max_min_value(int *array,int n)
{ int *p,*array_end; /* p是数组元素指针 */
array_end = array + n; /* 指向数组尾 */
max = min = *array; /* 第一个元素 array[0] */
for(p=array+1; p<array_end; p++) /* p++指向下一个元素 */
if (*p > max) max = *p;
else if (*p < min) min = *p;
}
void main(void)
{ int i,number[10],*p;
p = number; /* 指针 p指向数组 number首地址 */
printf("enter 10 data\n");
for(i=0;i<10;i++) scanf("%d",&number[i]);
printf("the 10 data:\n");
for(p=number,i=0; i<10; i++,p++) printf("%d ",*p);
p = number; /* for循环后,p指向数组尾,因此应为 p重新赋值 */
max_min_value(p,10);
printf("\nmax=%d,min=%d\n",max,min);
}
制 作:方 斌小结:数组作函数的参数,实参和形参之间传送数组的首地址,首地址可以用指针表示,也可以用数组名表示,
因此,实参和形参有以下四种组合情况。
组合情况 实参 形参
1 数组名 数组名
2 数组名 指针
3 指针 指针
4 指针 数组名详情请自行阅读教材相关内容制 作:方 斌四、多维数组的指针二维数组
static int a[3][4] =
{{1,3,5,7},{9,11,13,15},{17,19,21,23}};
理解为,有三个元素 a[0],a[1],a[2],每一个元素代表一行,每一个元素是一个包含 4个元素的数组。
数组名 a代表整个二维数组的首地址,也是元素 a[0][0]的地址,同时代表第一行元素的首地址 (a[0])。
a+1表示第二行元素的首地址 a[1],也是元素 a[1][0]的地址。
a+2表示第三行元素的首地址 a[2],也是元素 a[2][0]的地址。
制 作:方 斌设数组的首地址是 2000,则有 a等于
2000。
第一行 4个元素,占 8字节,因此第二行的首地址是 2008,即 a+1等于
2008。
第二行 4个元素,占 8字节,因此第三行的首地址是 2016,即 a+2等于
2016。
由于把 a[0],a[1],a[2]看成一维数组,它们代表各自数组的首地址,即:
a[0]~ &a[0][0] (~表示“相当于” )
a[1]~ &a[1][0]
a[2]~ &a[2][0]
制 作:方 斌根据一维数组的表示方法,有:
a[0]+1:表示一维数组中第二个元素的地址,~ &a[0][1]
a[0]+2,~ &a[0][2]
a[1]+1,~ &a[1][1];
综上所述,二维数组 a的地址用下图说明(见教材):
制 作:方 斌已知某元素的指针 (即地址 )后,可以用 *运算符访问该元素。例如
int a[3][4];
int (*p)[4]=a; 对于元素 a[1][2]其地址和元素的对应关系有:
地址 元素
&a[1][2] a[1][2]
*(a+1)+2 *(*(a+1)+2)
a[1]+2 *(a[1]+2)
*(p+1)+2 *(*(p+1)+2)
p[1]+2 *(p[1]+2)
…… ……
制 作:方 斌说明,
( 1) 指针变量的值是可以改变的,所以必须注意其当前值,否则容易出错 。
( 2) 指向数组的指针变量,可以指向数组以后的内存单元,虽然没有实际意义 。
( 3) 对指向数组的指针变量 ( px和 py) 进行算术运算和关系运算的含义
1) 可以进行的算术运算,只有以下几种:
px± n,px++/++px,px--/--px,px-py
·px± n:将指针从当前位置向前 ( +n) 或回退 ( -n) n个数据单位,而不是 n个字节 。 显然,px++/++px和 px--/--px是 px± n的特例 ( n=1) 。
·px-py:两指针之间的数据个数,而不是指针的地址之差 。
(4) 关系运算表示两个指针所指地址之间,位置的前后关系:前者为小,后者为大 。
例如,如果指针 px所指地址在指针 py所指地址之前,则 px<py的值为真 (1)。
制 作:方 斌在程序运行过程中,数组的大小是不能改变的 。 这种数组称为静态数组 。 静态数组的缺点是:对于事先无法准确估计数据量的情况,无法做到既满足处理需要,又不浪费内存空间 。
所谓 动态数组是指,在程序运行过程中,根据实际需要指定数组的大小 。
在 C语言中,可利用内存的申请和释放库函数,以及指向数组的指针变量可当数组名使用的特点,来实现动态数组 。
动态数组的本质是,一个指向数组的指针变量 。
补充内容 动态数组的实现制 作:方 斌
1)库函数 malloc()
·用法,void *malloc(unsigned size)
·功能:在内存的动态存储区分配长度为 size的连续空间。
·返回值:若申请成功,则返回新分配内存块的起始地址;否则,返回 NULL。
malloc()函数的返回值是一个无类型指针,其特点是可以指向任何类型的数据。但在实际使用 malloc()函数时,必须将其返回值强制转换成被赋值指针变量的数据类型,以免出错。
2)运算符 sizeof()
·格式,sizeof(变量名/类型名 )
·功能:求变量/类型占用的内存字节数(正整数)。例如,在 IBM-PC机上,
sizeof(int)=2,sizeof(float)=4 。
3) free()
·用法,void free(void *ptr)
·功能:释放由 ptr指向的内存块( ptr是调用 malloc() 函数的返回值)。
·返回值:无。
以上函数的函数原型是,alloc.h,stdlib.h。
三个与动态内存分配有关的函数及运算符制 作:方 斌
[案例 10.7] 动态数组的实现。
/*案例代码文件名,AL9_7.C*/
/*程序功能:实现动态数组 */
#include,alloc.h”
#include,stdlib.h”
void main()
{ int *array=NULL,num,i;
printf(“Input the number of element:,);
scanf(“%d”,&num);
/*申请动态数组使用的内存块 */
array=(int *)malloc(sizeof(int)*num);
if (!array) /*相当于 if(array==NULL,内存申请失败:提示,退出 */
{ printf(“out of memory,press any key to quit……”);
exit(0); /*exit():终止程序运行,返回操作系统 */
}
/*以下是申请成功的程序段 */
.....,/*略 */
}
制 作:方 斌如果内存申请成功的程序段示例
/*提示输入 num个数据 */
printf(“Input %d elements:,,num);
for (i=0; i<num; i++) scanf(“%d”,&array[i]);
/*输出刚输入的 num个数据 */
printf(“%d elements are:,,num);
for (i=0; i<num; i++) printf(“%d,”,array[i]);
printf(“\b,); /*删除最后输出的一个,*/
free(array); /*释放由 malloc()函数申请的内存块 */
}
说明,array可以像数组名一样使用,二者不同的是前者是指针变量而后者是常量。即 &array[i]可以写成 array+i,array[i]可以写成 *(array+i)
程序运行情况:
Input the number of element,3←┘
Input 3 elements,1 2 3←┘
3 elements are,1,2,3
制 作:方 斌程序说明:
( 1) array=(int *)malloc( sizeof(int) * num );
malloc( sizeof(int) * num )表示要申请 sizeof(int) * num 个字节的内存空间,(int *)表示将申请成功的内存空间首地址强制转换为 (int *)
思考:在该语句中,使用 sizeof(int)求出 1个 int型数据占用的内存字节数,
而不是使用常量,2”,为什么?
( 2) scanf(“%d”,&array[i]); 和 printf(“%d,”,array[i]);
将指向数组的指针变量当作数组名使用,所以就必须按引用数组元素的语法规则来使用。
( 3) printf(“\b,);
“\b,在该语句中的作用是,使光标定位到最后一个数据后的分隔符“,”
上,然后再输出一个空格,以达到删除之目的。
(4) free(array); 原则上,使用 malloc()函数申请的内存块,操作结束后,应及时使用 free()函数予以释放。尤其是循环使用 malloc()函数时,
如果不及时释放不再使用的内存块,很可能很快就耗尽系统的内存资源,
从而导致程序无法继续运行。
制 作:方 斌10
.4 字符串的指针一、字符串的表现形式
C语言中,有两种方式可以实现字符串:字符数组、字符指针。
字符数组 [例 9.16]
main()
{ static char string[ ] ="I love China!";
printf("%s\n",string);
}
string是数组名,代表字符数组的首地址。数组可以用下标访问,也可以用指针访问。例,string[4]表示一个元素其值是字符 v,也可以用 *(string+4)来访问,
string+4是指向字符 v的指针。
字符指针 [例 9.17]
main()
{ char *string ="I love China!";
printf("%s\n",string);
}
string是一个指针变量,,I love China!”是一个字符串常量。语句:
char *string = "I love China!"; 等价于 char * string;
string = "I love China!";
它把字符串常量的首地址赋给指针 string.不能理解为把字符串常量赋值给指针变量。 *string ="I love China!";
制 作:方 斌从以上两个例子中,可以看到:
1、字符数组和字符指针的概念不同。
2、字符指针指向字符串,而 C语言中,字符串按数组方式处理,
因此,字符数组和字符指针的访问方式相同。例如,均可以使用
%s格式控制符进行整体输入输出。但应注意,如果不是字符数组,
而是整型、实型等数字型数组,不能用 %s,只能逐个元素处理。
制 作:方 斌
[例 10.18] 将字符串 a复制到字符串 b。
void main(void)
{
char a[] = "Iam a boy.";
char b[20];
int i;
for(i=0; *(a+i) !='\0'; i++) *(b+i) = *(a+i);
*(b+i) = '\0';
printf("string a is,%s\n",a);
printf("string b is:");
for(i=0; b[i] !='\0'; i++) printf("%c",b[i]);
printf("\n");
}
制 作:方 斌
[例 10.19] 将字符串 a复制到字符串 b。(用指针处理)
void main(void)
{
char a[]="Iam a boy.",b[20],*p1,*p2;
int i;
p1 = a; p2 = b;
for(; *p1 != '\0'; p1++,p2++) *p2 = *p1;
*p2 = '\0';
printf("string a is,%s\n",a);
printf("string b is:");
for(i=0; b[i] !='\0'; i++) printf("%c",b[i]);
printf("\n");
}
制 作:方 斌二、字符串指针作函数参数将一个字符串从一个函数传递到另一个函数,可以使用传地址的方式,
即用字符数组名或字符指针变量作参数。
[例 10.20] 用函数调用实现字符串的复制。
( 1)用字符数组作参数。
void copy_string(char from[],char to[])
{ int i=0;
while(from[i] != '\0')
{ to[i] = from[i]; i++; }
to[i] = '\0';
}
void main(void)
{ char a[] = "I am a teacher.";
char b[] = "you are a student.";
printf("string_a =%s\n string_b =%s\n",a,b);
copy_string(a,b);
printf("string_a =%s\n string_b =%s\n",a,b);
}
制 作:方 斌
( 2)形参用字符指针。
void copy_string(char *from,char *to)
{
for(; *form != '\0'; from++,to++) *to = *from;
*to = '\0';
}
void main(void)
{ char *a = "I am a teacher.";
char *b = "you are a student.";
printf("string_a =%s\n string_b =%s\n",a,b);
copy_string(a,b);
printf("string_a =%s\n string_b =%s\n",a,b);
}
制 作:方 斌
( 3)对 copy_string函数的几种简化
void copy_string(char *from,char *to)
{
while((*to=*from) !='\0')
{to++; from++}
}
① *to=*from是一个赋值表达式,其值等于 *from。 to++
和 from++分别使指针指向下一个字节。先执行赋值表达式,
再判赋值表达式的值(等于 *from)是否为 '\0',因此,
from串中的结尾字符 '\0'被赋值给 to。
制 作:方 斌
void copy_string(char *from,char *to)
{
while((*to++=*from++) != '\0');
}
② *to++=*from++先执行 *to=*from,再使 to,from分别加 1。
void copy_string(char *from,char *to)
{
while(*from != '\0') *to++ = *from++;
*to = '\0';
}
③ 当遇到 *from='\0'时,不执行赋值运算 *to++=*from++,因此,
最后应加一句 *to='\0'。
制 作:方 斌
void copy_string(char *from,char *to)
{
while(*to++=*from++);
}
④ 与第②种简化相同,当 *from='\0'时,表达式
*to++=*from++的值等于 '\0'(假),结束 while循环。
void copy_string(char *from,char *to)
{
for(; (*to++ =*from++) !=0; );或 for(; *to++
=*from++; );
}
⑤ for循环的结束条件是表达式 *to++ =*from++的值(即
*from的值)等于 '\0'。且 form中的 '\0'已被赋给 to。注意 '\0'的
ASCII码是不是 0。
制 作:方 斌
void copy_string(char from[],char to[])
{
char *p1,*p2;
p1 = from; p2 = to;
while((*p2++=p1++) !='\0')
}
⑥ 形参用数组,使用局部指针变量指向形参数组。
制 作:方 斌
10.5 函数指针一、用函数指针变量调用函数
C语言中的指针,既可以指向变量(整型、字符型、实型、数组等),也可以指向程序的代码(如函数)。
一个函数在编译时被分配一个入口地址(第一条指令的地址),这个入口地址称为函数的指针。如果一个指针变量的值等于函数的入口地址,称为指向函数的指针变量,简称为函数指针。
可以通过函数指针来调用函数。
制 作:方 斌
[例 10.23] 求 a和 b中的大者。
用函数名调用函数 max()
int max(int x,int y); /*定义略 */
void main(void)
{
int a,b,c;
scanf("%d,%d",&a,&b);
c = max(a,b);
printf("a=%d,b=%d,max=%d",a,b,c);
}
制 作:方 斌用函数指针调用函数 max()
int max(int x,int y); /*原型 */
void main(void)
{
int (*p)(int,int);
int a,b,c;
p = max;
scanf("%d,%d",&a,&b);
c = (*p)(a,b);
printf("a=%d,b=%d,max=%d",a,b,c);
}
int max(int x,int y)
{
int z;
if (x>y) z = x;
else z = y;
return z;
}
制 作:方 斌
1、函数指针定义的一般形式:
函数返回值类型 ( *指针变量名)(形参类型 )
即:除函数名用( *指针变量名)代替外,函数指针的定义形式与函数的原型相同。(在函数指针定义中加入形参类型是现代程序设计风格)。
例:
int (*p) (int,int);
仅当形参类型是 int时,可以省略形参类型,一般不要省略。(见例 9.25)
int (*p) ();
2、语句 p=max,把函数 max的入口地址赋给函数指针 p,因此,
c=(*p)(a,b)中,*p就是调用函数 max。
注意:
语句 p=max中,函数名代表函数的入口地址,max后不跟函数参数。
用函数指针调用函数时,应指定实参。
3,(*p)()表示一个指向函数的指针变量,它可以先后指向不同的函数。
4、指向函数的指针变量 p,象 p++,p--,p+n等运算是无意义的。
制 作:方 斌二、函数指针作函数的参数 (不要求,可跳过 )
int f1(int i);
int f2(int i,int j);
int sub( int (*x1)(int),int (*x2)(int,int));
void main(void)
{
sub(f1,f2);
}
int f1(int i)
{
printf("f1,i=%d\n",i);
return i*2;
}
int f2(int i,int j)
{
printf("f2,i=%d,j=%d\n",i,j);
return i+j;
}
int sub( int (*x1)(int),int (*x2)(int,int))
{
int a,b,i=3,j=2;
a = (*x1)(i);
b = (*x2)(i,j);
printf("sub,a=%d,b=%d\n",a,b);
}
制 作:方 斌
[例 10.24] 设一个函数 process,在调用它的时候,每次实现不同的功能。输入 a和 b两个数,第一次调用时找出 a和 b中大者,第二次调用时找出 a和 b中小者,第三次调用时求 a与
b之和。
分析:将求大值、求小值、求和值分别设计为函数 max,min,add。
process使用指针调用这些函数。
int max(int x,int y) ; /* 求大值 */
int min(int x,int y) ; /* 求小值 */
int add(int x,int y) ; /* 求和值 */
void process(int x,int y,int(*fun)(int,int) );
void main(void)
{
int a,b;
printf("enter a and b:");
scanf("%d,%d",&a,&b);
printf("max=");
process(a,b,max);
printf("min=");
process(a,b,min);
printf("sum=");
process(a,b,add);
}
制 作:方 斌
int max(int x,int y)
{
int z;
if (x>y) z = x;
else z = y;
return z;
}
int min(int x,int y)
{
int z;
if (x<y) z = x;
else z = y;
return z;
}
int add(int x,int y)
{
int z;
z = x + y;
return z;
}
void process(int x,int y,int(*fun)(int,int) )
{
int result;
result = (*fun)(x,y);
printf("%d\n",result);
}
制 作:方 斌
[例 10,25]求定积分:
分析:编写一个求定积分的通用函数:
float integral(float (*fun)(float),float a,float b);
其中,a,b表示积分区间,fun是函数指针。
函数 f在区间[ a,b]的定积分公式:
制 作:方 斌需要积分的函数是:
程序,float f1(float x)
{
float f;
f = 1 + x*x;
return f;
}
float f2(float x)
{
float f;
f = 1 + x + x*x + x*x*x;
return f;
}
float f3(float x)
{
float f;
f = x / (1 + x*x);
return f;
}
制 作:方 斌
float integral(float (*fun)(float),float a,float b)
{
float s,h,y;
int n,i;
s = ( (*fun)(a) + (*fun)(b) ) /2.0;
n = 100;
h = (b-a)/n;
for(i=1; i<n; i++) s = s + (*fun)(a+i*h);
y = s * h;
return y;
}
void main()
{
float y1,y2,y3;
y1 = integral(f1,0.0,1.0);
y2 = integral(f2,0.0,2.0);
y3 = integral(f3,0.0,3.5);
printf("y1=%6.2f\ny2=%6.2f\ny3=%6.2f\n",y1,y2,y3);
}
制 作:方 斌
10.6 返回指针的函数一般形式:
类型标识符 * 函数名(参数表)
例、
int * a (int x,int y)
声明一个函数,函数名为 a,其返回值类型是“指向整型的指针”,
函数形式参数为 int x 和 int y。
[例 9.26]有若干学生的成绩(每个学生四门课程),要求用户在输入学生序号(从0开始)后,能输出该学生的全部成绩。
分析:
设计一个指针 pointer指向一个学生的四门成绩
float (*pointer)[4]
制 作:方 斌
pointer是一个指向一维数组的指针。数组元素个数为 4
(四门课程) pointer+1指向下一个学生的成绩。输入学生序号后,使 pointer指向该学生的成绩,然后返回 pointer
指针,
制 作:方 斌程序:
float * search( float (*pointer)[4],int n) ;
void main()
{
static float score[][4] =
{{60,70,80,90},{56,89,67,88},{34,78,90,66} };
float *p;
int i,m;
printf("enter the number of student:");
scanf("%d",&m);
printf("The scores of No.%d are:\n",m);
p = search(score,m); /* 在 score数组中查询 m号学生的成绩 */
/* 查询结果为指向成绩的指针 */
for(i=0; i<4; i++)
printf("%5.2f\t",*(p+i));
}
float * search( float (*pointer)[4],int n)
{
float *pt; /* pt是指向实数的指针,pointer是指向数组的指针 */
pt = *(pointer+n); /* pt = (float *)(pointer + n) */
return pt;
}
制 作:方 斌
[例 10.27]对上例中的学生,找出有不及格成绩的学生及其学号。
分析:上例中,search函数返回学生成绩的首地址。本例,用返回地址区分学生成绩中有无不及格课程,若有不及格课程时,仍返回学生成绩的首地址;若无不及格课程,则返回学生成绩的末地址(等于下一个学生的首地址)。
制 作:方 斌
float * search( float (*pointer)[4] );
void main()
{
static float score[][4] =
{{60,70,80,90},{56,89,67,88},{34,78,90,66} };
float *p;
int i,j;
for(i=0; i<3; i++) /* 三个学生 */
{
p = search(score+i);
if (p == *(score+i)) /* p指向 i号学生成绩首地址,该生有不及格课程 */
{
printf("No.%d scores:\t",i); /* 显示该生的四门课程成绩 */
for(j=0; j<4; j++) printf("%5.2\t",*(p+j));
printf("\n");
}
}
}
制 作:方 斌
float * search( float (*pointer)[4] )
{
int i;
float *pt;
pt = *(pointer + 1); /*先设 pt指向成绩末地址(等于下一个学生成绩首地址),
即先假设无不及格成绩 */
for(i=0; i<4; i++) /* 查四门课程中有无不及格成绩 */
{
if ( *(*pointer+i)<60 ) pt = *pointer; /*有不及格课程,pt指向成绩首地址 */
return pt;
}
}
制 作:方 斌
10.7 指针数组和指向指针的指针
一、指针数组
概念:指针数组是一个数组,该数组中的每一个元素是指针变量。
形式:类型标识符 *数组名[数组元素个数]
例子,int * p[4];
定义一个指针数组,数组名 p,有 4个元素,每一个元素是指向整型变量的指针。
区分,int (*p)[4] (指向数组的指针)
定义一个指针变量,它指向有 4个元素的一维数组。
用途:处理多个字符串。
字符串本身是一维数组,多个字符串可以用二维数组来处理,
但会浪费许多内存。用指针数组处理多个字符串,不会浪费内存。
制 作:方 斌二维数组
char name[][16],
浪费许多内存指针数组
char *name[],
不浪费内存制 作:方 斌
[例 10.28]、将若干字符串按字母顺序(由小到大)输出。
#include "stdio.h"
#include "string.h"
void sort(char *name[],int n); /* 排序函数原型 */
void print(char *name[],int n); /* 输出函数原型 */
void main()
{
static char *name[] ={"Follow me","BASIC","Great Wall",
"FORTRAN","Computer Design"};
int n = 5;
sort(name,n); /* 排序 */
print(name,n); /* 输出 */
}
制 作:方 斌
void sort(char *name[],int n) /* 冒泡法排序 */
{
char *temp;
int i,j,k;
for(i=0; i<n-1; i++) /* n个字符串,外循环 n-1次 */
{
k = i;
for(j=i+1; j<n; j++) /* 内循环 */
if (strcmp(name[k],name[j]) > 0) k = j;
/*比较 name[k]与 name[j]的大小,较小字符串的序号保留在 k中 */
if (k != i)
{ /*交换 name[i]与 name[k]的指向 */
temp = name[i];
name[i] = name[k];
name[k] = temp;
}
}
}
制 作:方 斌
void print(char *name[],int n)
{
int i;
for (i=0; i<n; i++) printf("%s\n",name[i]);
}
初始状态各指针元素的指向排序以后各指针元素的指向。
注:排序交换的不是字符本身,
而是交换各指针的指向。
制 作:方 斌二、指向指针的指针一级指针二级指针
n级指针定义举例:
char * * p;
p是一个指向指针的指针。被 p指向的指针指向字符变量。
例,char **p; char * pch;
char ch=?A?;
则,pch=&ch; p=&pch; 是合法的
2000

3010

2000

A

ch
pch
p
3010
3410
制 作:方 斌使用举例:
字符指针数组 char *name[]指向字符串。
定义指向指针的指针 p,char **p,使其指向 name。
p = name + 2; /*p指向 name[2]*/
printf("%o\n",*p); /* 以八进制形式输出 name[2]的值,
它是字符串,Great Wall”的地址 */
printf("%s\n",*p); /* 输出 name[2]指向的字符串 */
制 作:方 斌
[例 10.29]
void main()
{
static char *name[] ={"Follow me","BASIC","Great Wall",
"FORTRAN","Computer Design"};
char **p;
int i;
for(i=0; i<5; i++)
{
p = name + i;
printf("%d %d %s\n",i,*p,*p);
}
} 输出:
0 414 Follow me
1 424 BASIC
2 430 Great Wall
3 441 FORTRAN
4 449 Computer Design
制 作:方 斌例:
int a[5]={1,3,5,7,9};
int *p;
int **pp;
p=a;
pp=&p;
则访问数组 a的元素 a[i]就有如下方式:
(1) a[i]
(2) *(p+i)
(3) *(a+i)
(4) *(*pp+i)
数组元素 a[i]的地址有如下表示方式:
(1) &a[i]
(2) p+i
(3) a+i
(4) *pp+i
a(2000)
2000

9
7
5
3
1

a[0]
pp
3010 p
a[1]
a[2]
a[3]
a[4]


3420 3010
制 作:方 斌
P252 例 10.29
void main(void)
{
static int a[5]={1,3,5,7,9};
int *num[5]
for(i=0;i<5;i++)
num[i]=&a[i]; /*或 num[i]=a+i*/
int **p,i;
p=num;
printf("\n");
for(i=0;i<5;i++)
{
printf("%5d",**p);
p++;
}
}
a?2000

9
7
5
3
1

a[0]
num?3010
p
a[1]
a[2]
a[3]
a[4]

2002
2004
2006
2008
num[0]
num[1]
num[2]
num[3]
num[4]
2000
2002
2004
2006
2008
3010

3012
3014
3016
3018
3600输出结果为:
1 3 5 7 9
制 作:方 斌三,main()函数的参数运行程序的命令行中,可以包含参数,例、
命令名 参数 1 参数 2,...,参数 n
例如,“命令名”是可执行文件 file1.exe,执行该命令时包含两个字符串参数:
file1 China Beijing
在源程序 file1.c中,用 main()函数的参数来表示命令的参数,
main(int argc,char *argv[])
其中,argc表示命令行参数的个数(包括命令名),指针数组 argv用于存放参数(包括命令名),上例中:
argc = 3;
argv[0] ="file1.exe",argv[1] ="China",argv[2] = "Beijing"
制 作:方 斌例、(设文件名是 file1.c)
main(int argc,char *argv[])
{
while(argc>1)
{
++argv;
printf("%s\n",*argv);
--argc;
}
}
输入,file1 China Beijing
输出,China
Beijing
该程序可改写为:
main(int argc,char *argv[])
{
while( argc--> 1) printf("%s\n",*++argv);
}
制 作:方 斌例,echo(参数回送)命令的程序。
main(int argc,char *argv[])
{
while(--argc>0)
printf("%s%c",*++argv,(argc>1) ':'\n');
}
或写为:
main(int argc,char *argv[])
{
int i;
for(i=1; i<argc; i++)
printf("%s%c",argv[i],(i<argc-1) ':'\n');
}
制 作:方 斌
[案例 10.13] 用同一程序实现文件的加密和解密。
约定:程序的可执行文件名为 lock.exe,其用法为:
lock +|- <被处理的文件名 >,其中,+”为加密,,-”为解密。
/*案例代码文件名,AL9_13.C*/
void main(int argc,char *argv[])
{ char c;
if (argc != 3) printf("参数个数不对! \n");
else
{ c=*argv[1]; /*截取第二个实参字符串的第一个字符即 *(*argv[1]+0)*/
switch(c)
{ case '+',/*执行加密 */
{ /*加密程序段 */
printf("执行加密程序段。 \n");
}
break;
case '-',/*执行解密 */
{ /*解密程序段 */
printf("执行解密程序段。 \n");
}
break;
default,printf("第二个参数错误! \n");
}
}
}
制 作:方 斌
10.8 指针使用小结一、有关指针的数据类型定义 含义
int i; 定义整型变量 i
int *p; p是指向整型数据的指针变量
int a[n]; 定义数组 a,元素类型为 int,元素个数是 n
int *p[n]; p是指针数组,包含 n个指针,每一个指针可以指向整型数据
int (*p)[n]; p是指向数组的指针,数组有 n个整型数
int f(); f是函数,返回值是 int
int *p(); p是函数,返回值是指针,该指针指向整型数据
int (*p)(); p是函数指针,所指向的函数返回整型数据
int **p; p是指针,指向一个指向整型数据的指针制 作:方 斌二、指针运算小结
1、指针变量加 /减运算
p++,p--,p+i,p-i,p+=i,p-=i
加 1表示指向下一个数据。
制 作:方 斌
2、指针变量赋值
p = &a; 变量 a的地址赋给 p,即指针 p指向 a
p = array; 数组 array首地址赋给 p
p = &array[i]; 数组元素 array[i]的地址赋给 p
p = max; 函数 max的入口地址赋给 p
p1 = p2; 指针 p2的值赋给指针 p1,即 p1,p2所指的数据相同注意:
p = 1000;
i = p;
int i;
int *p = &i;
*p = 100; 使 p指向的数据(即变量 i)等于 100。
制 作:方 斌
char *string = "I love China!"; 使 p指向字符串制 作:方 斌
3、空指针 p = NULL;
空指针 p=NULL表示 p不指向任何数据。
在 stdio.h中,NULL被定义为 0:
即 #define NULL 0
习惯上,不使用 p = 0
而使用 p = NULL
指针变量 p可以与 NULL作比较,例、
if (p == NULL),..
注意:空指针不指向任何数据,与 p未赋值不同。当 p未赋值时,
其值是不确定的,而空指针的值是确定数 0。
制 作:方 斌
4、指针变量相减。
当 p1,p2指向同一个数组的元素,指针相假 p2-p1等于 p1,p2
间的元素个数。
注意:指针相加无意义。
5、两个指针的比较当 p1,p2指向同一个数组的元素时,可以比较,如,p1 > p2,
这时比较的是位置关系。若 p1,p2不是指向同一个数组的元素,比较无意义。
制 作:方 斌三、空类型指针 void *
void *p,表示 p是空类型指针,它可以指向任何数据类型。
空类型指针与其他类型指针之间赋值时,应进行强制类型转换,
例、
char *p1;
void *p2;
p1 = (char *)p2;
p2 = (void *)p1;
本章作业
10.1 10.2 10.3 10.5 10.8 10.9 10.11 10.13 10.14