第七章 概述
★ 内容提要:
指针概念与数据访问方式
指针变量和指针运算
指向数组、字符串的指针
动态申请存储空间
指针数组和指向指针的指针
返回指针的函数
? 指针概念
? 指针变量和指针运算
? 指针与数组
? 字符数组与指针
? 动态申请存储空间
? 指针数组
? 返回指针的函数
指针是 C 语言中的一个重要的概念,也是 C 语
言的一个重要特色。正确而灵活地运用指针:
⑴ 可以有效地表示复杂的数据结构;
⑶ 能方便地使用字符串;
⑵ 实现动态分配存储空间;
⑷ 有效而方便地使用数组;
⑸ 在调用函数时使能得到多于一个的值 ;
⑹ 能直接处理内存地址等。
void mai n(){
int i,j,k,*i_pointer ;
scanf(, % d % d,,&i,& j);
k=i +j;
printf(, %d \ n,,k);
i_poi nter= &i;
k=*i _pointer;
printf(, %d \ n,,k);
}
…
…
…
…
2000
2004
2008
3000
地址
存储单元
变量
i
j
k
i_poi nter
…
…
…
…
存
放
整
数
值
存放整数
存储单元
的地址
[[ 例例 1010 -- 1]1] 数据存取访问方式与指针
9
6
3按变量
存取方
式可称
,直接
访问,
方式
2000
按地址
存取方
式可称
,间接
访问,
方式
3
指针变量所包含的是内 存地址,而该
地址便是另一个变量在内存中的存 储位
置。 指针就是地址,通过指针可以 达到
间接存取目标的目的。
要正确区分,
指针与指针变量这两个概念 !
7.2 指针运算符, &, 和, *,
,&,,取地址符,其操作数可以是各种类型
的简单变量、数组元素、结构成员,不能用于
表达式、常量和寄存器变量,是描述变量地址
的工具。属单目运算符。如:
int x; scanf(, %d,,&x );
给出变量的存储单元的地址,作为
存放键盘输入数据的指 针,将数据存
入变量 x 的存储单元之中
,*,,指针运算符(或 称, 间接访问, 运算
符)。属单目运算符。 作用:
? 定义说明指针变量。
当指针运算符出现在定义说明语句中时,
说明其后的标识符为存放地址的变量,系统
为其分配相应的存储单元。
int i,j,k,*i_pointer ;
,*,,指针运算符(或 称, 间接访问, 运算
符)。属单目运算符。 作用:
? 通过其操作数(指针)实现, 间接访问, 存
储单元的数据。
是将指针 i_pointer 所指的存储单元的内容赋给变量 k
,实际上是对变量 i 的间接存取。 * i_pointer 意为指
针变量 i_pointer 所指的变量,相当于变量 i 代表值。
i_pointer=&i; k = *i_pointer ;
* i_pointer 可出现在变量 i 能出现的任何地方。
‘ * ’ 要求其操作数具有指针意义的值 !
i;k* i _ p o i n t ;k
???? ???
等价
只是存取方式不同
???;ik;*i_point)k
???????? ????? (
等价
只是存取方式不同
?????
7.3 指针变量的定义说明
指针必须先定义后引用,在说明中 必须指出
它所指向对象的数据类型。一旦 说明后,就不
能随意指向别的不同类型的变量。
一般形式:
类型标识符 * 指针说明符,?? ;
几种常用定义形式,(对标识符的说明应以
运算符的优先级为依据层层结合分解理解)
int x,*p;
P 是指向整型变量的指针
int *pf( 形参说明表列 );
pf 是函数
pf 是返回值为指向整型存储单元地
址的指针函数
int ( *pfg ) ( 形参说明表列 );
pfg 是指针
pfg 是 指向一 返回整型量函数的指针是
pfg 是指向函数的指针
int *(*pfgi)( 形参说明表列 );
pfgi 是指针
pfgi 所指函数是返回指针的函数
pfgi 是指向函数的指针
pfgi 所指函数是返回指向整型量存 储单元
地址的函数
int *a[ 5 ];
a是数组
a是指针数组
a数组的 5个元素用于存储指向整型存储单元的
地址
对标识符的说明应以运算符的优先级为
依据层层结合分解理解
7.4 指针与数组
指针与数组两者关系密切,通过指针可方便
地存取数组元素,且质量高,速度快。
(1) 数组的指针表示:
数组名,代表该数组存储单元的起始地址,可称为数
组的指针,是地址常量。
数组元素,相当于变量,代表值,有固定的存储单元
及相应地址,可通过 ‘ & ’ 取地址符得到其地址。因此
可通过指向数组元素的指针找到所需的元素。且质量
高,速度快。
设, int a[10],*pa; pa=&a[5];
pa
当前元素前趋元素 后继元素
0 1 2 3 4 5 6 7 8 9
a
系统的内部实现:
pa+ i 所指向的地址 = pa + i * ( 比例因子 )
数组元素所占字节数数组元素所占字节数
00
11
22
……
……
ii
……
……
……
99
……
……
……
……
……
aa papa a[0]a[0]
a+1a+1 pa+1pa+1 a[1]a[1]
a+2a+2 pa+2pa+2 a[2]a[2]
…… ……
…… ……
a+ia+i pa+ipa+i a[i]a[i]
…… ……
…… ……
…… ……
a+9a+9 pa+9pa+9 a[9]a[9]
下标下标 aa 数组数组 数组名数组名 指针指针 下标法下标法
*(a)*(a)
*(a+1)*(a+1)
*(a+2)*(a+2)
……
……
*(a+i)*(a+i)
……
……
……
*(a+9)*(a+9)
数组名法数组名法
……
……
……
……
……
……
……
……
……
……
*(pa)*(pa)
*(pa+1)*(pa+1)
*(pa+ 2)*(pa+ 2)
……
……
*(pa+ i)*(pa+ i)
……
……
……
*(pa+ 9)*(pa+ 9)
指针法指针法
……
……
……
……
……
……
……
……
……
……
pa[0]pa[0]
pa[1]pa[1]
pa[2]pa[2]
……
……
pa[ i]pa[ i]
……
……
……
pa[ 9]pa[ 9]
指针数组法指针数组法
……
……
……
……
……
……
……
……
……
……
位序位序 地址地址
(2) (2) 数组元素的几种表示:设数组元素的几种表示:设 int a[10],pa=a;int a[10],pa=a;
数组元素的表示(值)数组元素的表示(值)
void mai n(){
static i nt a[10]={1,2,3,4,5,6,7,8,9,10};
int i,*p;
for(i =0; i <10; i ++ )
printf(, %d \ n,,a[i]); // 下标法
for(i =0; i <10; i ++ )
printf(, %d \ n,,*(a+i)); // 数组名(地址常量)法
for(p=a; p< a+10; p+ +)
printf(, %d \ n,,*p); // 指针变量法
}
? 下标法和数组名法执行效果相同,C 编译程序将
a[i] 转换为 *( a+i) 处理,即先计算元素地址,然后存
取,较费时;
[[ 例例 77 -- 2]2] 运算运算
中数组元素的中数组元素的
表示方式的优表示方式的优
劣比较。劣比较。
void mai n(){
static i nt a[10]={1,2,3,4,5,6,7,8,9,10};
int i,*p;
for(i =0; i <10; i ++ )
printf(, %d \ n,,a[i]); // 下标法
for(i =0; i <10; i ++ )
printf(, %d \ n,,*(a+i)); // 数组名(地址常量)法
for(p=a; p< a+10; p+ +)
printf(, %d \ n,,*p); // 指针变量法
}
? 指针变量法比前两种方法快,用指针直接指向元素
,不必每次都重新计算地 址。有规律的改变地址值(
p++ ) 能大大提高执行效率;
[[ 例例 77 -- 2]2] 运算运算
中数组元素的中数组元素的
表示方式的优表示方式的优
劣比较。劣比较。
void mai n(){
static i nt a[10]={1,2,3,4,5,6,7,8,9,10};
int i,*p;
for(i =0; i <10; i ++ )
printf(, %d \ n,,a[i]); // 下标法
for(i =0; i <10; i ++ )
printf(, %d \ n,,*(a+i)); // 数组名(地址常量)法
for(p=a; p< a+10; p+ +)
printf(, %d \ n,,*p); // 指针变量法
}
? 下标法较直观,a[5] 即为第 6 个元素,数组名法和
指针变量法不直观,不易 判断当前处理的元素位置,
出错处理较复杂,需要采用数据跟踪查错。
[[ 例例 77 -- 2]2] 运算运算
中数组元素的中数组元素的
表示方式的优表示方式的优
劣比较。劣比较。
void mai n(){
static i nt a[10]={1,2,3,4,5,6,7,8,9,10};
int i,*p;
for(i =0; i <10; i ++ )
printf(, %d \ n,,a[i]); // 下标法
for(i =0; i <10; i ++ )
printf(, %d \ n,,*(a+i)); // 数组名(地址常量)法
for(p=a; p< a+10; p+ +)
printf(, %d \ n,,*p); // 指针变量法
}
? C 程序员的一般做法是:若所编制的程序是按严格
的递增或递减顺序访问数 组,由于指针用起来快捷方
便,所以指针运算方法为首选方案;
[[ 例例 77 -- 2]2] 运算运算
中数组元素的中数组元素的
表示方式的优表示方式的优
劣比较。劣比较。
? 注意数组名和指针之间的区别:
指 针,是变量,pa=a;pa++; 都是有意义的操作。
数组名,代表数组存储单元的起始地址,是地址常
量,因此,a=pa ; a++ 都是非法操作
? 正确区分赋值概念。
pa=&a[0]; // 赋地址,pa 指向 a 数组的第 0 号元素。
* pa=123; // 赋数值,pa 所指的存储单元的值被更
新为 123 。
[[ 例例 77 -- 3]3] 数组元素表示方法,阅读程序给出结果。数组元素表示方法,阅读程序给出结果。
void main(){
int a[5],*p=a;
a[0]=100;
p++; *p=101;
p -- ; *(a+2)=102;
*(p+3)=103;
p[4]=104;
for(p=&a[4]; p - &a[0]>=0; p -- )
printf("%4d",*p);
printf(" \ nOK! \ n");
}
可以使用多种不同的
方式来引用一个数组
元素。数组名是地址
常量,指针是变量,
应随时注意其值(地
址)的变化。
运行结果:
104 103 102 101 100
OK!
特别说明, ++ 和 — 运算符用于指针变量十分有
效,可以使指针变量自动向前 或向后移动,指
向下一个或上一个数组元素。
papaia
papaia
papaia
papaia
???? ??????
???? ??????
???? ??????
???? ??????
*)(*][
*)(*][
*)(*][
*)(*][
等价
等价
等价
等价
表达式运算过程应从运算符与操作数的结合
和运算次序两个方面去理解,* pa++
㈠ 首先根据运算符的优先级与操作数结合
① pa++ pa 首先与 ++ 结合,确定 ++ 的操作数是 pa ;
② *pa * 与 pa 结合间接存取 pa 所指存储单元的值。
㈡ 然后按运算规则分步逐一运算
① *pa 首先间接存取 pa 所指存储单元的值;
② pa++ 然后 pa++, 即 pa 指向下一个元素,因为 ++
在 pa 的右边 所以 * pa++ 与 *( pa++) 等价。
同理, a[i++] 也应从运算符与操作数的结合和运算次
序两个方面去理解,因为 ++ 在操作数 i 的右边,所以先
存取 a[i] 的值,然后再 i++ 。
数组名 a 代表数组的起始地址,是常量:
a=pa; a++; // 都为非法操作 ;
pa 是指针变量:
pa=a; pa++; // 都为有意义的操作。
7,5 数组名,指针和函数参数
采用数组名和指针作为函数的参数,可通过
传地址方式,实现形参和实参共享存储单元,
达到数据互相传递的目的,主要有四种组合形
式:
⑴ 形参和实参都用数组名
⑶ 实参用数组名,形参用指针变量
⑵ 实参和形参都用指针变量
⑷ 实参为指针变量,形参为数组名
(1) (1) 形参和实参都用数组名形参和实参都用数组名
fun(int x[],int n ){
……
}
main( ){
int a[10];
……
fun( a,10 );
……
}
说明:
① 定义数组时必须
说明大小。
② 数组元素的个数
n 的传递是需要的,
以便正确控制元素
的访问区域,有效
防止数组的越界使
用。
(2) (2) 实参和形参都用指针变量实参和形参都用指针变量
fun(int *x,int n ){
……
}
main( ){
int a[10], *p;
p=a;
……
fun( p,10 );
……
}
③ 形参数组以指
针形式定义,用
指针实现对数组
元素的操作,定
义简单,不是很
直观,但有时效
率较高。
(2) (2) 实参和形参都用指针变量实参和形参都用指针变量
fun(int *x,int n ){
……
}
main( ){
int a[10], *p;
p=a;
……
fun( p,10 );
……
}
④ 实参指针变量 p 必
须有确定的地址,即
必须事先指向一个可
使用的存储空间,即
语句 p=a; 是必须的。
否则,可能由于局部
变量的初值的不确定
性,导致用户程序对
重要数据区或系统文
件的读写,造成严重
的不可预料的后果。
(3) (3) 实参用数组名,形参用指针变量实参用数组名,形参用指针变量
fun(int *x,int n ){
……
}
main( ){
int a[10];
……
fun( a,10 );
……
}
⑤ 形参以指针形
式表示,不是很直
观,使用时应该搞
清楚形参和实参的
对应关系。
(4) (4) 实参和形参都用指针变量实参和形参都用指针变量
fun(int x[],int n ){
……
}
main( ){
int a[10], *p;
p=a;
……
fun( p,10 );
……
}
⑥ 实参指针变量 p
必须有确定的地址
。其所指的存储空
间,可由静态数据
结构获得,也可由
动态申请存储空间
获得。
7,6 动态申请存储空间简介
向量点乘示例说明
][][][][
1
0
ibianbna
n
i
?
?
?
?。求,设有
#include <stdio.h>
#include <malloc.h>
float product(float *a,float *b,int n){
int i; float s;
for(i=0,s=0;i<n;i++)
s=s+(*a++ * *b++);
return(s);
}// 向量点乘功能模块
void main(){
float *d1,*d2,s1; int i;
d1=(float *)malloc(4 * sizeof(float));
d2=(float *)malloc(4 * sizeof(float));
for(i=0; i<3; i++)
scanf("%f%f",d1++,d2++);
d1=d1 - 3; d2=d2 - 3; / 指针复位
s1=product( d1,d2,3);
printf(" \ ns1=%f \ n",s1);
}
d1=(float *)malloc(4 * sizeof(float));
Memory - alloc, 动态存储
分配函数,它分配后面括 号
内指定字节数的存储区,并
返回 void/char 类型的指针,
即所分配内存区的首地址,
若动态申请空间失败,则 返
回 NULL(0) 。
,动态存储
回 。
申请所需存储空间的
大小,字节为单位。
申请所需存储空间的
强制类型转换:
由于返回的存储区
首地址是 void/char
类型的指针,若要
适用于存放特定类
型的数据,则必须
作强制类型的转换
,使适合存储相应
数据类型的数据,
及以相应类型字长
计算地址。
强制类型转换:
由于返回的存储区
首地址是
类型的指针,若要
适用于存放特定类
型的数据,则必须
作强制类型的转换
,使适合存储相应
数据类型的数据,
及以相应类型字长
float product(fl oat *a,float *b,i nt n){
int i ; float s;
for(i =0,s= 0; i< n; i+ +)
s=s+(*a+ + * *b+ +);
return(s);
}
void mai n(){
float *d1,*d2,s1; int i;
d1=(fl oat *)mall oc(4 * siz eof(fl oat)) ;
d2=(fl oat *)mall oc(4 * siz eof(fl oat)) ;
for(i =0; i <3; i ++ )
scanf(" % f % f",d 1++,d2++ );
d1=d1 - 3; d2=d2 - 3;
s1=product( d1,d 2,3);
printf(" \ ns1= % f \ n",s1 );
}
运行结果:
s1=14.000000
输入数据,
1 1
2 2
3 3
7.7 多维数组与指针
地址的计算与表示, 以三维数组的元素为例,若设 i n t
b[3][4][5] ; 则其元素 b[i][j][k] 各部分可分解表示
为:
b[i][j]
b[i]
b
元素(数值)
一维数组名 ( 地址 ) 列列
二维数组名 ( 地址 ) 行行
三维数组名 ( 地址 ) 层层
b[i][j][k] 含义 单位 ( 地址 )
数组名代表的维
数不同,计算的地
址单位也就不同,
要以层、行和列的
观点理解地址的表
示方式。
以二维数组为例说明:
int a[3] [4],*p; p = *a;
a,a[0],p
2044
12
2040
11
2036
10
2032
9
2028
8
2024
7
2020
6
2016
5
2012
4
2008
3
2004
2
2000
1
a+1,a[1]
a+2,a[2]
a[0]+1,p+1
2000 1 a[0] [ 0]
2004 2 a[0] [ 1]
2008 3 a[0] [ 2]
2012 4 a[0] [ 3]
2016 5 a[1] [ 0]
2020 6 a[1] [ 1]
2024 7 a[1] [ 2]
2028 8 a[1] [ 3]
2032 9 a[2] [ 0]
2036 10 a[2] [ 1]
2040 11 a[2] [ 2]
2044 12 a[2] [ 3]
a,a[0],p
a[0]+1,p+1
a+1,a[1]
a+2,a[2]
a[1]+2,p+6
外部逻辑形态:地址的单位不
同,指针运算的走向不同。
内部一维存储空间根据地址单位
的不同,指针运算的走向形态。
内存中的表示
以二维数组为例说明:
int a[3] [4],*p; p = *a;
2000 1 a[0] [ 0]
2004 2 a[0] [ 1]
2008 3 a[0] [ 2]
2012 4 a[0] [ 3]
2016 5 a[1] [ 0]
2020 6 a[1] [ 1]
2024 7 a[1] [ 2]
2028 8 a[1] [ 3]
2032 9 a[2] [ 0]
2036 10 a[2] [ 1]
2040 11 a[2] [ 2]
2044 12 a[2] [ 3]
a,a[0],p
a[0]+1,p+1
a+1,a[1]
a+2,a[2]
a[1]+2,p+6
内部一维存储空间根据地址单位
的不同,指针运算的走向形态。
⑴ p = * a ; 指针赋值时赋
值运算符的两边的地址单
位应一致;
⑵ a+1 相当于以一维数组
为存储单位(行) +1,表
示下一行地址;
说明,
以二维数组为例说明:
int a[3] [4],*p; p = *a;
2000 1 a[0] [ 0]
2004 2 a[0] [ 1]
2008 3 a[0] [ 2]
2012 4 a[0] [ 3]
2016 5 a[1] [ 0]
2020 6 a[1] [ 1]
2024 7 a[1] [ 2]
2028 8 a[1] [ 3]
2032 9 a[2] [ 0]
2036 10 a[2] [ 1]
2040 11 a[2] [ 2]
2044 12 a[2] [ 3]
a,a[0],p
a[0]+1,p+1
a+1,a[1]
a+2,a[2]
a[1]+2,p+6
内部一维存储空间根据地址单位
的不同,指针运算的走向形态。
⑶ a[0] 一维数组名单位
列,a[0]+1 相当于以一个
元素为存储单位 +1,表示
下一列地址;
⑷ p 是指向整型存储单
元的指针,相当于以元素
为单位,故 p+1 就是下移
一个元素位置。
以二维数组为例说明:
int a[3] [4],*p; p = *a;
2000 1 a[0] [ 0]
2004 2 a[0] [ 1]
2008 3 a[0] [ 2]
2012 4 a[0] [ 3]
2016 5 a[1] [ 0]
2020 6 a[1] [ 1]
2024 7 a[1] [ 2]
2028 8 a[1] [ 3]
2032 9 a[2] [ 0]
2036 10 a[2] [ 1]
2040 11 a[2] [ 2]
2044 12 a[2] [ 3]
a,a[0],p
a[0]+1,p+1
a+1,a[1]
a+2,a[2]
a[1]+2,p+6
内部一维存储空间根据地址单位
的不同,指针运算的走向形态。
⑸ 若 要 通 过 p 访问
a[I][j] 则应表示为以下形
式:
以行和列的观点解释多维数组的概念,
当数组为三维时,可在行和列的基础上
增加层或页的概念,因此不难理解指针
的计算及不同情况下的计算公式(根据
定义的指针数据结构与所给的初始地址
不同情况(层、行、列)而确定)。
不同维数的数组名所代表的地 址的单位不同,
其存取元素的方式也不同。
若以二维数组名对其下属元素进行存取,需
二次间址访问;
列地址行地址
转换
?? ???
相应存储单元的内容列地址 间址访问 ??? ???
行转换为列
以列为单位计算地址
间址访问
]1][2[)1)2(( aa 存取的元素为???? 存取的元素为
不同维数的数组名所代表的地 址的单位不同,
其存取元素的方式也不同。
若以一维数组名对其下属进行存取,一次间
址访问即可:
以列为单位计算地址
间址访问
]2][0[)2]0[ aa 存取的元素为(
值)相应存储单元的内容(列地址
间址访问
??
??? ???
指针变量作形参以 接受通过实参数组名传递来的地
址,有两种方法:
⑴ 用指向元素的指针变量(单位列);
⑵ 用指向一维数组的指针变量(单位行)。
不同维数的数组名所代表的地 址的单位不同,
其存取元素的方式也不同。
关键是形实结合时对应的地址计算
单位应匹配一致!!!
void aver age(fl oat *p,int n){
float *p_end,aver,sum=0;
for(p_end=p+n - 1; p<= p_end; p ++ ) sum=su m+(*p);
aver= sum/n; p ri ntf(" average =% 5,2f \ n",aver);
}
void searc h(float (*p)[4],int n){
int i ;
for(i =0; i <4; i ++ )
printf("% 5,2f ",*(* (p+n)+i ));
}
void mai n(){
static fl oat score[3][4]= {{65,67,70,60},
{80,87,90,81},{ 90,99,100,98}};
average ( *score,12 ); sear ch( score,2 );
}
[ 例 7 - 4 ] 有一个班,3 个学生,各学 4 门课程,计算总平均分及打印
第 3 名学生的成绩。
average=82.25
90.00 99.00 100.00 98.00
7.8 字符数组和指针
[ 例 7 - 5 ] 一个具有文字复制和比较功能的程序
#include <stdio.h>
void main(){
static char s1[13]="I love USTC!";
char s3[13],*s2="I love USTC";
void u_strcpy( char *s,char *t );
int u_strcmp( char *s,char *t );
u_strcpy( s3,s1 );
printf("%s %d \ n",s3,u_strcmp( s1,s2 ));
}
……
⑴ 字符常数在内存中以字符数组形式存储。
static char s1[13]="I love USTC!";
char s3[13],*s2="I love USTC";
说明,
2000
I □ l o v e □ U S T C ! \ 0□ □
2013
I □ l o v e □ U S T C \ 0□ □
⑵ 给字符数组赋初值首先分配存储空间,然
后采用逐个字符复制的形式。
static char s1[13]="I love USTC!";
2000
I □ l o v e □ U S T C ! \ 0□ □
s1
0 1 2 3 4 5 6 7 8 9 10 11 12
I □ l o v e □ U S T C ! \ 0□ □
void u_strcpy( char *s,char *t ){
w hil e((*s = *t) != ' \ 0'){
s++ ; t++ ;
}
}
s1
I □ l o v e □ U S T C ! \ 0
0 1 2 3 4 5 6 7 8 9 10 11 12
□ □
s3
0 1 2 3 4 5 6 7 8 9 10 11 12
t
main(){
……
u_strcpy( s3,s 1 );
…
}
S
s3
I □ l o v e □ U S T C ! \ 0□ □
int u_strcmp( char *s,char *t ){
for( ; *s== *t; s++,t++ )
if(*s= =' \ 0') r eturn(0);
return(*s - *t);
}
s1
I □ l o v e □ U S T C ! \ 0
0 1 2 3 4 5 6 7 8 9 10 11 12
□ □
2013
I □ l o v e □ U S T C \ 0
s2 2013
□ □
t
2013
传递
s
传递
main(){
……
u_strcmp( s1,s 2 )
…
}
字符数组,固定长度且一般需要预定义得大一
些,可能浪费内存空间。
字符串不但可以用字符数组表示,也可以用
字符指针变量来表示:
字符指针,内存空间可根据字符串实际长度确
定,节省内存空间。
7.9 指针运算
指针是指向某类型数据存储单元的地址,需
注意以下问题:
⑴ 变量、数组的地址分配是由编译程序决定 的,因
此不能将任意的整数向指针变量赋值;
⑵ 指针不是整数,但指针的值是具体类型的 特定变
量地址所允许的整 数,因此不能象整型变量那样对指
针进行所有的算术、逻辑和关系运算。
1) 算术运算,设 p,q 为指针,且指向同类型数据集合,
n 为整数,以下为合法且意义为:
⑶ 指针允许实施的运算:
p+n, p 指向同类型数据集合的当前位置后的第 n 个成员。
p - n, p 指向同类型数据集合的当前位置前的第 n 个成员。
编译程序实施的内部运算分别为:
p+n*( 类型所占字节的长度 )
p - n*( 类型所占字节的长度 )
p++, 当前指针位置后移一个成员,先以 p 指针作即时
操作数,然后自增。
-- p, 当前指针位置前移一个成员,先自减,然后以自
减后的 p 作为即时操作数。
++ p, 当前指针位置后移一个成员,先自增,然后以自
增后的 p 作为即时操作数。
p —, 当前指针位置前移一个成员,先以 p 指针作即时
操作数,然后自减。
p - q, 其值表示 p,q 之间的成员个数,其值为整数,而
不是指针。
2) 关系运算,( 前提是指针必须是指向同一类型的数
据),若 <,<=,==,!=,>=,> 等运算符两边的操作数是
指向同一数据集合(如数组、结构等),就成为有
意义的比较。
p<q, 表示 p,q 为指向同一数组中的两个不同的元素,
它表明如果 p 所指向的元素在 q 所指元素之前时表达式
值为 ‘ 真 ’,否则为 ‘ 假 ’,如果 p,q 指向不同数组或
其它不同的数据集合则无意义。
3) 赋值运算(常用于给指针赋初值), 若设 p,q 为指针,
且指向同类型数据集合,n 为整数,则
p=q,p=q+n,p=q - n,p+=n,p - =n 均有效。
4) 指针的特殊值( NULL ), NULL 其实就是整数 0,
但习惯上通过 define 定义为 NULL 。
# define NULL 0
C 语言允许指针为零值,它作为指针的异常
事件的标志。但保证有效指针不会是 NULL 值。
若动态申请内存空间失败,则返回值为 NULL 。
字符指针交换排序演示
Fol loe meFol loe me \\ 00
BAS ICBAS IC \\ 00
Gre at w allGre at w all \\ 00
FORTRA NFORTRA N \\ 00
Comp uter designComp uter design \\ 00
nam enam e
0
1
2
3
4
void mai n(){
static c har *name[]={"Fll o w me"," BAS IC ",
"Great w all "," FOR TRA N","C omputer design"} ;
int n=5;
sort(name,n); print(name,n);
}
说明:
① 字符串常数以字符数
组形式存放在某一确定
地址的内存空间。
② 指针数组赋初值,实
际上是将每个字符串的
首地址传送给相应的字
符指针数组的元素。
nam e
0 Fol loe meFol loe me \\ 00
BAS ICBAS IC \\ 00
Gre at w allGre at w all \\ 00
FORTRA NFORTRA N \\ 00
Comp uter designComp uter design \\ 00
1
2
3
4
i
k
j
void sort( char *name[],i nt n ){
char *t; i nt i,j,k;
for(i =0; i <n - 1; i+ +){
k=i ;
for(j= i+ 1; j<n; j+ +)
if(strc mp(name[k],n ame[j]) > 0 ) k=j;
if(k!= i){t= name[i]; name[i ]=name[k]; name[k]=t;}
}}
选择排序过程一
nam e
0 Fol loe meFol loe me \\ 00
BAS ICBAS IC \\ 00
Gre at w allGre at w all \\ 00
FORTRA NFORTRA N \\ 00
Comp uter designComp uter design \\ 00
1
2
3
4
i
k
j
void sort( char *name[],i nt n ){
char *t; i nt i,j,k;
for(i =0; i <n - 1; i+ +){
k=i ;
for(j= i+ 1; j<n; j+ +)
if(strc mp(name[k],n ame[j]) > 0 ) k=j;
if(k!= i){t= name[i]; name[i ]=name[k]; name[k]=t;}
}}
选择排序过程二
nam e
0 Fol loe meFol loe me \\ 00
BAS ICBAS IC \\ 00
Gre at w allGre at w all \\ 00
FORTRA NFORTRA N \\ 00
Comp uter designComp uter design \\ 00
1
2
3
4
i
k
j
void sort( char *name[],i nt n ){
char *t; i nt i,j,k;
for(i =0; i <n - 1; i+ +){
k=i ;
for(j= i+ 1; j<n; j+ +)
if(strc mp(name[k],n ame[j]) > 0 ) k=j;
if(k!= i){t= name[i]; name[i ]=name[k]; name[k]=t;}
}}
选择排序过程三
nam e
0 Fol loe meFol loe me \\ 00
BAS ICBAS IC \\ 00
Gre at w allGre at w all \\ 00
FORTRA NFORTRA N \\ 00
Comp uter designComp uter design \\ 00
1
2
3
4
i
k
j
void sort( char *name[],i nt n ){
char *t; i nt i,j,k;
for(i =0; i <n - 1; i+ +){
k=i ;
for(j= i+ 1; j<n; j+ +)
if(strc mp(name[k],n ame[j]) > 0 ) k=j;
if(k!= i){t= name[i]; name[i ]=name[k]; name[k]=t;}
}}
选择排序过程四
nam e
0 Fol loe meFol loe me \\ 00
BAS ICBAS IC \\ 00
Gre at w allGre at w all \\ 00
FORTRA NFORTRA N \\ 00
Comp uter designComp uter design \\ 00
1
2
3
4
i
k
j
void sort( char *name[],i nt n ){
char *t; i nt i,j,k;
for(i =0; i <n - 1; i+ +){
k=i ;
for(j= i+ 1; j<n; j+ +)
if(strc mp(name[k],n ame[j]) > 0 ) k=j;
if(k!= i){t= name[i]; name[i ]=name[k]; name[k]=t;}
}}
选择排序过程结束
nam e
0 Fol loe meFol loe me \\ 00
BAS ICBAS IC \\ 00
Gre at w allGre at w all \\ 00
FORTRA NFORTRA N \\ 00
Comp uter designComp uter design \\ 00
1
2
3
4
void mai n(){ …… print(name,n); …… }
print(char *name[],i nt n ){
int i ;
for(i =0; i <n; i ++ ) printf(, %s \ n,,n ame[i ]);
}
运行结果,
BASIC
Computer design
FORTRAN
Fllow me
Great wall
void mai n(){
static c har *name[4]={" w ang"," z h ang","li","chen" };
char **p; int i ;
p=name;
for(i =0; i <4; i ++ )
printf(" % s \ n",*p++ );
p=p - 4;
printf(" % c % c \ n",*n ame[1],**(p+1));
printf(" % c % c \ n",*( name[1]+1),*(*(p+ 1)+1));
}
wang \ 0
zhang \ 0
li \ 0
chen \ 0
name[0]
name[1]
name[2]
name[3]
name
p
void mai n(){
static c har *name[4]={" w ang"," z h ang","li","chen" };
char **p; int i ;
p=name;
for(i =0; i <4; i ++ )
printf(" % s \ n",*p++ );
p=p - 4;
printf(" % c % c \ n",*n ame[1],**(p+1));
printf(" % c % c \ n",*( name[1]+1),*(*(p+ 1)+1));
}
? 相对于格式符 ? % s ?, 二级
指针还须间接访问一次,一
级指针直接访问即可。
? name[1] 相当于一维数组
的起始地址单位列,再一次
间址访问取得该相应串的第
一个字符 ? Z ?, **(p+1) 同理,
void mai n(){
static c har *name[4]={" w ang"," z h ang","li","chen" };
char **p; int i ;
p=name;
for(i =0; i <4; i ++ )
printf(" % s \ n",*p++ );
p=p - 4;
printf(" % c % c \ n",*n ame[1],**(p+1));
printf(" % c % c \ n",*( name[1]+1),*(*(p+ 1)+1));
}
运行结果,
wang
zhang
li
chen
z z
h h
void mai n(){
static c har *name[4]={" w ang"," z h ang","li","chen" };
char **p; int i ;
p=name;
for(i =0; i <4; i ++ )
printf(" % s \ n",*p++ );
p=p - 4;
printf(" % c % c \ n",*n ame[1],**(p+1));
printf(" % c % c \ n",*( name[1]+1),*(*(p+ 1)+1));
}
(2) 命令行参数(指针数组作 main 函数的形参)
? 在系统提示符后键入的使计算机操作系统完成一定
任务的输入行称为命令行。一般形式(一 UNIX 系统为
例):
$ 命令名 ( 参数 0) 参数 1 参数 2 ?? 参数 n
$cp oldfile newfile
$vi file.c
$cc file.c - o exec_filename
例
参数 0 参数 1 参数 2 参数 3 (共 4 个参数)参数 参数 参数 参数 (共 个参数)
? C 程序 main() 函数的两种形式:
1) 不带参数的主函数,编译后生成一个不带参数的
shell 命令。
main( ){ ?? }
2) 带参数的主函数,编译后生成一个带参数的 s he l l
命令。
main( int argc,char *argv[] ){ ?? }
argc, 表示命令行参数的个数;
argv, 指向命令参数的指针数组,大小由 argc 确定。
#include <stdio.h>
void main(int argc,char *argv[]){
int i;
for(i=1; i<argc; i++)
printf("%s%c",argv[i],(i<argc - 1?' ':' \ n'));
}
[ 例 10 - 6 ] 以 display.c 为例说明,其功能是对 display
后的参数内容重复输出回显。
$ cc display.c - o display
$display How are you!
How are you!
编译
运行
运行
时数
据状
态:
4
display \ 0
How \ 0
are \ 0
you! \ 0
argc
Argv[0]
Argv[1]
Argv[2]
Argv[3]
#include <stdio.h>
void main(int argc,char *argv[]){
int i;
for(i=1; i<argc; i++)
printf("%s%c",argv[i],(i<argc - 1?' ':' \ n'));
}
$display How are you!
max (int x,i nt y ){
return( (x >y)? x, y );
}
test(int (*t)(),i nt a,int b ){
return( (*t)(a,b) );
}
main(){
int a,b,c,d,e,(*p)();
scanf(" % d % d ",&a,& b);
p=max ;
c= (*p)(a,b);
d=test(p,a,b);
e= max (a,b );
printf(" % d % d % d % d % d \ n",a,b,c,d,e);
}
? 函数名代表该函
数的入口地址
? 指向函数的指针
类型应与所指函数
类型匹配一致。
运行结果:
input,1 3
output,1 3 3 3 3
float *sear ch( float (*poi nter)[4],i nt n ){
float *pt; pt=*( poi nter + n );
return(pt);
}
void mai n(){
static fl oat score[ ][4]= {{60,70,80,90},
{56,89,90,95},{ 75,80,85,90} };
float *p; int i,m;
scanf(" % d ",&m);
p=searc h( score,m );
for(i =0; i <4; i ++ )
printf("% 7,2f",*(p +i ));
}
运行结果:
input,1
output,56.00 89.00 90.00 95.00
# include <stdio.h>
#include <math.h>
#define EPS 1e - 7
void main(){
double udf_sin(double x); // 用户自定义函数原型说明
double a; scanf("%lf",&a);
printf("%f %f \ n",udf_sin(a),sin(a));
}
double udf_sin( double x ){ // 用户自定义函数
double sum,term,n=1; sum=term=x;
while( fabs(term) > EPS ){
n=n+1;
term=term*( - x*x)/((2*n - 2)*(2*n - 1));Th e end
培 育 英 才 钻 研 科 学
书山有径勤为路
学海无边苦作舟
书山有径勤为路书山有径勤为路
学海无边苦作舟学海无边苦作舟