C程序设计语言5Word文档格式.docx
- 文档编号:21352264
- 上传时间:2023-01-29
- 格式:DOCX
- 页数:52
- 大小:98.68KB
C程序设计语言5Word文档格式.docx
《C程序设计语言5Word文档格式.docx》由会员分享,可在线阅读,更多相关《C程序设计语言5Word文档格式.docx(52页珍藏版)》请在冰豆网上搜索。
Y、=*ip;
*ip=0;
ip=&z【0】;
ip现在指向x+/
y的值现在为l+/
x的值现在为04/
ip现在指向Z[0]4/
变量x、Y与Z的声明方式我们已经在前面的章节中见到过。
我们来看指针ip的声明,如
下所示:
int·
这样声明是为了便于记忆。
该声明语句表明表达式*ip的结果是int类型。
这种声明变量的语
法与声明该变量所在表达式的语法类似。
同样的原因,对函数的声明也可以采用这种方式。
例如,声明
double*dp,atof(char·
);
表明,在表达式中,*dp和atof(S)的值都是double类型,J王atof的参数是一个指向]char
类型的指针。
我们应该注意,指针只能指向某种特定类型的对象,也就是说,每个指针都必须指向某
种特定的数据类型。
(一个例外情况是指向v。
id类型的指针可以存放指向任何类型的指针,
但它不能间接引用其自身。
我们将在5.11节中详细讨论该问题)。
如果指针ip指向整型变量x,那么在x可以出现的任何上下文中都可以使用4ip,因此,
语句
。
●
*ip;
*ip+10;
将把*ip的值增iJIl0。
.
一元运算符木和&
的优先级比算术运算符的优先级高,因此,赋值语句
Y2*ip+1、
将把*ip指向的对象的值取出并加l,然后再将结果赋值给Y,而下列赋值语句:
*ip+=1
回则将ip指向的对象的值加l,它等同于
++·
ip’
或
(*ip)++
语句的执行结果。
语句(术iD)十+中的圆括号是必益的,否则,该表达式将对ip进行加二运算,
而不是塑ip指向的对象进行地二蓝算,这是因为,类似于术和++这样的一元运算符遵循从右
l
指针与数纽
至左的结合顺序。
最后说明一点,由于指针也是变量,所以在程序中可以直接使用,而不必通过间接引用
的方法使用。
例如,如果iq是另一个指向整型的指针,那么语句
iq=ip
将把ip中的值拷贝到iq中,这样,指针iq也将指向ip指向的对象。
5.2指针与函数参数
由于9蔓盖星丛传值的方煎!
睦数值传递给被调用函数,因此,被调用函数不能直接修改
主调函数中变量的值。
例如,排序函数可能会使用二个名为swap的函数来交换两个次序颠倒
的元素。
但是,如果将swap函数定义为下列形式:
厂———————————————~九
ivoidswap(intX。
intY),/4错误定义的函数+/J
T—————————————√、
inttemp:
temp=X;
X2Y;
Y。
temp;
则下列语句无法达到该目的。
’swap(a,b);
这是因为,由于参数传递采用传值方式,因此上述的swap函数不会影响到调用它的例程中的
参数a和b的值。
该函数这堡交换了a和b的副本的值。
,
—’0-o-_-●o__l-___00●-0■__-____-_-一
那么,如何实现我们的目标呢?
可以使主调程序将指向所要交换的变量的指针传递给被
调用函数,即:
swapC&a。
&b);
由于一元运算符&
用来取变量的地址,这样&
a就是一个指向变量a的指针。
swap函数的所有
参数都声明为指针,并且通过这些指针来间接访问它们指向的操作数。
眄习
卜、————————————-、
lVoidswap(int*px。
py'
1/+交换+px和4PY+/。
1。
————————————一
$nttemp;
temp。
*px;
●px。
●PY:
*Py。
temp:
’
我们通过图5—2进行说明。
塑针参数使堡垫塑盥数能够访问和修改主诅函数中对象的值。
我们来看这样一个例子:
函数9etint接受自由格式的输入,并执行转换,将输入的字符流分解成整数,且每次调用得
到一个整数。
9etint需要返回转换后得到的整数,并且,在到达输入结尾时要返回文件结
标记。
这些值必须通过不同的方式返回。
EOF(文件结束标记)可以用任何值表示,当然也
可用一个输入的整数表示。
在主调函数中:
在
图5—2
可以这样设计该函数:
将标识是否到达文件结尾的状态作为9etint函数的返回值,同时,
使用一个指针参数存储转换后得到的整数并传回给主调函数。
函数scanf的实现就采用了这
种方法,具体细节请参见7.4节。
下面的循环语句调用getint函数给一个整型数组赋值:
intn.array[SIZE]。
getint(int·
).,
forInt0:
n《SIZE&&
getint(&
array[n])1;
EOF;
n++J
;
每次调用9etint时,输入流中的下一个整数将被赋值给数组元素array【n],同时,n的值
将增加1。
请注意,这里必须将array[n]的地址传递给函数9etint,否则函数getint将
无法把转换得到的整数传回给调用者。
该版本的9etint函数在到达文件结尾时返回EoF,当下一个输入不是数字时返回0,当
陌习输入中包含一个有意义的数字时返回一个正值。
#include<
ctype.h>
intgetch(void);
voidungetch(int):
/+getint函数:
将输入中的下一个整型数赋值给+pn+/
intgetint(int.pn)
{
intc。
sign;
while.I≤》(c—geteh()))/+跳过空白符4/
if(1isdiglt(c)&
&
cl_EOF&_&
cl-’+’&矗eIt’·
’){
ungetch(c);
/+输入不是一个数字+/
return0;
J
指针与教纽
)
signt(ct一’-’)7—1:
1:
if(c一·
’+’:
:
ct-’一’)
c。
getch();
for(*pn-0;
isdiglt(c);
ctgetch())
.pn_10·
-pn+(c一’0’);
*pn·
tsign:
if(cI=EOF)
returnC;
}
在getint函数中,幸pn始终作为一个普通的整型变量使用。
其中还使用了9etch和
ungetch两个函数(参见4.3节),借助这两个函数,函数getint必须读人的一个多余字符
就可以重新写回到输入中。
练>
U5-1在上面的例子中,如果符号+或一的后面紧跟的不是数字,getint函数将把符
号视为数字0的有效表达方式。
修改该函数,将这种形式的+或一符号重新写回到输入流中。
练习5-2模仿函数9etint的实现方法,编写一个读取浮点数的函数9etfloat。
getfloat函数的返回值应该是什么类型?
5.3指针与数组
在c语言中,指针和数组之间的关系十分密切,因此,在接下来的部分中,我们将同时讨
论指针与数组。
通过数组下标所能完成的任何操作都可以通过指针来实现。
一般来说,用指
针编写的程序比用数组下标编写的程序执行速度快,但另一方面,用指针实现的程序理解起
来稍微困难一些。
臣口
声明
Int“10】;
定义了一个长度为l0的数组a。
换句话说,它定义了一个由10个对象组成的集合,这l0个对象
存储在相邻的内存区域中,名字分别为a[0]、a[1]、…、a[9](参见图5—3)。
a:
[工]二[工]][[丁卫
^【O】■【1】-【9】
图5—3
a[i]表示该数组的第i个元素。
如果pa的声明为
int*pa;
则说明它是一个指向整型对象的指针,那么,赋值语句
pa-&“0】;
‘
则可以将指针pa指向数组a的第0个元素,也就是说,pa的值为数组元素a[0]的地址(参见
图5—4)。
这样,赋值语句
x_·
pa;
pa:
a【0】
图5—4
将把数组元素a[0]中的内容复制到变量X中。
如果pa指向数组中的某个特定元素,那么,根据指针运算的定义,pa+1将指向下l个
、‘-,,’——’——————————一—'
-。
、
蓝pa+i将指向pa所指向数组元素之后的第i个霾素,而pa—i将指向pa所指向数组元
素之前的第i个元素。
因此,如果指针pa指向a[0],那么木(pa+1)引用的是数组元素
a[1]的内容,pa+i是数组元素a[i]的地址,术(pad)引用的是数组元素a[i幽容
(参见图5—5)。
图5.5
无论数组a中元素的类型或数组长度是什么,上面的结论都成立。
“指针加l”就意味着,
pa+1指向pa所指向的对象的下一个对象。
相应地,pa+i指向pa所指向的对象之后的第i个
乜纠对象。
c。
下标和指针运算之间具有密切的对应关系。
根据定义,数组类型的变量或表达式的值是
该数组第0个元素的地址。
执行赋值语句
pa薯&
a【0】;
后,pa和a具有相同的值。
因为数组名所代表的就是该数组最开始的一个元素的地址,所以,
赋值语句pa=&
a[0]也可以写成下列形式:
pa=a;
对数组元素a[i]的引用也可以写成半(a+i)这种形式。
对第一次接触这种写法的人来说,
可能会觉得很奇怪。
在计算数组元素a[i]的值时,C语言实际上先将其转换为t(a+i)的形式,
然后再进行求值,因此在程序中这两种形式是等价的。
如果对这两种等价的表示形式分别施
加地址运算符&
,便可以得出这样的结论:
a[i]和a+i的含义也是相同的。
a+i是a之后第i
指针s数组
个元素的地址。
相应地,如果pa是一个指针,那么,在表达式中也可以在它的后面加下标。
3a[i]与t(pa+i)是等价的。
简而言之,一个通过数组和下标实现的表达式可等价地通过指
针和偏移量实现。
但是,我们必须记住,数组名和指针之间有一个不同之处。
指针是一个变量,因此,在C
浯言中,语句pa=a和pa++都是合法的。
但数组名不是变量,因此,类似于a=pa和a++形式
的语句是非法的。
当把数组名传递给一个函数时,实际上传递的是该数组第一个元素的地址。
在被调用函
数中,该参数是一个局部变量,因此,数组名参数必须是一个指针,也就是一个存储地址值
的变量。
我们可以利用该特性编写strlen函数的另一个版本,该函数用于计算一个字符串的
长度。
/4Strlen函数:
返回字符串S的长度+/
intstrlen(char·
s)
‘
intn;
forIn昌0;
·
sI=’\0’;
s++)
n++;
returnn;
因为S是一个指针,所以对其执行自增运算是合法的。
执行S++运算不会影响到Strlen函数
的调用者中的字符串,它仅对该指针在Strlen函数中的私有副本进行自增运算。
因此,、类似
于下面这样的函数调用:
7
strlen(”hell0,world”);
/4字符串常量+/
strlen(array):
/4字符数组array有100个元素+/
strlen(ptr);
/+ptr是一个指向char类型对象的指针+/
部可以正确地执行。
在函数定义中,形式参数
char8【】;
和
是等价的。
我们通常更习惯于使用后一种形式,因为它比前者更直观地表明了该参数是一个
指针。
如果将数组名传递给函数,函数可以根据情况判定是按照数组处理还是按照指针处理,
随后根据相应的方式操作该参数。
为了直观且恰当地描述函数,在函数中甚至可以同时使用
故组和指针这两种表示方法。
也可以将指向子数组起始位置的指针传递给函数,这样,就将数组的一部分传递给了函
牧。
例如,如果a是一个数组,那么下面两个函数调用
fl&a【2】)
与
圈
f(a+2)7
都将把起始于a[2]的子数组的地址传递给函数f。
在函数f中,参数的声明形式可以为
f(intaEE【】){…)
或
f(int*aEr){…,
对于函数f来说,它并不关心所引用的是否只是一个更大数组的部分元素。
如果确信相应的元素存在,也可以通过下标访问数组第一个元素之前的元素。
类似于
P【-1]、P卜2]这样的表达式在语法上都是合法的,它们分别引用位于p[0]之前的两个元素。
当然,引用数组边界之外的对象是非法的。
5.4地址算术运算
如果P是一个指向数组中某个元素的指针,那么P++将对P进行自增运算并指向下一个元
素,而p+=i将对P进行加i的增量运算,使其指向指针P当前所指向的元素之后的第i个元素。
这类运算是指针或地址算术运算中最简单的形式。
C语言中的地址算术运算方法是一致且有规律的,将指针、数组和地址的算术运算集成在
一起是该语言的一大优点。
为了说明这一点,我们来看一个不完善的存储分配程序。
它由两
个函数组成。
第一个函数alloc(n)返回一个指向n个连续字符存储单元的指针,alloc函数
的调用者可利用该指针存储字符序列。
第二个函数afree(P)释放已分配的存储空间,以便
以后重用。
之所以说这两个函数是“不完善的”,是因为对afree函数的调用次序必须与调用
1100lallOC函数的次序相反。
换句话说,alloc与afree以栈的方式(即后进先出的列表)进行
存储空间的管理。
标准库中提供了具有类似功能的函数malloc和free,它们没有上述限制,
我们将在8.7节中说明如何实现这些函数。
最容易的实现方法是让allOC函数对一个大字符数组allocbuf中的空间进行分配。
该
数组是allOC和afree两个函数私有的数组。
由于函数alloc和afree处理的对象是指针而
不是数组下标,因此,其他函数无需知道该数组的名字,这样,可以在包含alloc和afree
的源文件中将该数组声明为StatiC类型,使得它对外不可见。
实际实现时,该数组甚至可以
没有名字,它可以通过调用malloc函数或向操作系统申请一个指向无名存储块的指针获得。
a110Cbuf中的空间使用状况也是我们需要-『解的信息。
我们使用指针allocp指向
a110Cbuf中的下一个空闲单元。
当调用all0C申请n个字符的空间时,all0C检查
allocbuf数组中有没有足够的剩余空间。
如果有足够的空闲空间,贝r]alloc返回allocp的
当前值(即空闲块的开始位置),然后将allocp力In以使它指向下一个空闲区域。
如果空闲
空间不够,则all0C返回0。
如果P在a110cbuf的边界之内,则afree(P)仅仅只是将
allocp的值设置为P(参见图5—6)。
#defineALLOCSIZEl0000/+可用空间大小4/
staticcharallocbuf[ALLOCSIZE]:
/+alloc使用的存储区+/
staticchar.allocp2a110cbuf;
/+下一个空闲位置4/
char*alloc(intn)/+返回指向n个字符的指针+/
if(allocbuf+ALLOCSIZE—allocp)-2n){/+有足够的空闲空间+/
allocp+窜n:
retur—nallocp—n;
/4分配前的指针p8/
)else/+空闲空间不够+/
'
voidafree(char·
p)/+释放P指向的存储区+/
if(p>
=allocbuf&
p《allocbuf+ALLOCSIZE)
allocp。
p;
调用allOC之前:
allocp:
almbur:
[工二口二[二二二二二二]
_已使用—_+_空闲————一.
调用allOC之后:
all。
cbur:
[[二口二[二]二二二二]
_已使用H_空闲——一.
图5—6
一般情况下,同其他类型的变量一样,指针也可以初始化。
通常,对指针有意义的初始
七值只能是0或者是表示地址的表达式,对后者来说,表达式所代表的地址必须是在此前已定
泛的具有适当类型的数据的地址。
例如,声明
staticchar*allocp2allocbuf;
辱allocp定义为字符类型指针,并将它初始化为allocbuf的起始地址,该起始地址是程序
jl【行时的下一个空闲位置。
上述语句也可以写成下列形式:
’staticchar*allocp2&
allocbuf[0】;
茎是因为该数组名实际上就是数组第0个元素的地址。
下列if测试语句:
if(allocbuf+ALLOCSIZE—allocp>
=n){/+有足够的空闲空间+/
釜查是否有足够的空闲空间以满足n个字符的存储空间请求。
如果空闲空间足够,则分配存储
!
间后allocp的新值至多比allocbuf的尾端地址大l。
如果存储空间的申请可以满足,
lloc将返回一个指向所需大小的字符块首地址的指针(注意函数本身的声明)。
如果申请无
满足,allOC必须返回某种形式的信号以说明没有足够的空闲空间可供分配。
℃语言保证,
永远不是有效的数据地址,因此,返回值0可用来表示发生了异常事件。
在本例中,返回值D
回
表示没有足够的空闲空间可供分配。
指针与整数之间不能相互转换,但O是惟一的例外:
常量0可以赋值给指针,指针也可以
和常量0进行比较。
程序中经常用符号常量NULL代替常量0,这样便于更清晰地说明常量0是
指针的一个特殊值。
符号常量NuLL定义在标准头文件<
Stddef.h>
中。
我们在后面部分经常
会用NNULL。
类似于.
if(allocbuf+ALLOCSIZE—allocp'
zn){/4有足够的空闲空间+/
以及
if(P>
=a110cbuf&&P《allocbuf+ALLOCSIZE)
的条件测试语句表明指针算术运算有以下几个重要特点。
首先,在某些情况下对指针可以进
行比较运算。
例如,如果指针p和q指向同一个数组的成员,那么它们之间就可以进行类似于
==、!
=、<
、>
=的关系比较运算。
如果P指向的数组元素的位置在q指向的数组元素位置之前,
那么关系表达式
P‘q
匝圈的值为真(true)。
任何指针与0R行相等或不等的比较运算都有意义。
但是,指向不同数组
的元素的指针之间的算术或比较运算没有定义。
(这里有一个特例:
指针的算术运算中可使用
数组最后一个元素的下一个元素的地址。
)
其次,我们从前面可以看到,指针可以和整数进行相加或相减运算。
例如,结构
P+n
表示指针P当前指向的对象之后第n个对象的地址。
无论指针P指向的对象是何种类型,上述
结论都成立。
在计算p+n时,lq将根据p指向的对象的长度按比例缩放,而b指向的对象的长度
则取决于p的声明。
例如,如果int类型占4个字节的存储空间,那么在int类型的计算中,
对应的n将按4的倍数来计算。
指针的减法运算也是有意义的:
如果P和q指向相同数组中的元素,且P<
q,那么q—p+l
就是位于P和q指向的元素之间的元素的数目。
我们由此可以编写出函数strlen的另一个版
本,如下所示:
/+Strlen函数:
返回字符串S的长度+/t
intstrlen(char·
B)
{
char●p=S:
while(·
pl=’\0’)
p++;
returnP一8:
在上述程序段的声明中,指针P被初始化为指向S,即指向该字符串的第一个字符。
whileN
环语句将依次检查字符串中的每个字符,直到遇到标识字符数组结尾的字符’\0’为止。
由于
P是指向字符的指针,所以每执行一次p++,P就将指向下一个字符的地址,P—S则表示已经
检查过的字符数,即字符串的长度。
(字符串中的字符数有可能超过int类型所能表示的最大
范围。
头文件<
中定义的类型ptrdiff_t足以表示两个指针之间的带符号差值。
但是,我们在这里使用SiZe,t作为函数Strlen的返回值类型,这样可以与标准库中的函数
版本相匹配。
SiZe_t是由运算符Sizeof返回的无符号整型。
指针的算术运算具有一致性:
如果处理的数据类型是比字符型占据更多存储空间的浮点
类型,并且P是一个指向浮点类型的指针,那么在执细++后,P将指向下一个浮点数的地址。
因此,只需要将alloc和afree函数中所有的char类型替换为float类型,就可以得到一个
适用于浮点类型而非字符型的内存分配函数。
所有的指针运算都会自动考虑它所指向的对象
的长度。
有效的指针运算包括相同类型指针之间的赋值运算;
指针同整数之间的加法或减法运
茸;
指向相同数组中元素的两个指针间的减法或比较运算;
将指针赋值为0或指针与0之间的
比较运算。
其他所有形式的指针运算都是非法的,例如两个指针间的加法、乘法、除法、移
直或屏蔽运算;
指针同float或double类型之间的加法运算;
不经强制类型转换而直接
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 程序设计语言