§1.2.1 指 针
指针是 C语 言的基本概念,C语 占中指针 尢处不住c实 际上,每 种数据类Ⅱ刂T,都 有本H 应 的指 向 T的 指针类型。指针类型变甘存放的值,实 际 上就足内存地山|∶。指针类 Ⅱ刂有两个鼓 基本的操作:
&取 地址操作
★去引用 (间接 引用 )操 作 给定如 卜声明语句:
我们知道 土是整型变量 ,而 p土足指 向整数的指针 。如呆令:
第 Ι章 基本概念
p土 = &土 ;
则 &⊥返 冂 ⊥的地址并把它赋值给 p土。要 为 i赋 值 ,可 以写
△ =△ 0氵
或者
以上两条赋值语句都把整数值 10存 储在 土之 中。第工 条赋值语句 p⊥ 前 的 ★足 上引 用,10并 未存入指针里 ,而 是存 入指针 pi指 向的存储 单元之 中。
对指针还可 以做其它操作 。指针 的值可 以用来赋值给指针变蚩 。指针 足一个△负整数, 冈而 C允 许指针做算术运算 ,包 括加 、减 、乘 、除。指针之 间可 以做 比较 ,结 呆返回人 J=、小 于、相等二者乏一。指针还可 以通过强制显式地转换成整数。
指针变蚩 的 κ度在不 同的计算机 中可 以取不 同值 。有时,指 针变 筻的 K度 在同一 台计算 机 中也会不 同。例如 ,指 向 char的 指针变量 长度也 许比指 向 n。 a△的指针变蚩 长度更 κ。C 用特殊 的值 NuLL表 示空指针 。空指针不指 向变董 ,也 不指 向函数 。对 1・贝体系统 ,空 指针川 整数值 0表 示 ,C中 bluLL是 一个宏 ,具 体实现就定义为常鞋值 0。空指针可川在关 系表达式 中,表 示布尔童“假”
,因 而 ,测 试空指针 的语句可 以是:
土£ (p土 == NULL)
或者可 以是更简洁 的形式 :
土£ (!p土 )
§1.2.2 动 态存储分配
程序在运行时需要 中请存储空间,用 来存放信息,但 在编程阶段 ,我 们 并不知道稆序在 运行时需耍 多人空问 (例 如 ,数 组大小可能依赖 丁输入数据 ),也 不想韦先预 留一块 △常人 的区域 ,冈 为其中很多空间也 许根本就不会用到 。针对这个 问题 ,C语 言捉供 丁一套机制 , 可 以在程序运行时分配存储空间,这 块区域称为系统堆 (heap)。 如呆需耍新 的存储空问,我 们可 以调用 函数 ma11。 c中 请所需大小的一块 内存空间。如呆当前系统存在空闲 内存 ,那 么 ma11oc函 数返冂指 向这块空闲内存起始地址 的指针 ;反 乏 ,如 果当前 系统没有空闲内存 ,则
函数 ma11。 c返 冂指针 NuLL。 以后如果不再需耍这块存储空问,可 以调用 函数 free把 它释 放 ,交 还给 系统 。一 口^一块存储空间被释放,就 不应再用它。程序 1.1是 中请 、释放存储空间 的例Lf,注 意指针的用法。
稆序 1.1凋 用 ma11。 c的 参章分别对应类型 ht和 类型 ⒒oa△ 的存储空闸人小,返 冂值楚 指 向这块存储空间第一个字节 的指针 。这个 函数的返冂类型对不同F向系统可能不同,有 些系 统返 冂 char★ ,即 指 向 char的 指针 。但 ANSI C返 冂 vo土d★ 。标记 (土n△ *)和 "⒒ oa△ *) 足类 型强制转换表达式 ,在 程序 1.1中 本来可 以缺省 ,这 两个标 记把返冂类 型变换为正确的 类型。free函 数把 以前用 ma11。 c申 请 的存储空间释放 。在有些版本的 C语 亩中,free耍 求 的参董类 型是 char★ ,而 ANSI C要 求 的参蚩类型足 vo土d★ 。通常 ,£ ree的 参耄类型转 换都是缺省的。
Ⅲ.2指 针和动态存储分配
i n t i , * P i ; f l o a t f , * P f ;
p i = ( i n t * ) m a l l - o c ( s i z e o f ( i n t ) ) ; p f = ( f I o a t * ) m a l l o c ( s i z e o f ( f t o a t ) ) ;
* p i 1 0 2 4 ;
* p f = 3 . L 4 ;
p r i n t f ( " a n - i n t e g e r - = - ? d , - a - f 1 o a t , = - ? f \ n " , * p i , * P f ) ; f r e e ( p i ) ;
f r a a l n f \ L - / ,\
程序 1.1分 配、释放存储 空间
如 杲存储空问不足 ,调 川 ma11⊙c会 使 巾活失败,以 卜给 出的代码 吏可车 ,可 以用来替 换稆{1,1中 调川 ma11。c的 相应代码 。
土 £ ((p土 = (土 nt *)ma11OC(g土 zeo£ (土 nt))) == NULL ||
(p£ = (£ △° at ★ )ma11○ c(臼 土zeof(f1oa△ ))) == NULL) ( fprintf(stderr` "Insu£ £ 土c土 ent凵 mem0ry")`
ex土 t(EXIT FAILURE)氵
)
或使 用 以 卜等价 的代 码
土f (!(p土 = (土 nt ★ )ma11OC(臼 土zeo£ (土n△ ))) ||
!(pf = (£ 1° at ★ )ma11。 C(臼 土zeof(£ △。at)))) (
£pr土 ntf(stderr` "Insuffic土 ent凵 mem⊙ ry")`
exit(EX工 T FAILURE)`
)
囚为 ma1・1Oc在 程 序 中经 常 出现 ,方 便 的做 法 楚定 义宏语 句,在 宏 评扌句 中 调用 ma11。 c, 如 呆 ma11oc失 败 则退 出 。以 卜是这 种 宏讶i句 的 一
种实现 :
#de£ 土 ne rˇ 】ALLoc(p`s) \
土 f (!(p) = ma11⊙ C(s))) (\
£pr⊥ nt£ (stderr` "Insuff土 c⊥ ent凵 mem○ ry")氵 \ ex土 t(EXIT FAILURE)` \
)
现 在 ,我 们可 以川两 条语 句替换 稆 序 1,1屮 凋川 ma11。 c的 语 句 。
MALLOC(p土 ` g土 zeof(土 n△ ))`
MALL⊙ C(pf` s土 zeo£ (f△ oat))氵
在稆序 1.1中 紧接 print£ 语句之后插入如 卜一行 :
pf = (f1° at ★ ) ma11⊙ C(臼 土zeof(f1。 a△ ))氵
第 】章 基本概念 这时,现 指针指 向的存储空间,不 再足存放 3,14的 存储 弟元。现在无法再访 问原来那块存储 空间。这是悬空引用 (dangling reference)的 一个例fr。 只要指向动态存储区域的指针丢掉 了,原 存储 区域对稆序而言 ,就 丢掉 了。在稆序中使用指针和动态存储分配时,应 牢记一点, 如呆不再需要一块动态存储空间,那 么一定要把它还给 系统。
§1.2.3 指 针 隐患
C程 序 中让所有 尚未指 向实际 日标 的指针都取 NuLL值 是好 习惯 。这样做 ,可 以尽董避 免访 问一块 尚未 中请 的空问,或 访 问一块我们并无权限访 问的空问。有些计算机在涉及空指 针操作时,返 冂 NuLL,能 够接着执行 ,而 另一些系统,将 直接对地址单元 0操 作,引 发严重 错误 。
另一个好习惯是,在 转换指针类型时,显 式地使用强制类I刂转换,例 如:
p土 = ma1△ oc(吕 土zeo£ (土nt));
/★ aSs工 gn L。 pェ a po立 nter ε o 1nt ★ /
pf = (£ △。at ★ ) p土 氵
/★ caStS an Ⅰ nt poInter ε o a f王 oat po立 nter ★ /
需耍指 出,在 很 多系统 中,指 针类 型的人小和 主nt类 型的大小相 同。由于 土n△ 是函数 返冂的约定类型 ,一 些稆序员定 义函数时会省略返冂类型 。函数的返 回类型如 呆没有显式定 义 ,那 么这个约定的 土nt返 冂类 型以后可能被解释 为指针 。芋实证 明,这 种 习惯对某些系统 足很危险的,l,xl而 ,稆 序 员应 该明确指定函数的返 冂类型。
§ 1。 3 算 法形式规 范
§ 1.
3.
1 综
论
算法 是计算机利'丫的基本概念 。许多问题都有现成 的求解算法 ,对 △人规模计算机 系 统 ,设 计高效算法是解 决问题 的核心 。有鉴 T此 ,我 们必须首先来详细讨论算法概念 。以 卜 从算法定义开始。
定义 算法足指令 的有 限集合 ,顺 序执行这些指令 ,可 以完成特 定的任 务。同时,所 有算法 都递循 以 卜判据 。
(1)输 入 从外界获取零个或 多个蚩 。
⑵ 输 出 产生至少一个鞋 。
(3)确 定性 每条指令清 晰 、无工 义性 。
(4)有 限性 算法对所有情形都能在执行仃 限步之后纬束 。
(5)有 效性 每条指令都是基本可执行的,意 为借助纸 、笔即可完成 。判据 lS)要 求 的确定性 并不充分,每 条指令还必须足能行的 (feasible)。 □
§】。3算 法形 式规范 7
计算理论范畴中的算法 Jj程 序有不同含义,计 算理论 中的稆序无需满足 以上 的判据 ⑷ 。 例如 ,操 作系统 的I作 模式足一个无限循环,无 终 l卜地等待为所有任务服务,这 个系统程序 从不结束,除 非系统 出错而崩溃 。本书讨论 的程序总会结束,因 而 ,本 {氵不区分算法和程序 , 这两个名 词可 以互换 。
算法 的描述方式可 以多种多样 ,比 如可 以用 白然语 亩,如 英语 ,但 这时必须保证每条指 令都是确定的。借助 图形也可 以表示算法 ,称 为流程 图,但 只适用小而简单的算法 。本书描 述算法都用 C语 言 ,不 过有时会用英语与 C语 言的组合表示算法 。以 卜给 出两个例子 ,说 明 从 问题描述到算法形成 的转换过程 。
例 1.1(选 择排序)设 计稆序 ,将 彳≥1个 整数排序 。以 卜足简单的求解过程 :
From those △ ntegers that are current△ y unsorted`
f土 nd the sma11est and p1ace 土 t next 土 n △ he sorted 1ist.
(在当前所有未排序的整数中,找 出最小的一个,把 它放在当前有序表的后一个位置。)
虽然这句话充分描述 了排序 问题 ,但 它并不足算法 ,冈 为其 中有儿处疑 问。例如 ,这 句 话没告诉我们这些整数开始时如何存放 ,也 没告诉我们结呆存放在哪里 ,更 没告诉我们存放 形式足什么。假定整数存放在数组 中,数 组名为 1土st,那 么第 J个 整数应存放在第 j个 位置 , 即 1土st[j],0兰 j<彳 。程序 1.2是 构建算法 的第一次尝试,注 意它部分足 C语 言,部 分是英 语 。
f o r ( i = 0 ; i < n ; i + + ) t
E x a m i n e l i s t t i l t o l - i s t [ n - 1 ] a n d s u p p o s e t h a t t h e s m a l l e s t i n t . e g e r i s a t l i s t [ m i n ] ;
r n t e r c h a n g e l i s t I i ] a n d I i s t [ m i n ] ;
)
程序 1.2选 择排序 算法
耍把程序 1.2转 换成萁正的 C语 言 ,还 必须实现两个 已明确定义的1f任 务:一 个是找出 最小整数 ,一 个是将 当前最小值 与 1土st[氵]交 换 。后一个子任务可 以用函数 (见 稆序 1.3)实 现 ,也 可 以用宏实现 。
v o i d s w a p ( i n t * X , i n t * Y )
{ / * b o t h p a r a m e t e r s a r e p o i n t e r s t o i n t s * /
i n t t e m p = * x ; / * d e c T a r e s t e m p a s a n i n t a n d a s s i g n e s t o i t t h e c o n t e n t s o f w h a t x p o i n t s t o * /
* x = * y ; / * s t o r e s w h a t y p o i n t s t o i n t o t h e T o c a t i o n w h e r e x P o i n t s * /
* y = t e m p ; / * p T a c e s t h e c o n t e n t s o f t e m p i n T o c a t i o n p o i n t e d t o b Y Y * /
)
程 序 1.3swap函 数
第 】章 基本概念 以 卜是这个函数 swap的 用法 。假定 a和 b都 声明为 ht类 氵l刂,要 交换存放在两者 中的 值 ,调 用:
swap(&a` &b)'
传给 swap的 参数足 a和 b的 地址 。以 卜是交换两变鞋值 的宏语句:
#de£ 主ne SWAP(x`y`t) ((t) = (x)` (x) = (y)` (y) = t)
两种实现各有优点,函 数实现更易读 ,宏 实现适用 T、所有变章类型。
现在 ,我 们转 向第一个子任务。假定 肖窝I的 最小值在 1主st[j]叶 I,把 它和 1土st[j+1]' 1土st[氵+2]`・ …'1土s乜[彳-1]比 较 ,只 耍找到更小值 ,就 用它作最小值 。这样 ,到 1土st[彳 一"
整个 I∶作就完成 了。把所有这些 匚作组织在一起 ,就 是函数 sort(见 程序 1.4)。 稆序 1。4是 一个完整的稆序 ,可 以在计算机上运行。稆序屮用到定义在 <stⅡ b.h)中 的函数 rand,它 产生一系列随机数传给 sort。 现在我们耍 问这个函数楚否正确 。
定理 1.1对 Tˉ彳≡ 1个 元素的整数集合 ,函 数 sort(1ist`″ )引卜序 的结呆足正确 的。数据
最 后 存 放 在 在 1土 st[0]'… ・
'1土 st[″ -1]中 ,且 △ 土st[Ol兰 1土 st⒒ ]兰 … ・
兰 1iSt!″ -1]。
证 明 当 最 外 层 £or循 环 结 束 第 j=呷 次 循 环 时 ,我 们 有 1主 st[叼 ]兰 1主 st[r]'呷 <r(″ 。 接
着 ,执 行后续循环到 j)呷 ,此 日寸从 1土st[O]到 1⊥st[q]的 内容不变 。lAl此,当 最外层 for执
行 到 最 后 一
个 循 环 时 (即 j=彳 -2之
后 ),有 1⊥ st[0]兰 1iSt[△ ]兰 … ・
兰 1土 st[彳 -11。 □
例 1.2(折 半查找)假 定 刀≥1个 有序整数存储在数细^Ⅱ st之 中,1ist[0]兰 1土st⒒ ]兰 …・兰 1立st【彳-1]。 我们 想知道整数 searchnum楚 否 出现在这 个表屮,如 呆足 ,则 返 冂下标 j, searchnum=Ⅱ st[j];否 则返冂 -1。由 「这个表仃序,可 以川 以 卜方法 今找给点值 。
令 1eft、 r土 ght分 别 表 示 表 中 待 杏 范 l+l的 左 、 亻 f端 点 ,初 值 为 :1eft=O'r土 ght=彳 -1。
令 m主 ddle=(1eft+r土 ght)/2,足 表 的 中 点 卜 标 值 。 searchnum和 1⊥ st[m土 dd1e]比 较 的 结
果 ,有 三 种 可 能 :
(1)searchnum(1土 st[m土 dd1e]。 此 时 ,如 呆 searchnum在 表 中 ,它
一
定 在 位 置 0与 碱 dd1e-1
之 间 ,冈 此 ,把 right。 设 成 m土 dd1e1。
(2) Searchnum〓 △ 土st[m土 dd1e]。 此 日寸 , 返 「冂 midd1e。
⑶ searChnum>1土 st[m土 dd1e]。 此 时 ,如 果 searchnum在 表 中 ,它 一
定 在 位 置 呐 dd1e-1与
″-1乏
间 ,冈 此 ,把 r土 gh匕 设 成 m土 dd1e-1。
当 searchnum还 没被兖到 ,同 时 尚有没检 兖的其它整数 ,我 们重新计算 m土ddle,重 复上述 杏找过稆 。稆序 1.5足 这种杏找策略的实现 。算法包括 两个子任务:(1)表 中是否还有未杏找 过的整数 , (2)比较 searchnum和 1土st[m土dd1e]。
比较操作既可 以用函数实现 ,也 可 以用宏实现 。尢论采用哪种实现 ,都 需要分别为小于 、 等 丁:←人 l=指定数值 。我们遵循 C语 占库 函数 的习惯 :
・若前者 小 丁:后者 ,则 返冂负数 ←1)。
§】。3算 法形式规范
#土 nc△ ude (std土 O。 h)
#土nc△ ude <s乜 d1土 b.h>
艹de£ 土ne V⒇ 《 SIZE △ 0△
艹de£ 土ne sWAP(x`y`t) ((t) = (x)` (x) = (y)` (y) = t)
vo土 d sort(土 nt []' 土 nt)` /★ seⅠ ecε Ion sOrε ★ /
vo土 d ma土 n(vo土 d)
(
土n△ i` n氵
土nt 1⊥ st[MAX SIZEl`
pr土 ntf("Enter凵 the凵 number凵 ⊙ £ 凵 numbers凵 t° 凵 generate:凵
")′
scanf("%d"` &n)氵
土 £ (n < △ || n > MAx_SIZE) (
£pr⊥ ntf(stderr` "Improper凵 va1ue凵 ⊙ f凵 n/n")`
exit(EX工 T FAILURE)`
)
£ or (土 = 0' 土 ( n` i++) ( /★ rand。 mⅠ y generaε e numbers */
1ist[i] = rand() % 10OO氵
pr土 nt£ ("%d凵 凵"′ 1土 st[土 ])氵
)
s⊙ rt(1主 st` n)氵
pr土 tnf("\n凵 s° rted凵 array:\n凵 ")′
for (立 〓 0` 土 ( n氵 ⊥ ++) /★ pr立 nε out sOrted numbers ★ /
printf("%d凵 凵"′ 1ist[⊥ ])氵
pr土 ntf("\n")氵
)
vo土 d sort(主 nt 1土 st[]` 土 n△ n)
(
土n△ i` j` m土 n` temp'
£or (i = 0' 土 ( n-△ 氵 土 +十 ) (
m土 n = 土 氵
£or (j = i十 1' j ( n' j++)
土 £ (1土 st[j] ( 1土 st[m土 n])
m土n = j'
SWAp(1⊥ st[i]` 1土 st[m⊥ n]` temp)'
) )
程序 1。4选 择排序
10 第 】章 基本概念
w h i l e ( t h e r e a r e m o r e i n t e r g e r s t o c h e c k ) { m i d d l e = ( I e f t + r i g h t ) / 2 ;
i f ( s e a r c h n u m < l i s t [ m j - d d ] e l ) r i g h t = m i d d l e - 1 - ;
e l s e i f ( s e a r c h n L r m = = l i s t [ m i d d l e ] ) r e t u r n m i d d l e ;
e l s e L e f t . = m i d d l e + 1 ;
\
程序 1.5查 找有序表
・若两者相等 ,则 返冂 0。
・若前者大于后者,则 返冂正数 (1)。
・若前者大于后者,则 返冂正数 (1)。