第 8章 指针
C程序设计中使用指针可以,
使程序简洁、紧凑、高效
有效地表示复杂的数据结构
动态分配内存
得到多于一个的函数返回值
8.1 指针类型和指针变量
8.2 指针和函数
8.3 指针与数组
8.4 链表
8.1 指针类型和指针变量地址和指针的概念指针和变量的定义指针变量的使用
§ 8.1 指针类型和指针变量程序中,int i;
float k;
内存中每个字节有一个编号 -----地址
…...
…...
2000
2001
2002
2005
内存
0
2003
i
k
编译或函数调用时为其分配内存单元变量 是对程序中数据存储空间的抽象
8.1.1 地址和指针的概念
1、地址的概念
…...
…...
2000
2004
2006
2005
整型变量 i
10
变量 i_pointer
2001
2002
2003
2,3、指针类型与指针变量
指针类型:地址也是数,这类数据就是 ~
指针变量:专门存放地址的变量叫 ~
2000
指针指针变量变量的 内容变量的 地址指针变量变量变量地址 (指针 )
变量值指向 地址存入指针变量
&与 *运算符
含义含义,取变量的地址单目运算符优先级,2
结合性,自右向左含义,取指针所指向变量的内容单目运算符优先级,2
结合性,自右向左
两者关系:互为 逆运算
理解
i_pointer-----指针变量,它的内容是地址量
*i_pointer----指针的 目标变量,它的内容是数据
&i_pointer---指针变量占用内存的地址
2000 10
i_pointer *i_pointer
&i_pointer
i
i_pointer &i &(*i_pointer)
i *i_pointer *(&i)
i_pointer = &i = &( i_pointe
i = *i_pointer = *(&i)
…...
…...
2000
2004
2006
2005
整型变量 i
10
变量 i_pointer
2001
2002
2003
2000
指针变量
直接访问与间接访问
直接访问:按变量地址存取变量值
间接访问:通过存放变量地址的变量去访问变量例 i=3; -----直接访问指针变量
…...
…...
2000
2004
2006
2005
整型变量 i
10
变量 i_pointer
2001
2002
2003
2000
3
例 *i_pointer=20; -----间接访问
20
指针变量
…...
…...
2000
2004
2006
2005
整型变量 i
10
变量 i_pointer
2001
2002
2003
2000
整型变量 k
例 k=i; --直接访问
k=*i_pointer; --间接访问
10
例变量的指针和指向变量的指针变量指针变量 与其 所指向的变量 之间的关系
8.1.2 指针变量的定义
一般形式,[存储类型 ] 数据类型 *指针名;
3
变量 i
2000
i_pointer
*i_pointer
i *i_pointer
&i i_pointer
i=3; *i_pointer=3
3
变量 i
2000
i_pointer
*i_pointer
i *i_pointer
&i i_pointer
i=3; *i_pointer=3
合法标识符指针变量本身的存储类型 指针的目标变量的数据类型 表示定义指针变量 不是‘ *’运算符例 int *p1,*p2;
float *q ;
static char *name;
注意:
1,int *p1,*p2; 与 int *p1,p2;
2,指针变量名是 p1,p2,不是 *p1,*p2
3,指针变量只能指向定义时所规定类型的变量
4、指针变量定义后,变量值不确定,应用前必须先赋值
指针变量的初始化和指针所指的变量一般形式,[存储类型 ]数据类型 *指针名 =初始地址值 ;
赋给指针变量,
不是赋给目标变量例 int i;
int *p=&i; 变量必须 已说明过类型 应一致例 int *p=&i;
int i;
例 int i;
int *p=&i;
int *q=p;
用已初始化指针变量作初值例 main( )
{ int i;
static int *p=&i;
..............
} (?)
不能用 auto变量的地址去初始化 static型指针
8.1.3 指针变量的使用例 main( )
{ int i=10;
int *p;
*p=i;
printf(“%d”,*p);
}
危险!
例 main( )
{ int i=10,k;
int *p;
p=&k;
*p=i;
printf(“%d”,*p);
}
指针变量必须 先赋值,再使用
…...
…...
2000
2004
2006
2005
整型变量 i
10
指针变量 p
2001
2002
2003
随机
零指针与空类型指针
零指针,(空指针 )
定义,指针变量值为零
表示,int * p=0;
p指向地址为 0的单元,
系统保证该单元不作它用表示指针变量值 没有意义
#define NULL 0
int *p=NULL:
p=NULL与未对 p赋值不同
用途,
避免指针变量的非法引用
在程序中常作为 状态 比较例 int *p;
......
while(p!=NULL)
{,..…
}
void *类型指针
表示,void *p;
使用时要进行 强制类型转换例 char *p1;
void *p2;
p1=(char *)p2;
p2=(void *)p1;
表示不指定 p是指向哪一种类型数据的指针变 量
2、使用 指针访问变量(所指的)
main()
{ int a;
int *pa=&a;
a=10;
printf("a:%d\n",a);
printf("*pa:%d\n",*pa);
printf("&a:%x(hex)\n",&a);
printf("pa:%x(hex)\n",pa);
printf("&pa:%x(hex)\n",&pa);
}
运行结果:
a:10
*pa:10
&a:f86(hex)
pa:f86(hex)
&pa:f88(hex)
…...
…...
f86
f8a
f8c
f8b
整型变量 a
10
指针变量 pa
f87
f88
f89 f86
例:
例,输入两个数,并使其从大到小输出
main()
{ 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);
}
运行结果,a=5,b=9
max=9,min=5
…...
…...
指针变量 p1
指针变量 p
2000
2008
2002
2004
2006
指针变量 p2
整型变量 b
整型变量 a5
2006
9
2008
2006
2008
2006
8.2 指针和函数
8.2.1 指针变量作函数参数
8.2.2 返回指针值的函数
8.2.3 指向函数的指针变量
8.2.1 指针变量作为函数参数
swap(int x,int y)
{ int temp;
temp=x;
x=y;
y=temp;
}
main()
{ int a,b;
scanf("%d,%d",&a,&b);
if(a<b) swap(a,b);
printf("\n%d,%d\n",a,b);
}
例 8.5 将数从大到小输出
( 1)下面程序正确吗? …...
…...
2000
2008
200A
2002
2004
2006
5 变量 a变量 b
(main)
9
变量 temp
变量 y
变量 x(swap)
5
5
95
9
COPY
swap(int x,int y)
{ int temp;
temp=x;
x=y;
y=temp;
}
main()
{ int a,b;
scanf("%d,%d",&a,&b);
if(a<b) swap(a,b);
printf("\n%d,%d\n",a,b);
}
值传递
…...
…...
2000
2008
200A
2002
2004
2006
5 变量 a变量 b
(main)
9
运行结果,5,9
swap(int *p1,int *p2)
{ int p;
p=*p1;
*p1=*p2;
*p2=p;
}
main()
{ 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",a,b);
}
…...
2000
2008
200A
2002
2004
2006
200C
200E
2010,..
5
9
整型变量 a
整型变量 b
(main)
指针 pointer_1
指针 pointer_220002002
(swap) 指针 p1
指针 p2
整型 p
5
9
2000
2002
COPY
5
例 8.5 将数从大到小输出
( 2)下面程序正确吗?
swap(int *p1,int *p2)
{ int p;
p=*p1;
*p1=*p2;
*p2=p;
}
main()
{ 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",a,b);
}
…...
2000
2008
200A
2002
2004
2006
200C
200E
2010,..
5
9
整型变量 a
整型变量 b
(main)
指针 pointer_1
指针 pointer_220002002
5
9
例 将数从大到小输出运行结果,9,5
地址传递
swap(int *p1,int *p2)
{ int *p;
*p=*p1;
*p1=*p2;
*p2=*p;
}
main()
{ 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",a,b);
}
运行结果,9,9
编译警告!
结果不对!
int x;
int *p=&x;x;
…...
2000
2008
200A
2002
2004
2006
200C
200E
2010,..
5
9
整型变量 a
整型变量 b
(main)
指针 pointer_1
指针 pointer_220002002
9
9
2000
2002
COPY (swap) 指针 p1
指针 p2
指针 p****
假设 2000
指针变量在使用前必须赋值!
例 8.5 将数从大到小输出
( 3)下面程序正确吗?
swap(int x,int y)
{ int t;
t=x; x=y; y=t;
}
main()
{ 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",a,b);
}
运行结果,5,9
值传递
…...
2000
2008
200A
2002
2004
2006
200C
200E
2010,..
5
9
整型 a
整型 b
(main)
pointer_1
pointer_220002002
9
COPY (swap) 整型 x
整型 b
整型 t55
59
例 8.5 将数从大到小输出
( 4)下面程序正确吗?
运行结果,5,9
swap(int *p1,int *p2)
{ int *p;
p=p1;
p1=p2;
p2=p;
}
main()
{ 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("%d,%d",*pointer_1,*pointer_2);
}
…...
2000
2008
200A
2002
2004
2006
200C
200E
2010,..
5
9
整型 a
整型 b
(main)
pointer_1
pointer_220002002
2000
2002
COPY (swap)
指针 p1
指针 p2
指针 p****2000
地址传递
2000
2002
例 8.5 将数从大到小输出
( 5)下面程序正确吗?
8.2.2 返回指针的函数
(返回值为某指针类型的函数)
函数定义形式:
类型标识符 *函数名 (参数表 );
例 int *f(int x,int y)
{
……
}
例 写一个函数,求两个 int型变量中居于较大值的变量的地址
int *f1(int *x,int *y)
{
if(*x>*y)
return x;
else
return y;
}
main()
{ int a=2,b=3;
int *p;
p=f1(&a,&b);
printf("%d\n",*p);
}
…...
2000
2008
200A
2002
2004
2006
2
3
指针变量 y
指针变量 x(f1)
2002
2000
COPY
变量 a
变量 b
(main)
指针变量 p**
例 写一个函数,求两个 int型变量中居于较大值的变量的地址
int *f3(int x,int y)
{
if(x>y)
return &x;
else
return &y;
}
main()
{ int a=2,b=3;
int *p;
p=f3(a,b);
printf("%d\n",*p);
}
…...
2000
2008
200A
2002
2004
2006
2
3
变量 y
变量 x(f3)
3
2
COPY
变量 a
变量 b
(main)
指针变量 p**
例 写一个函数,求两个 int型变量中居于较大值的变量的地址不能返回 形参 或 局部变量的 地址,思考为什么?
…...
…...
2000
2008
200A
2002
2004
2006
2 变量 a变量 b
(main)
3 指针变量 p
**200A
int *f3(int x,int y)
{
if(x>y)
return &x;
else
return &y;
}
main()
{ int a=2,b=3;
int *p;
p=f3(a,b);
printf("%d\n",*p);
}
例 8.6 输入 a,b,c三个数,调用函数 func,得到其中最大数的地址。
int *func(int x,int y,int z) /*返回地址值的函 */
{ int max;
if(x>=y&&x>=z) return(&x);
if(y>=x&&y>=z) return(&y);
return(&z);
}
main()
{ int a,b,c,*p;
printf("\nPlease input a,b,c:\n");
scanf("%d,%d,%d\n",&a,&b,&c);
p=func(a,b,c);
printf("a=%d,b=%d,c=%d,*p=%d\n",a,b,c,*p);
}
找出错误,并指出原因?
8.2.3 指向函数的指针函数在编译时被分配的入口地址(程序段的存储地址)
称为函数指针,用函数名表示。我们将这一地址存到一个指针变量里去,这个变量就是指向函数的指针变量。
max
…...
指令 1
指令 2?函数指针变量赋值,如 p=max;
函数返回值的数据类型 专门存放函数入口地址可指向返回值类型相同的不同函数
指向函数的指针变量
定义形式,数据类型 (*指针变量名 )();
如 int (*p)();
函数指针变量指向的函数必须有 函数说明
函数调用形式,c=max(a,b);? c=(*p)(a,b);? c=p (a,b);
对函数指针变量 p?n,p++,p--无意义
( )不能省
int (*p)() 与 int *p()不同
【 例 8.7】 编写函数 maxn,完成“输入 n( n<10)个整数,
找出其中最大数来”的功能。
main()
{ int (*p)();
int m,n;
p=maxn;
scanf("%d",&n);
m=(*p)(n);
printf(“n=%d,max =%d\n",n,m);
}
maxn(int n)
{int i,x,max;
scanf("%d",&max);
for(i=1;i<n;i++)
{scanf("%d",&x);
if(max<x)
max=x; }
return(max);
}
有关说明
1、注意指向函数的指针变量定义的格式
2、在给一个指向函数的指针变量赋值时,不用带参数,如,p=max; 而不能 p=max(a,b);
3、只能给指向函数的指针变量赋值同类型的函数。
4、用函数指针变量调用函数时,只需将 (*p)代替函数名即可,实参必须写上(若有参数的话)。
5、指向函数的指针变量可以根据需要指向同类型的不同函数;
6、注意 指向函数的指针变量的操作。
用函数指针变量作函数参数例:用函数指针变量作参数,求最大值、最小值和两数之和 void main()
{ int a,b,max(int,int),
min(int,int),add(int,int);
void process(int,int,int (*fun)());
scanf("%d,%d",&a,&b);
process(a,b,max);
process(a,b,min);
process(a,b,add);
}
void process(int x,int y,int (*fun)())
{ int result;
result=(*fun)(x,y);
printf("%d\n",result);
}
max(int x,int y)
{ printf(“max=”);
return(x>y?x:y);
}
min(int x,int y)
{ printf(“min=”);
return(x<y?x:y);
}
add(int x,int y)
{ printf(“sum=”);
return(x+y);
}
【 例 8.8】 已知契比雪夫多项式的定义如下:
x ( n=1)
2x2-1 ( n=2)
4x3-3x ( n=3)
8x4-8x2+1 ( n=4)
编写程序,从键盘输入整数 n和实数 x,根据 n的值调用不同函数,计算多项式的值 。
main()
{float fn1(float),fn2(float);
float fn3(float),fn4(float);
float x,result;
int n;
do
{printf("Input x and n:\n");
scanf("%f,%d",&x,&n);
} while(n<1||n>4);
switch(n)
{case 1:p=fn1;break;
case 2:p=fn2;break;
case 3:p=fn3;break;
case 4:p=fn4;break;
}
result=(*p)(x);
printf("result=%f",result);
}
float fn1(float z)
{ printf("z=%f",z); return(z);
}
float fn2(float x)
{ float y;
y=2*x*x-1; return(y);
}
float fn3(float x)
{ float y;
y=4*x*x*x-3*x; return(y);
}
float fn4(float x)
{ float y;
y=8*x*x*x*x-8*x*x+1;
return(y);
}
8.3 指针与数组一维数组和指针二维数组和指针字符串和指针数组元素作函数参数数组名作函数参数指针数组及带参数的 main函数
8.3.1 一维数组和指针
1.一维数组元素的地址和一维数组名
a [0]
a [1]
a [2]
a [3]
a [9]
...
整型指针 p &a [0]
p
例,int a[10];
数组元素 a[0]----a[9],都是整型变量,因此都有一个地址,& a[i] (0<=i<=9)
例,int a[10];
数组名 是表示数组 首地 址的 地址常量
8.3.1 一维数组和指针
1.一维数组元素的地址和一维数组名
a[0]
a[1]
a[2]
a[3]
a[9]
...
整型指针 p ****
a设,int a[10],*p;数组名 是表示数组 首地 址的 地址常量对于一维数组 a,数组名 a是数组元素
a[0]的 地 址,即 &a[0]与 a是等值的。属性也相同,都是 int 的地址。
&a[1]是数组元素的地址;
&a[2]是数组元素的地址;
&a[i](0<=i<=9有效 )是数组元素的地址;
a++; a=&a[3];
设有,int i,a[10],*p,p1,p2;
则:
p=&i; (将变量 i地址?p)
p=a; (将数组 a首地址?p)
p=&a [i]; (将数组元素地址?p)
p1=p2; (指针变量 p2值?p1)
不能把一个整数?p,也不能把 p的值?整型变量如 int i,*p;
p=1000; (?)
i=p; (?)
指针变量所指的变量的 数据类型只能是它的基类型
a[0]
a[1]
a[2]
a[3]
a[9]
...
整型指针 p &a[0]
p
(1)可以通过数组名来表示数组元素的地址:
a+1是数组元素 a[1]的地址 ;即,&a[1]
a+i是数组元素 a[i]的地址 ; 即,&a[i]
a( 即 a+0) 是 a[0]的地址即,&a[0]
2、指向一维数组的指针变量设,int a[10],*p=a;
(2)有关指针的运算算术运算:
p?i? p?i?d (i为整型数,d为 p指向的变量所占字节数 )
p++,p--,p+i,p-i,p+=i,p-=i等
若 p1与 p2指向同一数组 中的元素,p1-p2=两指针间元素个数?(p1-p2)/d
p1+p2 无意义例 p指向 float数,则 p+1? p+1?4
例 p指向 int型数组,且 p=&a[0];
则 p+1 指向 a[1]
例 int a[10];
int *p=&a[2];
p++;
*p=1;
例 int a[10];
int *p1=&a[2];
int *p2=&a[5];
则,p2-p1=3;
a[0]
a[1]
a[2]
a[3]
a[4]
a[5]
a[6]
a[7]
a[8]
a[9]
a数组p
p+1,a+1
p+i,a+i
p+9,a+9
1
指针变量的关系运算
若 p1和 p2指向同一数组,则
p1<p2 表示 p1指的元素在前
p1>p2 表示 p1指的元素在后
p1==p2 表示 p1与 p2指向同一元素
若 p1与 p2不指向同一数组,比较无意义
p==NULL或 p!=NULL
( 3)数组元素表示方法
a[0]
a[1]
a[2]
a[3]
a[9]
...
a
a+9
a+1
a+2
地址 元素下标法
a[0]
a[1]
a[2]
a[9]
a[0]
a[1]
a[2]
a[3]
a[9]
...
p
p+9
p+1
p+2
地址 元素指针法
*p
*(p+1)
*(p+2)
*(p+9)
[] 变址运算符
a[i]? *(a+i)
a[i]? p[i]? *(p+i)?*(a+i)
*a
*(a+1)
*(a+2)
*(a+9)
p[0]
p[1]
p[2]
p[9]
a[0]
a[1]
a[2]
a[3]
a[4]
例 数组元素的引用方法
main()
{ int a[5],*pa,i;
for(i=0;i<5;i++)
a[i]=i+1;
pa=a;
for(i=0;i<5;i++)
printf("*(pa+%d):%d\n",i,*(pa+i));
for(i=0;i<5;i++)
printf("*(a+%d):%d\n",i,*(a+i));
for(i=0;i<5;i++)
printf("pa[%d]:%d\n",i,pa[i]);
for(i=0;i<5;i++)
printf("a[%d]:%d\n",i,a[i]);
}
1
2
3
4
5
pa
例 int a[]={1,2,3,4,5,6,7,8,9,10},*p=a,i;
数组元素地址的正确表示:
( A) &(a+1) ( B) a++ ( C) &p ( D) &p[i]?
数组名是 地址常量
p++,p-- (?)
a++,a-- (?)
a+1,*(a+2) (?)
例 void main()
{ int a []={5,8,7,6,2,7,3};
int y,*p=&a[1];
y=(*--p)++;
printf(“%d,,y);
printf(“%d”,a[0]);
}
输出,5 6
p
p 5
8
7
6
2
7
3
0
1
2
3
4
5
6
a
例 注意指针变量的运算
6
main()
{ int i,*p,a[7];
p=a;
for(i=0;i<7;i++)
scanf("%d",p++);
printf("\n");
for(i=0;i<7;i++,p++)
printf("%d",*p);
}
例 注意指针的当前值
p=a;
p
p 5
8
7
6
2
7
3
0
1
2
3
4
5
6
a
p
p
p
p
p
p
指针变量可以指到 数组后 的内存单元
指针变量与一维数组的关系设有,int *p 与 int a[10]
数组名是指针(地址) 常量
若已有 p=a; 即已使 p指向了 a[0];则 p+i 是 阿 a[i]的地址
数组元素的表示方法,下标法 和 指针法,即若,p=a;
则 p[i]? a[i]? *(p+i)? *(a+i)
形参数组 实质上是 指针变量,即 int q[ ]? int *q
在定义指针变量(不是形参)时,不能 把 int *p 写成 int p[];
系统只给 p分配能保存一个指针值的内存区 (一般 2字节);而给 q分配 2*10字节的内存区
8.3.2 二 维数组和指针
1.二维数组元素的地址和二维数组名对于一维数组,
( 1)数组名 a表示数组的首地址,即 a [0]的地址;
( 2)数组名 a 是地址 常量
( 3) a +i是元素 a [i]的地址
( 4) a [i]? *(a +i)
这些同样可以运用于二维数组
a int a[10];
设有,int a[3][4],*p;
对于二维数组:
( 1) a是 数组名,
包含三个元素
a[0],a[1],a[2]
( 2) 每个元素 a[i]
又是一个一维数组,包含 4个元素
a
a+1
a+2
*(*(a+0)+1)
*(a[0]+1)int a[3][4];
a[0]
a[1]
a[2]
2000
2008
2016
2000
2002
2008
2010
2016
2018
a[0][0]
a[0][1]
a[1][0]
a[1][1]
a[2][0]
a[2][1]
a[0][2]
a[0][3]
a[1][2]
a[1][3]
a[2][2]
a[2][3]基类型行指针与列指针
a[0]+1
a[1]+1
a[2]+1
*(a+0)+1
*(a+1)+1
*(a+2)+1
对二维数组 int a[3][4],有
a-----二维数组的首地址,即第 0行的首地址
a+i-----第 i行 的首地址
a[i]? *(a+i)------第 i行第 0列 的元素地址
a[i]+j? *(a+i)+j -----第 i行第 j列 的元素地址
*(a[i]+j)? *(*(a+i)+j)? a[i][j]
a+i=&a[i] <-------? a[i]=*(a+i) =&a[i][0],
值相等,含义不同
a+i? &a[i],表示第 i行首地址,指向行
a[i]? *(a+i)? &a[i][0],表示第 i行第 0
列元素地址,指向列
int a[3][4];
a[0]
a[1]
a[2]
2000
2008
2016
2000
2002
2008
2010
2016
2018
a[0][0]
a[0][1]
a[1][0]
a[1][1]
a[2][0]
a[2][1]
a[0][2]
a[0][3]
a[1][2]
a[1][3]
a[2][2]
a[2][3]
a
a+1
a+2
int a[3][4];
a[0][0]
a[0][1]
a[1][0]
a[1][1]
a[2][0]
a[2][1]
a[0][2]
a[0][3]
a[1][2]
a[1][3]
a[2][2]
a[2][3]
二维数组元素表示形式:
( 1) a[1][2]
( 2) *(a[1]+2)
( 3) *(*(a+1)+2)
( 4) *(&a[0][0]+1*4+2)
地址表示:
(1) a+1
(2) &a[1][0]
(3) a[1]
(4) *(a+1)
(5)(int *) (a+1)
属性为行指针属性为元素指针地址表示:
(1) &a[1][2]
(2) a[1]+2
(3) *(a+1)+2
(4)&a[0][0]+1*4+2
表示形式 含义 地址
a 二维数组名,数组首地址
a[0],*(a+0),*a 第 0行第 0列元素地址
a+1 第 1行首地址
a[1],*(a+1) 第 1行第 0列元素地址
a[1]+2,*(a+1)+2,&a[1][2] 第 1行第 2列元素地址
*(a[1]+2),*(*(a+1)+2),a[1][2] 第 1行第 2列元素值
2000
2000
2008
2008
2012
13
2,指向二维数组 元素 的指针变量例 10.12 指向二维数组元素的指针变量
main()
{ static int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23};
int *p;
for(p=a[0];p<a[0]+12;p++)
{ if((p-a[0])%4==0) printf("\n");
printf("%4d ",*p);
}
}
p=*a;
p=&a[0][0];
p=*(a+0);
p=a;
p=*a;
p=&a[0][0];
p=(int *)a;
p=a;
int a[3][4];
a[0][0]
a[0][1]
a[1][0]
a[1][1]
a[2][0]
a[2][1]
a[0][2]
a[0][3]
a[1][2]
a[1][3]
a[2][2]
a[2][3]
p
3,指向二 维 数组中的行 的指针变量
定义形式,数据类型 (*指针名 )[一维数组维数 ];
例 int (*p)[4];
( )不能少
int (*p)[4]与 int *p[4]不同
p的值是一维数组的首地址,p是 行指针
可让 p指向二维数组某一行如 int a[3][4],(*p)[4]=a;
int a[3][4];
a[0][0]
a[0][1]
a[1][0]
a[1][1]
a[2][0]
a[2][1]
a[0][2]
a[0][3]
a[1][2]
a[1][3]
a[2][2]
a[2][3]
a
a+1
a+2
p
p+1
p+2
p[0]+1或 *p+1
p[1]+2或 *(p+1)+2
*(*p+1)或 (*p)[1]
*(*(p+1)+2)
一维数组指针变量维数和二维数组 列数 必须相同例,二维数组行指针变量举例
main()
{ static int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23};
int i,j,(*p)[4];
for(p=a,i=0;i<3;i++,p++)
for(j=0;j<4;j++)
printf("%d ",*(*p+j));
printf("\n");
}
p=a[0];
p=*a;
p=&a[0][0];
p=&a[0];
p=a[0];
p=*a;
p=&a[0][0];
p=&a[0];
int a[3][4];
a[0][0]
a[0][1]
a[1][0]
a[1][1]
a[2][0]
a[2][1]
a[0][2]
a[0][3]
a[1][2]
a[1][3]
a[2][2]
a[2][3]
p
p
p
p[0][j]
例,二维数组与指针运算
main()
{ int a[3][4]={{1,2,3,4},{3,4,5,6},{5,6,7,8}};
int i;
int (*p)[4]=a,*q=a[0];
for(i=0;i<3;i++)
{ if(i==0)
(*p)[i+i/2]=*q+1;
else
p++,++q;
}
for(i=0;i<3;i++)
printf("%d,",a[i][i]);
printf("%d,%d\n",*((int *)p),*q);
}
运行结果,2,4,7,5,3
1 2 3 4
3 4 5 6
5 6 7 8
p
q
2p
q
p
q
8.3.3 字符串与指针
1、字符串表示形式两种方法
用字符数组实现,
例 10.16
main( )
{ char string[]=“I love China!”;
printf(“%s\n”,string);
printf(“%s\n”,string+7);
}
I
l
o
v
e
C
h
i
string[0]
string[1]
string[2]
string[3]
string[4]
string[5]
string[6]
string[7]
string[8]
string[9]
string
string[10]
string[11]
string[12]
string[13]
n
!
a
\0
用字符指针实现例 10.17 main( )
{ char *string=“I love China!”;
printf(“%s\n”,string);
string+=7;
while(*string)
{ putchar(string[0]);
string++;
}
}
I
l
o
v
e
C
h
i
string
n
!
a
\0
字符指针 初始化,把字符串 首地址 赋给 string
char *string;
string=“I love China!”;
string
*string!=0
用字符指针实现注意:
( 1) string是一个存放字符串起始地址的指针变量,不是一个字符串变量。
( 2)在程序中出现字符串常量时,
系统返回的是一个起始地址,因此可以将一个字符串常量赋值给一个字符指针变量。
*string=“I love China”; 是错误的 !
I
l
o
v
e
C
h
i
string
n
!
a
\0
字符指针 初始化,把字符串 首地址 赋给 string
char *string;
string=“I love China!”;
对字符串中字符的存取,可以用下标法也可以用指针法。
【 例 8.14】 判断字符串是否回文,回文指的是正读和倒读相同 。
main()
{char c[80],*pa,*pb;
int flag=1;
printf("\nplease input the string,");
gets(c);
pa=c; /*pa指向第 0个字符 */
pb=c;
while((*pb)!= '\0') pb++;
pb--; /*pb指向最后一个字符 */
while((pa<pb)&&flag)
{if (*pa!=*pb) flag=0;
else {pa++; pb--;}
} /*判断是否回文 */
if (flag) printf("\nyes,");
else printf("\nno,");
}
【 例 8.15】 复制字符串 。
main()
{char *ps="This is a string,";
char str[30];
char *pa,*pb;
pa=ps; pb=str;
while(*pa!= '\0')
{*pb=*pa;
pa++;pb++;
}
*pb='\0 ';
printf("\nthe sourse string is,%s",ps);
printf("\nthe purpose string is,%s",str);
}
8.3.4 数组名作函数参数
数组名作函数参数,是 地址传递
数组名作函数参数,实参与形参的对应关系实参 形参数组指针变量数组指针变量数组名数组名指针变量指针变量
i j
3 7 9 11 0 6 7 5 4 2
0 1 2 3 4 5 6 7 8 9
i ji ji jji
117 6 05 94 72 3
实参与形参均用数组
【 例 8.16】 数组逆置:
main()
{void fun(int b[10]);
int a[10];
int k;
printf("\nplease input the array:\n");
for (k=0;k<10;k++)
scanf("%d",&a[k]);
fun(a);
printf("\nnow the array is:\n");
for(k=0;k<10;k++)
printf("%3d",a[k]);
}
void fun(int b[10])
{int l,r;
int t;
l=0;r=10-1;
while(l<r)
{t=b[l];b[l]=b[r];b[r]=t;
l++;r--;
}
return;
}
① 数组名做函数的参数,可以定义形参和实参是同类型的大小相等的数组,象例 8.16中,实参数组 a和形参数组 b都是整型的长度为 10的一维数组。
② 一般情况下,为了函数的通用性强,可以不定义形参数组的长度,
而另外采用一个参数来表示数组长度,因为 C编译系统并不检查形参数组的大小,只是将实参数组的起始地址传递给形参数组。例 8.17中
,没有定义形参数组 a的长度,而是用另一个整型形参 n表示其长度。
这样,函数就能对不同长度的数组实现既定的功能。
③ 数组名做函数的实际参数时,并不是将所有数组元素的值传递给形参数组,而是传递数组的起始地址,所以形参数组和实参数组是共占内存单元。
④ 函数参数为数组名,当发生函数调用时,并不为形参数组名所对应的形参数组分配空间,而只为形参开辟一个存放地址的存储单元,用来存放实参数组名所表示的数组的起始地址。
也可以向函数传递结构体数组。
向函数传递结构体数组与传递其它的数组一样,实质上传递的是数组的首地址,形参数组与实参数组共占内存单元。
例 将数组 a中的 n个整数按相反顺序存放
void inv(int *x,int n)
{ int t,*i,*j,*p,m=(n-1)/2;
i=x; j=x+n-1; p=x+m;
for(;i<=p;i++,j--)
{ t=*i; *i=*j; *j=t; }
}
main()
{ int i,a[10],*p=a;
for(i=0;i<10;i++,p++)
scanf("%d",p);
p=a; inv(p,10);
printf("The array has been reverted:\n");
for(p=a;p<a+10;p++)
printf("%d",*p);
}
实参与形参均用指针变量例 将数组 a中的 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;
}
}
main()
{ int i,a[10],*p=a;
for(i=0;i<10;i++,p++)
scanf("%d",p);
p=a; inv(p,10);
printf("The array has been reverted:\n");
for(p=arr;p<arr+10;p++)
printf("%d ",*p);
}
实参用指针变量,形参用数组
0
1
2
3
4
5
6
7
8
9
a 9
13
27
32
49
57
68
76
88
99
array
i=8
例 8.13 数组排序 ----简单选择排序
void sort(int array[],int n)
{ int i,j,k,t;
for(i=0;i<n-1;i++)
{ k=i;
for(j=i+1;j<n;j++)
if(array[j]<array[k]) k=j;
if(k!=i)
{ t=array[i];
array[i]=array[k];
array[k]=t;
}
}
}
main()
{ int a[10],i;
for(i=0;i<10;i++)
scanf("%d",&a[i]);
sort(a,10);
for(i=0;i<10;i++)
printf("%d ",a[i]);
printf("\n");
}
数组名作函数参数
地址传递
在主调函数与被调函数分别定义数组,且类型应一致
形参数组大小 (多维数组第一维 )可不指定
形参数组名是 地址变量例 8.11 求学生的平均成绩
#include <stdio.h>
float average(float stu[10]);
void main()
{ float score[10],aver;
int i;
printf("Input 10 scores,\n");
for( i=0; i<10; i++ )
scanf("%d",&score[i]);
aver=average(score);
printf("Average is,%5.2f",aver);
}
float average(float stu[10])
{ int i;
float aver,sum=array[0];
for( i=1; i<10; i++ )
suml += stu[i];
aver = sum/n;
return aver;
}
实参 用 数组 名形参用数组定义,?int stu[ ]
.
.
2
1
0
9
score
56
23
12
….
….
88
stu
再 例 8.11 求学生的平均成绩
#include <stdio.h>
float average(int stu[10],int n);
void main()
{ int score[10],i;
float aver;
printf("Input 10 scores,\n");
for( i=0; i<10; i++ )
scanf("%d",&score[i]);
aver=average(score,10);
printf("Average is,%.2f",av);
}
float average(float stu[10],int n)
{ int i;
float aver,sum=0;
for( i=1; i<n; i++ )
sum += stu[i];
aver = sum/n;
return aver;
}
实参用数组名形参用数组定义,?int stu[ ]
.
.
2
1
0
9
score
56
23
12
….
….
88
stu
例 数组元素与 数组名作函数参数比较
1
2
a
调用前
a[0]
a[1]
1
2
a
调用
a[0]
a[1]
1
2
x
y
2
1
x
y
交换
1
2
a
返回
#include <stdio.h>
void swap2(int x,int y)
{ int z;
z=x; x=y; y=z;
}
main()
{ int a[2]={1,2};
swap2(a[0],a[1]);
printf("a[0]=%d\na[1]=%d\n",a[0],a[1]);
}
值传递
1
2
a
调用前
1
2
a
x
调用
2
1
a
x
交换
2
1
a
返回
#include <stdio.h>
void swap2(int x[])
{ int z;
z=x[0]; x[0]=x[1]; x[1]=z;
}
main()
{ int a[2]={1,2};
swap2(a);
printf("a[0]=%d\na[1]=%d\n",a[0],a[1]);
}
地址传递例 数组元素与 数组名作函数参数比较例 8.13 数组排序 ----简单选择排序
void sort(int array[],int n)
{ int i,j,k,t;
for(i=0;i<n-1;i++)
{ k=i;
for(j=i+1;j<n;j++)
if(array[j]<array[k]) k=j;
if(k!=i)
{ t=array[i];
array[i]=array[k];
array[k]=t;
}
}
}
main()
{ int a[10],i;
for(i=0;i<10;i++)
scanf("%d",&a[i]);
sort(a,10);
for(i=0;i<10;i++)
printf("%d ",a[i]);
printf("\n");
}
0
1
2
3
4
5
6
7
8
9
a 49
68
57
32
9
99
27
13
76
88
array kj
j
j k
j k
j
j
j
j
j
9
49
i=0
例 8.13 数组排序 ----简单选择排序
void sort(int array[],int n)
{ int i,j,k,t;
for(i=0;i<n-1;i++)
{ k=i;
for(j=i+1;j<n;j++)
if(array[j]<array[k]) k=j;
if(k!=i)
{ t=array[i];
array[i]=array[k];
array[k]=t;
}
}
}
main()
{ int a[10],i;
for(i=0;i<10;i++)
scanf("%d",&a[i]);
sort(a,10);
for(i=0;i<10;i++)
printf("%d ",a[i]);
printf("\n");
}
k
j
j k
j
kjj
j
j
j
0
1
2
3
4
5
6
7
8
9
a 49
68
57
32
9
99
27
13
76
88
array 9
49
k
k
13
68
i=1
3,二维数组的指针作函数参数
用指向数组元素的指针变量作参数
用指向二维数组?行?的指针变量作参数
用二维数组名作参数实参 形参数组 int x[][4]
指针变量 int (*q)[4]
数组 int x[][4]
指针变量 int (*q)[4]
数组名 a
数组名 a
指针变量 p1
指针变量 p1
若有定义,int a[3][4]; int (*p1)[4]=a; int *p2=a[0];
指针变量 p2 指针变量 int *q
例,求二维数组中最大元素值
1 3 5 7
2 4 6 8
15 17 34 12
i
j
max=1
1 3 5 7
2 4 6 8
15 17 34 12
i
j
max=3
1 3 5 7
2 4 6 8
15 17 34 12
i
j
max=5
j
1 3 5 7
2 4 6 8
15 17 34 12
i
max=7
j
1 3 5 7
2 4 6 8
15 17 34 12
i
max=7
j
1 3 5 7
2 4 6 8
15 17 34 12i
max=34
int max_value(int array[ ][4])
{ int i,j,k,max;
max=array[0][0];
for(i=0;i<3;i++)
for(j=0;j<4;j++)
if(array[i][j]>max)
max=array[i][j];
return(max);
}
main()
{ int a[3][4]={{1,3,5,7},
{2,4,6,8},{15,17,34,12}};
printf("max value is %d\n",max_value(a));
}
多维形参数组第一维维数可省略,第二维必须相同
int array[][4]
例,求二维数组中各行元素之和
get_sum_row(int x[][3],int result[],int row,int col)
{ int i,j;
for(i=0;i<row;i++)
{ result[i]=0;
for(j=0;j<col;j++)
result[i]+=x[i][j];
}
}
main()
{ int a[2][3]={3,6,9,1,4,7};
int sum_row[2],row=2,col=3,i;
get_sum_row(a,sum_row,row,col);
for(i=0;i<row;i++)
printf("The sum of row[%d]=%d\n",i+1,sum_row[i]);
}
3
1 4
6
7
9
a sum_row
x
result
18
12
例,3个学生各学 4门课,计算总平均分,并输出第 n个学生成绩
main()
{ void average(float *p,int n);
void search(float (*p)[4],int n);
float score[3][4]=
{{65,67,79,60},{80,87,90,81},
{90,99,100,98}};
average(*score,12);
search(score,2);
}
void average(float *p,int n)
{ float *p_end,sum=0,aver;
p_end=p+n-1;
for(;p<=p_end;p++)
sum=sum+(*p);
aver=sum/n;
printf("average=%5.2f\n",aver);
}
void search(float (*p)[4],int n)
{ int i;
printf(" No.%d,\n",n);
for(i=0;i<4;i++)
printf("%5.2f ",*(*(p+n)+i));
}
元素指针行指针函数说明
float p[][4]
65 52 79 60
80 87 90 81
90 99 100 98
p
p
p[n][i]
例,在上题的基础上,查找一门以上课 不及格学生,输出其各门课 成绩
void search(float (*p)[4],int n)
{ int i,j,flag;
for(j=0;j<n;j++)
{ flag=0;
for(i=0;i<4;i++)
if(*(*(p+j)+i)<60) flag=1;
if(flag==1)
{ printf("No.%d is fail,his scores are:\n",j+1);
for(i=0;i<4;i++)
printf("%5.1f ",*(*(p+j)+i));
printf("\n");
}
}
}
main()
{ void search(float (*p)[4],int n);
float score[3][4]={{...},{...},{...}};
search(score,3);
}
65 52 79 60
80 87 90 81
90 99 100 98
p
p[j][i]
二维数组与指向一行的(一维数组)指针变量的关系如 int a[5][10] 与 int (*p)[10];
二维数组名是一个指向有 10个元素的一维数组的 指针常量
p=a+i 使 p指向二维数组的第 i行
*(*(p+i)+j)? a[i][j]
二维数组形参实际上是一维数组指针变量,
即 int x[ ][10]? int (*x)[10]
变量定义 (不是形参)时两者不等价
系统只给 p分配能保存一个指针值的内存区 (一般 2字节);
而给 a分配 2*5*10字节的内存区例,指针函数实现:有若干学生成绩,要求输入学生序号后,
能输出其全部成绩。main(){ float score[][4]={{60,70,80,90},
{56,89,67,88},{34,78,90,66}};
float *search(float s[][4],int n);
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);
for(i=0;i<4;i++)
printf("%5.2f\t",*(p+i));
}
float *search(float s[ ][4],int n)
{ float *pt;
pt=*(s+n);
return(pt);
}
s
s+1
34 78 90 66
56 89 67 88
60 70 80 90
score数组
p p p p
例,指针函数实现:有若干学生成绩,要求输入学生序号后,
能输出其全部成绩。main()
{ float score[][4]={{60,70,80,90},
{56,89,67,88},{34,78,90,66}};
float *search(float s[][4],int n);
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);
for(i=0;i<4;i++)
printf("%5.2f\t",*(p+i));
}
float *search(float s[ ][4],int n)
{ float *pt;
pt=*(s+n);
return(pt);
}
s
s+1
34 78 90 66
56 89 67 88
60 70 80 90
score数组
p p p p
main()
{ float score[][4]={{60,70,80,90},
{56,89,67,88},{34,78,90,66}};
float *search(float (*pointer)[4],int n),*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);
for(i=0;i<4;i++)
printf("%5.2f\t",*(p+i));
}
float *search(float (*pointer)[4],int n)
{ float *pt;
pt=*(pointer+n);
return(pt);
}
pointer
pointer+1
34 78 90 66
56 89 67 88
60 70 80 90
score数组
p p p p
用指向函数的指针调用;
将一个字符串从一个函数传递到另一个函数,可以用地址传递的方法,即用字符数组名作参数 或 用指向字符串首地址的 字符指针变量作参数。
这样若在被调用的函数中改变了字符串的内容,在主调函数中则得到了改变后的字符串。
4,字符串指针作函数参数例,用函数调用实现字符串复制
( 1)用字符数组作形参
void copy_string(char from[],char to[])
{ int i=0;
while(from[i]!='\0')
{ to[i]=from[i];
i++;
}
to[i]='\0';
}
main()
{ 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("\nstring_a=%s\nstring_b=%s\n",a,b);
}
void copy_string(char from[],char to[])
{ int i=0;
while(from[i]!='\0')
{ to[i]=from[i];
i++;
}
to[i]='\0';
}
main() /*主函数也可以用字符指针 */
{ 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("\nstring_a=%s\nstring_b=%s\n",a,b);
}
( 2)用字符指针变量作形参 a
I
a
m
a
t
e
a
c
e
h
\0
r
.
from
a b
y
u
a
r
a
s
u
t
n
d
e
to
b
o
e
t
.
\0
I
a
a
e
c
e
h
\0
r
.
t
.
\0
m
t
a
void copy_string(char *from,char
*to)
{ for(;*from!='\0';from++,to++)
*to=*from;
*to='\0';
}
main()
{char *a="I am a teacher.";
char *b="You are a student.";
printf("string_a=%s\nstring_b=
%s\n",a,b);
copy_string(a,b);
printf("\nstring_a=%s\nstring_b=
%s\n",a,b);
}
COPY函数还可以进行简化方式 1:
void copy_string(char *from,char *to)
{ while ((*to=*from)!='\0')
{ to++; from++;}
}
方式 2:
void copy_string(char *from,char *to)
{ while ( (*to++=*from++)!='\0?); }
方式 3:
void copy_string(char *from,char *to)
{ while ( *from!='\0? )
*to++=*from++;
*to=?\0?;
}
方式 4:
void copy_string(char *from,char *to)
{ while ( (*to++=*from++) ); }
方式 5:
void copy_string(char *from,char *to)
{ for ( ; (*to++=*from++)!='\0?); ); }
方式 6:
void copy_string(char *from,char *to)
{ char *p1,*p2;
p1=from; p2=to;
while ( (*to++=*from++)!='\0? );
}
……
8.3.5 指针数组和多级指针 (指向指针的指针 )
1、指针数组 (数组的数据元素是指针)
定义:数组中的元素为指针变量
定义形式,数据类型 *数组名 [数组长度说明 ];
例 int *p[4];
区分 int *p[4]与 int (*p)[4]
指针数组赋值与初始化赋值,
main()
{ int b[2][3],*pb[2];
pb[0]=b[0];
pb[1]=b[1];
……..
}
int *pb[2]
pb[0]
pb[1]
int b[2][3]
1
2
3
2
4
6
初始化,
main()
{ int b[2][3],*pb[ ]={b[0],b[1]};
……..
}
int *pb[2]
pb[0]
pb[1]
int b[2][3]
1
2
3
2
4
6
指针数组赋值与初始化
L i s p \0
F o r t r a n \0
B a s i c \0
p[0]
p[1]
p[2]
p[3] 0
赋值,
main()
{ char a[]="Fortran";
char b[]="Lisp";
char c[]="Basic";
char *p[4];
p[0]=a; p[1]=b; p[2]=c; p[3]=NULL;
……..
}
或,
main()
{ char *p[4];
p[0]= "Fortran";
p[1]= "Lisp";
p[2]= "Basic";
p[3]=NULL;
……..
}
初始化,
main()
{ char *p[]={"Fortran","Lisp","Basic",NULL};
……..
}
i s \
F r t r a \
a s i c \
[0]
[1]
[2]
[3]
char name[5][9]={“gain”,“much”,“stronger”,“point”,“bye”};
char *name[5]={“gain”,“much”,“stronger”,“point”,“bye”};
g a i n \0
s t r o n g e r \0
p o i n t \0
m u c h \0
name[0]
name[1]
name[2]
name[3]
name[4] b y e \0
g a i n \0
s t r o n g e r \0
p o i n t \0
m u c h \0
b y e \0
二维数组与指针数组区别,
二维数组存储空间固定字符指针数组相当于 可变列长 的二维数组指针数组元素的作用相当于二维数组的行名但指针数组中元素是指针变量二维数组的行名是 地址常量
main()
{ int b[2][3],*pb[2];
int i,j;
for(i=0;i<2;i++)
for(j=0;j<3;j++)
b[i][j]=(i+1)*(j+1);
pb[0]=b[0];
pb[1]=b[1];
for(i=0;i<2;i++)
for(j=0;j<3;j++,pb[i]++)
printf("b[%d][%d]:%2d\n",i,j,*pb[i]);
}
例 用指针数组处理二维数组
int *pb[2]
pb[0]
pb[1]
int b[2][3]
b[0][0] *pb[0]
b[0][1] *(pb[0]+1)
b[0][2] *(pb[0]+2)
b[1][0] *pb[1]
b[1][1] *(pb[1]+1)
b[1][2] *(pb[1]+2)
1
2
3
2
4
6
例,对字符串排序(简单选择排序)
main()
{ void sort(char *name[],int n),print(char *name[],int n);
char *name[]={"Follow me","BASIC",
"Great Wall","FORTRAN","Computer "};
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++)
{ k=i;
for(j=i+1;j<n;j++)
if(strcmp(name[k],name[j])>0) k=j;
if(k!=i)
{ temp=name[i]; name[i]=name[k]; name[k]=temp;}
}
}
name[0]
name[1]
name[2]
name[3]
name[4]
name
Great Wall
FORTRAN
Computer
Follow me
BASIC
k
jk
j
j
j
i=0
例,对字符串排序(简单选择排序)
main()
{ void sort(char *name[],int n),print(char *name[],int n);
char *name[]={"Follow me","BASIC",
"Great Wall","FORTRAN","Computer "};
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++)
{ k=i;
for(j=i+1;j<n;j++)
if(strcmp(name[k],name[j])>0) k=j;
if(k!=i)
{ temp=name[i]; name[i]=name[k]; name[k]=temp;}
}
}
name[0]
name[1]
name[2]
name[3]
name[4]
name
Great Wall
FORTRAN
Computer
Follow me
BASICk
k
j
j
j
i=1
k
例,对字符串排序(简单选择排序)
main()
{ void sort(char *name[],int n),print(char *name[],int n);
char *name[]={"Follow me","BASIC",
"Great Wall","FORTRAN","Computer "};
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++)
{ k=i;
for(j=i+1;j<n;j++)
if(strcmp(name[k],name[j])>0) k=j;
if(k!=i)
{ temp=name[i]; name[i]=name[k]; name[k]=temp;}
}
}
name[0]
name[1]
name[2]
name[3]
name[4]
name
Great Wall
FORTRAN
Computer
Follow me
BASICk
k j
j
i=2
例,对字符串排序(简单选择排序)
main()
{ void sort(char *name[],int n),print(char *name[],int n);
char *name[]={"Follow me","BASIC",
"Great Wall","FORTRAN","Computer "};
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++)
{ k=i;
for(j=i+1;j<n;j++)
if(strcmp(name[k],name[j])>0) k=j;
if(k!=i)
{ temp=name[i]; name[i]=name[k]; name[k]=temp;}
}
}
name[0]
name[1]
name[2]
name[3]
name[4]
name
Great Wall
FORTRAN
Computer
Follow me
BASIC
k
k j
i=3
例,对字符串排序(简单选择排序)
main()
{ void sort(char *name[],int n),print(char *name[],int n);
char *name[]={"Follow me","BASIC",
"Great Wall","FORTRAN","Computer "};
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++)
{ k=i;
for(j=i+1;j<n;j++)
if(strcmp(name[k],name[j])>0) k=j;
if(k!=i)
{ temp=name[i]; name[i]=name[k]; name[k]=temp;}
}
}
name[0]
name[1]
name[2]
name[3]
name[4]
name
Great Wall
FORTRAN
Computer
Follow me
BASIC
2、指向指针的指针
定义,指向指针的指针
一级指针,指针变量中存放目标变量的地址
p1
&p2 &i 3
P2(指针变量 ) i(整型变量 )例 int **p1;
int *p2;
int i=3;
p2=&i;
p1=&p2;
**p1=5;
二级指针,指针变量中存放一级指针变量的地址例 int *p;
int i=3;
p=&i;
*p=5;
&i 3
P(指针变量 ) i(整型变量 )
一级指针 单级间接寻址二级指针 一级指针 目标变量二级间接寻址
定义形式,[存储类型 ] 数据类型 **指针名;
如 char **p;
例 int i,**p;
p=&i; (?)//p是二级指针,不能用变量地址为其赋值指针本身的存储类型 最终目标变量的数据类型 *p是 p间接指向对象的地址**p是 p间接指向对象的值例 int i=3;
int *p1;
int **p2;
p1=&i;
p2=&p1;
**p=5;
i
p1
p2
3
&i
&p1
**p2,*p1
*p2
多级指针例 三级指针 int ***p;
四级指针 char ****p;
2000
2008
200A
2002
2004
2006
1
2
变量 a
变量 b
(main)
指针变量 p2000
指针变量 q2002
例 一级指针与二级指针
#include <stdio.h>
void swap(int *r,int *s)
{ int *t;
t=r;
r=s;
s=t;
}
main()
{ int a=1,b=2,*p,*q;
p=&a;
q=&b;
swap(p,q);
printf("%d,%d\n",*p,*q);
}
2002
2000
COPY
指针变量 s
指针变量 r(swap)
指针变量 t2000
2002
2000
2000
2008
200A
2002
2004
2006
1
2
变量 a
变量 b
(main)
指针变量 p2000
指针变量 q2002
例 一级指针与二级指针
#include <stdio.h>
void swap(int *r,int *s)
{ int *t;
t=r;
r=s;
s=t;
}
main()
{ int a=1,b=2,*p,*q;
p=&a;
q=&b;
swap(p,q);
printf("%d,%d\n",*p,*q);
} 输出,1,2
例 一级指针与二级指针
#include <stdio.h>
void swap(int *r,int *s)
{ int *t;
t=r;
r=s;
s=t;
}
main()
{ int a=1,b=2,*p,*q;
p=&a;
q=&b;
swap(p,q);
printf("%d,%d\n",*p,*q);
}
a
b
p
q
a
b
p
q
r
s
a
b
p
q
s
r
a
b
p
q输出,1,2
例 一级指针与二级指针
#include <stdio.h>
void swap(int **r,int **s)
{ int *t;
t=*r;
*r=*s;
*s=t;
}
main()
{ int a=1,b=2,*p,*q;
p=&a;
q=&b;
swap(&p,&q);
printf("%d,%d\n",*p,*q);
}
2000
2008
200A
2002
2004
2006
1
2
变量 a
变量 b
(main)
指针变量 p2000
指针变量 q2002
2006
2004
COPY
二级指针 s
二级指针 r(swap)
指针变量 t
2000
2002
2000
例 一级指针与二级指针
#include <stdio.h>
void swap(int **r,int **s)
{ int *t;
t=*r;
*r=*s;
*s=t;
}
main()
{ int a=1,b=2,*p,*q;
p=&a;
q=&b;
swap(&p,&q);
printf("%d,%d\n",*p,*q);
}
2000
2008
200A
2002
2004
2006
1
2
变量 a
变量 b
(main)
指针变量 p2000
指针变量 q200220002002
输出,2,1
例 一级指针与二级指针
#include <stdio.h>
void swap(int **r,int **s)
{ int *t;
t=*r;
*r=*s;
*s=t;
}
main()
{ int a=1,b=2,*p,*q;
p=&a;
q=&b;
swap(&p,&q);
printf("%d,%d\n",*p,*q);
}
a
b
p
q
b
a
p
q
a
b
r
s
p
q
a
b
r
s
p
q
输出,2,1
例 用二级指针处理字符串
#define NULL 0
void main()
{
char **p;
char *name[]={"hello","good","world","bye",""};
p=name+1;
printf("%o,%s ",*p,*p);
p+=2;
while(**p!=NULL)
printf("%s\n",*p++);
}
name[0]
name[1]
name[2]
name[3]
name[4]
char *name[5]
world
bye
\0
hello
good
name
p
运行结果:
644,good bye
用 *p可输出 地址 (%o或 %x),
也可用它输出 字符串 (%s)
p?*(p++)
二级指针与指针数组的关系
int **p 与 int *q[10]
指针数组名是二级指针 常量
p=q; p+i 是 q[i]的地址
指针数组作形参,int *q[ ]与 int **q完全等价;但作为变量定义两者不同
系统只给 p分配能保存一个指针值的内存区;而给 q分配 10块内存区,每块可保存一个指针值
3、命令行参数
命令行:在操作系统状态下,为执行某个程序而键入的一行字符
命令行一般形式,命令名 参数 1 参数 2……… 参数 n
main(int argc,char *argv[])
{ ………
}
命令行参数传递
带参数的 main函数形式:
C:\TC> copy[.exe] source.c temp.c
有 3个字符串参数的命令行命令行中参数个数 元素指向命令行参数中各字符串首地址形参名任意命令行 实参 main(形参 )
系统自动调用
main函数时传递第一个参数,main所在的 可执行文件名例 输出命令行参数
/*test.c*/
main(int argc,char *argv[])
{ while(argc>1)
{ ++argv;
printf("%s\n",*argv);
--argc;
}
}
main(int argc,char *argv[])
{ while(argc-->0)
printf("%s\n",*argv++);
}
1,编译、链接 test.c,生成可执行文件 test.exe
2,在 DOS状态下运行 (test.exe所在路径下)
例如,C:\TC> test[.exe] hello world!
运行结果,hello
world!
运行结果,test
hello
world!
argv[0]
argv[1]
argv[2]
char *argv[]
world
test
hello
argv
argc=3
定义 含义
int i;
int *p;
int a[n];
int *p[n];
int (*p)[n];
int f();
int *p();
int (*p)();
int **p;
定义整型变量 i
p为指向整型数据的指针变量定义含 n个元素的整型数组 a
n个指向整型数据的指针变量组成的指针数组 p
p为指向含 n个元素的一维整型数组的指针变量
f为返回整型数的函数
p为返回指针的函数,该指针指向一个整型数据
p为指向函数的指针变量,该函数返回整型数
p为指针变量,它指向一个指向整型数据的指针变量指针的数据类型例 下列定义的含义
( 1) int *p[3];
( 2) int (*p)[3];
( 3) int *p(int);
( 4) int (*p)(int);
( 5) int *(*p)(int);
( 6) int (*p[3])(int);
( 7) int *(*p[3])(int); 函数指针数组,函数返回 int型指针指针数组指向一维数组的指针返回指针的函数指向函数的指针,函数返回 int型变量指向函数的指针,函数返回 int 型指针函数指针数组,函数返回 int型变量
§ 8.4 动态数据结构
8.4.1 结构体和指针
1、指向结构体变量的指针
定义形式,struct 结构体名 *结构体指针名 ;
例 struct student *p;
使用结构体指针变量引用成员形式 存放结构体变量在内存的起始地址
num
name
sex
age
stu
pstruct student{ int num;
char name[20];
char sex;
int age;
}stu;
struct student *p=&stu;
(*结构体指针名 ).成员名 结构体指针名 ->成员名 结构体变量名,成员名指向运算符优先级,1
结合方向:从左向右例 11.3 指向结构体的指针变量
main()
{ struct student
{ long int num;
char name[20];
char sex;
float score;
}stu_1,*p;
p=&stu_1;
stu_1.num=89101;
strcpy(stu_1.name,"Li Lin");
p->sex='M';
p->score=89.5;
printf("\nNo:%ld\nname:%s\nsex:%c\nscore:%f\n",
(*p).num,p->name,stu_1.sex,p->score);
}
例 int n;
int *p=&n;
*p=10;? n=10
struct student stu1;
struct student *p=&stu1;
stu1.num=101;? (*p).num=101
注意:
若有,struct student *p=&stu_1;
(*p).n 等价于 p->n
p->n++; 什么意思 等价于 (p->n)++;
++P->n; 什么意思 等价于 ++(p->n);
2、指向结构体数组的指针例 11.4 指向结构体数组的指针
struct student
{ int num;
char name[20];
char sex;
int age;
}stu[3]={{10101,"Li Lin",'M',18},
{10102,"Zhang Fun",'M',19},
{10104,"Wang Min",'F',20}};
main()
{ struct student *p;
for(p=stu;p<stu+3;p++)
printf("%d%s%c%d\n",p->num,p->name,p->sex,p->age);
}
num
name
sex
age
stu[0]
p
stu[1]
stu[2]
p+1
3,用指向结构体的指针作函数参数
用结构体变量的成员作参数 ----值传递
用指向结构体变量或数组的指针作参数 ---- 值传递 (此值为地址)
用结构体变量作参数 ----值 ( 多值 ) 传递,效率低
struct data
{ int a,b,c; };
main()
{ void func(struct data);
struct data arg;
arg.a=27; arg.b=3; arg.c=arg.a+arg.b;
printf("arg.a=%d arg.b=%d arg.c=%d\n",arg.a,arg.b,arg.c);
printf("Call Func()....\n");
func(arg);
printf("arg.a=%d arg.b=%d arg.c=%d\n",arg.a,arg.b,arg.c);
}
void func(struct data parm)
{ printf("parm.a=%d parm.b=%d parm.c=%d\n",parm.a,parm.b,parm.c);
printf("Process...\n");
parm.a=18; parm.b=5; parm.c=parm.a*parm.b;
printf("parm.a=%d parm.b=%d parm.c=%d\n",parm.a,parm.b,parm.c);
printf("Return...\n");
}
arga,27b,3
c,30
(main)
(func)
parm
a,27
b,3
c,30
copy
arg
a,27
b,3
c,30
(main)
(func)
parm
a,18
b,5
c,90
arg
a,27
b,3
c,30
(main)
arg
a,27
b,3
c,30
ain)例 用结构体变量作函数参数
struct data
{ int a,b,c; };
main()
{ void func(struct data *parm);
struct data arg;
arg.a=27; arg.b=3; arg.c=arg.a+arg.b;
printf("arg.a=%d arg.b=%d arg.c=%d\n",arg.a,arg.b,arg.c);
printf("Call Func()....\n");
func(&arg);
printf("arg.a=%d arg.b=%d arg.c=%d\n",arg.a,arg.b,arg.c);
}
void func(struct data *parm)
{ printf("parm->a=%d parm->b=%d parm->c=%d\n",parm->a,parm->b,parm->c);
printf("Process...\n");
parm->a=18; parm->b=5; parm->c=parm->a*parm->b;
printf("parm->a=%d parm->b=%d parm->c=%d\n",parm->a,parm->b,parm->c);
printf("Return...\n");
}
arg
a,18
b,5
c,90
(main)
arg
a,27
,3
c,30
ai )例 用结构体指针变量作函数参数
arga,27b,3
c,30
ai )
(func)
parm****
arga,18b,5
c,9
(main)
(f c)
8.4.2 链表链表的特点
链表是一种动态的进行存储分配的数据结构,程序执行中,
可以在需要时开辟存储单元,在不需要时释放存储单元。
链表的结点包含数据域和链接域,数据域用来保存数据信息,
链接域用来保存该结点的后继结点或前驱结点的地址。
一个链表用一个头指针来保存该链表的首地址,即第一个结点的地址。头指针是一个链表的标志。
一个单链表的逻辑示意图
【 例 8.23】 通过给结点的指针域赋值建立一个单链表。
main()
{struct node
{int data;
struct node *next;
}n1,n2,n3,*head,*p;
head=&n1;
n1.data=1; n1.next=&n2;
n2.data=2; n2.next=&n3;
n3.data=3; n3.next=0;
printf("\n");
for(p=head;p!= '\0';p=p->next)
printf("%3d",p->data);
}
创建链表
相关函数:
malloc函数
1.原型,void *malloc(unsigned int size);
2.功能:在内存的动态存储区中分配 size个字节的连续空间,
它的返回值是所分配的那一段空间的起始地址,若分配失败,
则返回一个空指针 ( 0) 。
calloc函数
1.原型,void *calloc(unsigned int n,unsigned int size);
2.功能:内存的动态存储区中分配 n个长度为 size个字节的连续空间,它的返回值是指向所分配空间的起始地址,若分配失败,则返回一个空指针 ( 0) 。
free函数
1.原型,void free(void *p);
2.功能:释放 p所指向的内存区,该函数没有返回值。一般 p是调用 malloc函数或 calloc函数的返回值。
建立单链表的主要步骤为:
生成只含有头结点的空链表;
然后读取数据信息,生成新结点,将数据存放于新结点中,插入新结点到单链表中
重复第二步,直到输入结束 。
根据新结点插入到链表的位置的不同,建立链表的方式,
分为在表尾插入的方法和在表头插入的方法 。
表尾插入,
例 8.24
表头插入,
例 8.25
举例,例 8.26
在链表中插入结点设指针变量 s指向待插入结点,假设指定结点为 p。
插入到指定结点之后,① snext=pnext; ② pnext=s;
插入到指定结点之前:首先要找到 p的前驱结点 q。
例 9.8
在链表中删除结点在一个单链表中删除指定结点,首先要找到该结点的前驱结点,
然后修改前驱结点的指针域指向待删结点的后继结点,然后释放被删结点。