第8章 - 指针

更新时间:2024-03-18 23:21:01 阅读量: 综合文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

第八章 指针 ........................................................................................................................................................ 2 8.1指针概述 .................................................................................................................................................... 2 8.1.1理解指针 ............................................................................................................................................. 2 8.1.2为什么引入指针的概念 ..................................................................................................................... 3 8.2 指针基础 ................................................................................................................................................... 7 8.2.1指针变量的声明 ................................................................................................................................. 7 8.2.2指针变量的初始化 ............................................................................................................................. 7 8.2.3指针变量的赋值 ................................................................................................................................. 8 8.3 指针编程实例 ......................................................................................................................................... 10 8.3.1 征婚匹配问题 .................................................................................................................................. 10 8.3.2 求解一元二次方程 .......................................................................................................................... 12 8.4指针型函数 .............................................................................................................................................. 14 8.5 指针与数组 ............................................................................................................................................. 17 8.5.1 一维数组与指针 .............................................................................................................................. 17 8.5.2指针运算 ........................................................................................................................................... 18 8.5.3用指针引用数组元素 ....................................................................................................................... 19 8.5.4二维数组与指针 ............................................................................................................................... 19 8.6指针与数组编程实例 .............................................................................................................................. 21 8.6.1 数列的中位数 .................................................................................................................................. 21 8.6.2 求矩阵的最大元素 .......................................................................................................................... 23 8.7 指针与字符串 ........................................................................................................................................ 24 8.7.1字符型指针、字符数组与字符串常量的概念 ............................................................................... 24 8.7.2编程实例判断回文单词和回文句子 ............................................................................................... 25 8.7.3 C语言字符串操作的库函数 ........................................................................................................... 31 8.7.4 字符串库函数应用实例 .................................................................................................................. 32 8.8 常见的编程错误 ..................................................................................................................................... 34 8.9小结 .......................................................................................................................................................... 37 习题八 ............................................................................................................................................................ 38

第八章 指针

学习目标

? ? ? ? ?

理解内存单元的双重属性-内容与地址及其表示方法;

理解基本数据类型变量与指针类型变量的异同及其相互关系;

学习指针的使用方法:指针初始化,获得变量地址,访问指针所指向的变量; 理解指针与数组的关系,学习如何利用指针处理数组;

掌握指针作为函数参数及其函数的返回值的作用及其使用方法。

本章我们介绍C语言中的一种新数据类型——指针。在C语言中,指针被称为派生数据类型,是由基本数据类型衍生而来。指针的值是内存地址,用指针可以直接访问、操作内存中的数据。

指针是C语言最显著的特点之一,也是C语言最重要的功能,利用指针变量可以方便、灵活地编写C语言程序,解决复杂的编程问题。例如:指针变量既可以作为函数参数,也可以作为函数返回值,从而使函数有多个返回值;指针提供了另外一种数组处理方法;利用指针可以更方便地处理字符串等。从本章开始,我们将由浅入深地逐步介绍指针的这些特征。

当然,指针还有其它应用,例如:指针支持动态内存管理,指针可以指向函数,利用指针操作动态数据结构(如链表、队列、栈和树等),有关这些内容我们将在后面章节介绍。

8.1指针概述

8.1.1理解指针

对于初学者而言,指针有点难以理解。让我们先看一下计算机内存是如何组织的。我们已经知道,计算机内存是一系列连续编址(从0开始,1,2,3…依次编号)的内存单元的集合。通常,内存编址的基本单位为字节,因此,内存中任意字节都有一个特定的地址编码,该地址编码也称为内存地址。

看下面语句: int x=5;

该语句指示系统为整形变量x分配存储空间,并把数值5存放在其中。假设系统为x变量分配的存储空间的内存地址为3000,变量x在内存中的位置可以如图8-1所示。请注意,图中x占用4个字节的内存(地址3000是该变量所占用内存的首地址),但是有些系统中int型数据占2个字节,请读者自行区分。

可以看出,变量x有两方面属性:一方面是x的地址3000,另一方面是x的值是5。在C语言中,当我们直接引用x时,我们取的是x的值,对于x的地址,我们需要一种新的数据类型——指针来表示。假设我们用指针变量p存放x变量的地址,p的值是3000,这样对于内存中的同一个值5,我们有两种方式访问它,可以直接通过变量名x访问,也可以通过指针变量p访问它。变量x是一个整型变量,而p就是一个指向整型变量的指针,是由整型变量派生的数据类型。由于变量p 的值是变量x的地址,我们称变量p指向变量x,这就是“指针”名称的由来。当然,指针变量也是一个变量,需要系统为它分配存储空间,我们可以将变量x和p的值与地址以及二者之间的指向关系表示为图8-2(设系统为变量p分配的内存地址为3100)。

地址 0 1 2 ?? 3000

3004 ??

内存单元

: : : 5 : : : 图8-1 变量与内存

变量

x

地址 3000

值 5 : : 3100

刚才我们谈到,当直接引用变量x时,取的是x的值属性。如何表示一个变量的地址属性呢?答案是:在C语言中用&运算符表示取变量的地址。例如变量x的地址可以表示为&x。

对于指针变量,除了值属性和地址属性以外,还有第三方面的属性,即指向属性。我们已经知道指针变量的值是另外一个变量的地址,也称指针变量指向另外一个变量。例如指针变量p存储变量x的地址,我们说成p“指向”x,我们一般不用具体的数值表示地址,而采用简单的图示方法,说明指针p“指向”变量x。如图8-3所示:

p

· 5 x

3000 p

图8-2 基本类型变量与指针变量的指向关系

变量 x

图8-3 p“指向”x

那么,如何使指针变量p“指向” x呢?我们通过赋值语句p=&x; 来实现。我们又如何通过指

针p访问x的值呢?在C语言中用*(间接寻址)运算符访问指针指向变量的值。即用*p来访问x的值。程序中x与*p是等价的,都代表x的值5,只不过是x是直接访问,*p是间接访问。

8.1.2为什么引入指针的概念

从上一节的内容可知,对变量的访问即可以通过变量名直接访问,也可以通过指针间接访问。既然如此,我们为什么要学习指针呢?因为利用指针变量可以方便、灵活地编写C语言程序,解决复杂的、利用以前学过的知识难于解决编程问题。下面我们通过一个例题说明来说明学习指针的重要性,也帮助我们更好地理解指针。

【例8-1】编写一个的子程序,子程序的功能为交换两个变量的值。 程序如下:

#include

void swap(int x,int y) { int t; printf(\交换前:x=%d,y=%d\\n\ t=x; x=y; y=t; printf(\交换后:x=%d,y=%d\\n\}

void main() { int a,b; scanf(\ printf(\调用前:a=%d,b=%d\\n\ swap(a,b); printf(\调用后:a=%d,b=%d\\n\}

运行结果: 3 5

调用前:a=3,b=5 交换前:x=3,y=5 交换后:x=5,y=3 调用后:a=3,b=5

Press any key to continue

子函数void swap(int x,int y)的功能为交换变量x和y的值。在主函数中调用swap(a,b)函数后,从运行的结果看,函数并没有实现交换a和b的值的功能,为什么呢?我们将如何实现这一功能呢?

在C语言中,函数实参和形参的传递规则是“单项值传递”,即将调用语句中实参赋值给子函数中的形参。这是一种单向传递方式,子函数的形参得到主调函数的实参值,但在子函数中改变形参的值并不影响主调函数中实参的值。我们结合图8-4来分析。

a main 函数 swap 函数 3 b 5 a 3 b 5 a 3 x b 5 y 3 t x 3 t y 5 x 5 t 3 y 3 5 3 (a) 调用swap函数 (b) 执行swap函数 (c) 从swap函数返回 图8-4 swap函数调用前后参数变化示意图

(1)在主程序中语句swap(a,b);完成子函数的调用,它将实参a、b中的值传递给子函数swap中的形参x、y,因为C语言的参数传递方式为“单项值传递”,传递后x为5,y为3,见图8-4(a)。

(2)在子程序中执行t=x; x=y; y=t;语句实现了x和y中的值对换,见图8-4(b),我们可以看到x和y的值确实交换了。

(3)当子程序执行执行完毕,程序释放掉形参x、y和局部变量t的存储空间,返回主函数,因此x和y中交换结果也就丢失了。主函数中变量a、b的值不变,见图8-4(c)。

那么怎样才能实现利用子程序交换两个变量的值呢?这时就要用指针来实现了。因为C语言的参数传递方式为“单项值传递”,当用简单变量作为函数的参数进行函数调用时,在子函数中对形参的改变不会影响到主调函数中的实参。我们将形参定义成指针类型swap(int *x,int *y),调用时将变量a和b的地址&a和&b作为实参传递给指针x和y,程序改写为如下形式:

源程序:

#include

void swap(int *x,int *y) { int t; printf(\ printf(\ t=*x; *x=*y; *y=t; printf(\}

void main() { int a,b; scanf(\ printf(\ printf(\调用前:a=%d,b=%d\\n\

swap(&a,&b); printf(\调用后:a=%d,b=%d\\n\}

运行结果: 3 5

&a=13ff7c,&b=13ff78 调用前:a=3,b=5

-----swap-----

x=13ff7c,y=13ff78 -----swap-----

调用后:a=5,b=3

Press any key to continue

我们看到程序的运行结果为a的值为5、b的值为3,实现了变量值的交换。程序的执行过程如图8-5所示:

a main 函数 swap 函数

· x t (a) 调用swap函数

b 5 3 a 5 t b 3 a 5 x y t 3 b 3 y y · x 3 (b) 执行swap函数

(c) 从swap函数返回

图8-5 用指针做形参的swap函数调用前后参数变化示意图

(1)在程序中swap(&a,&b);完成函数的调用,它将变量a和b的地址&a、&b作为实参递给子函数swap中的形参x、y,其参数传递方式仍为“单项值传递”,因为x和y是指针变量,所以传递后指针x中存放的为a的地址(13ff7c),即使x “指向” a,同理y中存放的为b的地址(13ff78),使y “指向” b,见图8-5(a)。

(2)在子程序中执行t=*x; *x=*y; *y=t;语句,通过指针x和指针y的间接访问变量a和b,在子程序中执行了将a的值存入t中(t=*x,通过指针x间接访问a);将b的值存入a(*x=*y,通过指针x间接访问a,通过指针y间接访问b);将t的值存入b中(*y = t,通过指针y间接访问b)实现了a和b的值对换,见图8-5(b)。

(3)当子程序执行执行完毕,程序释放掉形参x、y和局部变量t的存储空间,返回主函数,主函数中变量a、b的值为交换后的结果,见图8-5(c)。

以上例题只是说明了指针的一个具体应用,利用指针主要可以实现一下应用:

(1)指针作虚参可以在子函数中修改实参变量的值。 (2)利用指针作虚参可以使子函数有多于一个的返回值。 (3)利用指针可以实现动态分配内存。

(4)利用指针可以实现动态数据结构(链表、队列、堆栈、树等)的操作。 (5)指针可以改善某些子函数的效率。

指针的(2)~(5)的应用在以后的章节中陆续介绍。

8.2 指针基础

指针是C语言最显著的特点之一,也是C语言最重要的功能。利用指针变量可以方便、灵活地编写C语言程序,解决复杂的编程问题。

下面我们给出指针的定义及引用规则:

8.2.1指针变量的声明

1. 指针变量的声明

在C语言中,使用变量必须先定义后使用,指针变量在使用之前同样必须先定义。指针变量存储的是某个指定数据类型变量的地址,指针变量的定义如下:

基类型 * 指针变量名;

指针变量的定义包括三个内容:

(1) 符号*,表示所定义的变量是一个指针变量;

(2) 指针变量名,同普通变量一样,应为合法的标识符,在编译时需要为其分配内存空间; (3) 基类型,指针变量所指向数据的数据类型。 例如: int *p1; /*p1是指向整型数据的指针变量*/ float *p2; /*p2是指向浮点数据的指针变量*/ char *p3; /*p3是指向字符数据的指针变量*/

2. 指针变量的声明方式

指针变量的定义类似于普通变量,只不过加了一元运算符*。*可以出现在数据类型名与变量名之间的任意地方。有以下三种定义方式:

int* p; /* 方式1 */ int *p; /* 方式2 */ int * p; /* 方式3 */ 通常方式2最为流行,我们一般习惯使用方式2。 例如我们可以这样定义三个指针变量: char *a, *b, *c;

8.2.2指针变量的初始化

当我们定义了一个指针变量后,第一次把变量的地址赋给指针变量的过程称为指针变量的初始化。所有未初始化的指针的值是未知的、随机的。当这些值被解释为内存地址时,它们可能是有效地址,也可能是错误的地址。编译器并不检测这类错误,如果引用未初始化的指针将是非常危险的,可能导致系统的崩溃。例如:

int *p; *p=1;

指针p声明后,没有进行初始化,即它没有指向任何变量,此时我们称指针p为悬挂指针,p中存放的地址为一个不确定的地址,这个地址也可能为程序某条指令的地址或操作系统中特定字节的地址,上述赋值语句改变了该地址单元中的值后,可能导致系统崩溃或引起不可预知的错误。因此在程序使用指针之前,对它进行初始化是非常重要的。

指针初始化的方法有以下2种:

(1)先定义变量和指针,然后用赋值语句对指针进行初始化。 例: int *p,x; p=&x

(2)声明指针的同时进行初始化。 int x,*p=&x;

注意:要求变量x必须在指针p声明之前先声明,因此语句 int *p=&x,x; 是错误的。

8.2.3指针变量的赋值

在C语言中指针变量存放的是变量的地址。因此对指针变量的赋值有以下几种特殊要求。 (1)对指针变量的赋值需要保持数据类型的一致 例如: float x; int *p; p = &x; /*错误!*/

语句p = &x;是错误的,原因是p为指向整型变量的指针,而&x是一个浮点型数据的地址。 (2)不能将一个常数值赋给指针变量 例如: int *p;

p= 3000; /*错误!*/

语句p= 3000;是错误的,因为3000是一个整形常数不是一个整形变量的地址。 (3)指针变量可以被赋值为NULL或者零 例如: int *p ;

p= NULL;

p = 0;

指针变量被赋值为NULL或者0时,我们称这样的指针为空指针,它不指向任何地址空间。但除了NULL和零以外,其它常量数值不能赋给指针变量。

(4)&取地址运算符

指针变量的存储单元中存放的是它所指向变量的地址,为指针变量赋值时应注意&后必须是已声明的变量名。

例如:

int *p;

p=&125; /*错误!操作数为常量 */

p=&(x+y); /*错误!操作数为表达式*/

语句p=&125;和p=&(x+y);是错误的,因为取地址运算符&后只能是已声明的变量名,即 &变

量名,不能是常数和表达式。

(5) 允许使用赋值运算符进行指针的赋值运算

指针的赋值运算使两个指针指向同一个变量,但前提是两个指针必须具有相同的类型。 例如:

int *p,*q,x=5; p=&x; q=p;

执行结果如图8-6所示

p

· 5 · 图8-6 q=p

x q

但以下语句是错误的

int *p, x=5; float *q ; p=&x; q=p;

其原因为p为指向整型变量的指针,而q为指向实型变量的指针,两个指针变量的类型不同不能赋值。

注意不要把 q=p; 和 *q=*p

混淆,第一个语句是将p所指向变量的地址赋给q,即使指针q和指针p指向同一个变量;第二个语句是将p所指向变量的值赋给q指向的变量

例如:

int x=5,*p=&x,; float y=3.6,*q=&y ; *q=*p ;

以上语句的执行结果图8-7所示

p q p q · · x y · · 5 3.6 x y 5 5.0 (a) *q=*p执行前 图8-7 *q=*p

(b) *q=*p执行后

以上语句*q=*p与y=x等价。

指针变量的操作规则: (1)只有变量的地址才能存储在指针变量中。 (2)对指针变量的赋值需要保持数据类型的一致。 (3)不要把变量的值和常数赋给指针变量。 (4)指针变量在引用前必须初始化。 (5)只有相同类型的指针可以直接赋值 8.3 指针编程实例

8.3.1 征婚匹配问题

相信大部分人看过电视上的“征婚”栏目,下面我们就通过这个例子来理解指针。

电视台的“征婚”节目总会吸引不少漂亮的单身男女参加,参加节目的先生和女士们通过相互介绍、彼此交流、动作表演等各种形式充分展示自己的外在形象和内涵气质,以打动对方。经过这样一个互动过程,先生和女士们彼此之间形成了某些方面的了解,在各自的心目中,可能已经产生了自己对于另一方的喜欢或者选择。然后,主持人会要求先生和女士分别写下自己所喜欢的对方名字,统一提交上来进行匹配。如果先生(或女士)喜欢的对方恰好也喜欢自己,则“征婚”成功,可以进入下一阶段交往相处,否则不成功,只有等待新的机会。说到这里,请大家不要被那些情感细节所吸引,因为我们的问题仅仅是如何根据女士、先生们已经独立做出的选择进行匹配,而这恰好是计算机所擅长做的。计算机可能很难弄懂人类的情感,但计算机却可以帮助人类做出选择,有些时候还能帮助人类避免尴尬。我们进一步将问题规范化形成下面的例题。

【例8-2】三位先生和三位女士参加“征婚”栏目,分别写下了自己喜欢的对象,需要设计一个计算机程序找出成功的匹配。限制条件是:每位先生喜欢并且仅仅喜欢一位女士,而每一位女士也喜欢并且仅仅喜欢一位先生。匹配条件是:先生(或女士)所喜欢的人恰好也喜欢自己,则匹配成功,否则匹配不成功。结果要求打印出幸运的先生(或女士)。

我们首先定义变量: char *a, *b, *c; char x, y, z;

三个指针变量a、b、c分别表示先生,指向先生所喜欢的女士对应变量;三个字符变量x、y、z分别表示女士,存放女士所喜欢的先生变量名称。图8-8表示的是一组选择情况:a先生喜欢x女士,b先生喜欢z女士,c先生喜欢y女士,x女士喜欢a先生,y女士喜欢c先生,z女士也喜欢c先生。

变量a指向变量x,变量b指向变量z,变量c指向变量y,变量x的值是字符?a?,变量y的值是字符?c?,变量z的值是字符?c?。

a

b

c x 'a' y 'c' z 'c' 图8-8 例8-2征婚男女的选择图

该组选择中,a先生和c先生是幸运者。

y=tpre(prefb); z=tpre(prefc); printf(\ printf(\ printf(\ if (&a == (long **)*a) printf(\先生是幸运的!\\n\ if (&b == (long **)*b) printf(\先生是幸运的!\\n\ if (&c == (long **)*c) printf(\先生是幸运的!\\n\}

long *tpre(char pref) { switch (pref) { case 'x': return (long *) &x; case 'y': return (long *) &y; case 'z': return (long *) &z; case 'a': return (long *) &a; case 'b': return (long *) &b; case 'c': return (long *) &c; } }

运行结果:

请输入三位男士的选择对象(请选择x,y,z): xzy

请输入三位女士的选择对象(请选择a,b,c): acc

&a=42ac50 x=42ac50 a=42ac5c &x=42ac5c &a=42ac50 *a=42ac50 A 先生是幸运的! C 先生是幸运的!

Press any key to continue

程序说明:

在例8-5中,我们已改变例8-2中用字符变量(char x, y, z;)代表女士的做法,将先生和女士都用指针类型变量表示:

long *a, *b, *c; long *x, *y, *z;

并用函数long *tpre(char pref);返回一个指针值。

对于下面一组选择情况:a先生喜欢x女士,b先生喜欢z女士,c先生喜欢y女士,x女士喜欢a先生,y女士喜欢c先生,z女士喜欢c先生,则变量a指向变量x,变量b指向变量z,变量c指向变量y,变量x指向变量a,变量y指向变量c,变量z指向变量c。我们可以直观地用图8-10来表示:

a b c x y z 图8-10 例8-5征婚男女的选择图

函数long *tpre(char pref);返回一个指针值。根据输入的字符,得到该字符所代表变量的地址。如果字符为'a',则返回指向变量a的地址。

在程序中,判断能否匹配的条件为: &a == (long **)*a &b == (long **)*b &c == (long **)*c

需要注意的是,我们分别用变量a、b、c代表先生,变量x、y、z代表女士,在定义变量时会产生循环,因此,我们采用了一种办法,将变量a、b、c、x、y、z都定义为指向长整型的指针。然后通过强制的类型转换实现指针的比较或者返回。例如: (&a == (long **)*a)及case 'x': return (long *) &x; 这样做之所以正确,是因为我们隐含了一个前提:指针类型变量与长整型变量占用相同大小的内存空间。这点依赖于具体的语言实现。

8.5 指针与数组

8.5.1 一维数组与指针

一个数组是由连续的一块内存单元组成的。当定义一个数组时,编译器为数组分配一个首地址,并且从该首地址开始,分配足够的连续的内存空间存储整个数组。首地址是数组第0个元素(下标值为0)的地址,数组名是指向数组第0个元素的指针常量。例如定义如下的x数组:

int x[5]={10,20,30,40,50};

假定x的首地址为2000,整数占用4字节内存,则数组的存储如图8-11所示: 可以定义整型指针p指向数组x: int *p; p=x; p=&x[0];

赋值语句p=x; 和p=&x[0];二者等价,都是使p指向数组x的首元素地址。

数组元素 数组值 数组地址 x[0] 10 2000 x[1] 20 2004 首地址 p

图8-11 数组在内存中的存储

x[2] 30 2008 x[3] 40 2012 x[4] 50 2016 8.5.2指针运算

1.加减运算

可以对指针变量进行加减常数的算术运算,其结果是指针(内存地址值),指针类型不变。 例如:对于图8-11中的整型指针p,表达式p++表示指针p指向下一个整数。假设的p初值为2000,整数的大小是4字节,则经过p=p+1运算后,p的值是2004,而不是2001。也就是说,当对指针变量做加一运算时,所增加的值为该指针指向的数据类型的“长度”。该长度有时也称为指针变量递增的比例因子。

对于不同类型的指针,其递增的比例因子不同,不同的数据类型的数据所占的字节数取决于具体的系统,例如:在VC++6.0中int型数据占4个字节,在TC2.0中int型数据占2个字节。在程序中可以用sizeof运算符来得到不同数据类型的数据所占的字节数。例如:a为int型变量,那么我们通过sizeof(a)或sizeof(int)就可以得到a所占的字节数。

对于指向数组的指针变量,可以加上或减去一个整数n。设p是指向数组x的指针变量,则p+n,p-n,p++,++p,p--,--p 运算都是合法的。指针变量加或减一个整数n的意义是把指针的当前位置(指向某数组元素)向前或向后移动n个位置。应该注意,数组指针变量向前或向后移动一个位置和地址加1或减1 在概念上是不同的。因为数组可以有不同的类型, 各种类型的数组元素所占的字节长度是不同的。如指针变量加1,即向后移动1 个位置表示指针变量指向下一个数据元素。而不是在原地址的基础上加1。

例如: int x[5],*p;

p=x; /* p指向数组x,也就是指向x[0] */ p=p+3; /* p指向x[3],即p的值为&x[3] */ p=p-2; /* p指向x[1],即p的值为&x[1] */

指针变量的加减运算只能对指向数组的指针变量进行, 对指向其它类型变量的指针变量作加减运算是毫无意义的。

2.两个指针变量的减法运算

两个指针变量之间的减法运算,只能在指向同一数组的两个指针变量之间才能进行运算, 否则运算毫无意义。例如:设p=x; p1=x+3; 使p指向x[0] , p1指向x[3],则p1-p的值为3,其含义为p1和p之间相隔3个数组元素。

不能对两个指针变量进行加法运算,原因是指针变量相加没有实际意义。 3.两指针变量进行关系运算

同类型指针变量可以进行比较运算,指针的比较运算就是比较指针值的大小。例如,整型指针p1的值是2000,p2的值是2008,则表达式p1

8.5.3用指针引用数组元素

在C语言中,如果指针变量p已指向一个数组元素,则p+1指向该数组的下一个元素。 例如:如果指针变量p指向数组x的首元素x[0],则:

(1)p+i和x+i是x[i]的地址,它们指向x数组的第i个元素。例如:p = p + 3;表示将指针p后移3个数组元素,指向元素x[3]。

(2)*(p+i)或*(x+i)是p+i或x+i所指向的数组元素,即x[i]。因此 *(p+3)、*(x+3)和x[3]是等价的。

(3)指向数组元素的指针变量也可以带下标, 即p[i]与*(p+i)是等价的。

(4)p++运算是使指针指向下一个数组元素,但x++运算是错误的,因为x是数组的首地址,是一个常数,因此不能执行赋值操作。

若p=x; x是一维数组,p是指向数组元素的指针变量。引入指针变量后,可以有两种方法访问数组元素。

(1)下标法:可以用x[i]或p[i]的形式访问数组元素。

(2)指针法:即采用*(x+i)或*(p+i)形式,通过指针变量寻址的方法来访问数组元素。 指向数组的指针的操作规则: (1)指针变量可以与整数进行加减运算。注意:运算后指针不能越界。 (2)当两个指针指向同一数组时,可以进行减法运算。 (3)当两个指针指向同一数组时,可以用关系运算符进行比较运算。 (4)两个指针变量不能做加法运算。 (5)x[ ]是数组,x是数组的首地址,则对x不能执行赋值运算。 8.5.4二维数组与指针

C语言允许把一个二维数组分解为多个一维数组来处理,例如:数组a[10][10]可分解为10个一维数组,即a[0],a[1],a[2]. . . a[9],每一个一维数组又含有10个元素。如图8-12所示:

a[0][0] a[0]

a[0][1] . . . . . . . . . a[9]

a[9][0] a[9][1] . . . . . . . . . . . . a[9][9]

a[0][9] 图8-12 二维数组a[10][10]中的指针

数组a[10][10]有10*10=100个元素,整个数组的大小为400字节。

可以把a[0]看做一个一维数组,它包含a[0][0],a [0][1],a[0][2],. . . a[0][9]10个元素。 假设数组a[10][10]的首地址是3000,整数的长度为4字节(某些系统整型占2个字节)。从二

维数组的角度来看,a是二维数组名,它的值即为3000,是整个二维数组的首地址,也是该二维数组第0行元素的首地址,指向从3000开始的长度为40字节的数据的一维数组(第一行)。

a[0]一维数组的数组名和首地址,因此也为3000。*(a+0)或*a与a[0]等效, 它表示一维数组a[0][0]元素的首地址,也为3000。&a[0][0]是二维数组a的0行0列元素首地址,同样是3000。因此,a,a[0],*(a+0),*a,&a[0][0]值相等。

同理,a+1是二维数组1行的首地址,等于3040。a[1]是一维数组的数组名和首地址,因此也为3040,指向第1行这个一维数组。以此类推,a[9]的值是3000+9*40=3360,表示数组a的第9行的第0个元素的地址,指向第9行这个一维数组。

&a[1][0]是二维数组a的1行0列元素地址,也是3040。因此a+1与&a[1] 等价,*(a+1)与&a[1][0]等价。

在二维数组中a[i]表示数组a第i行首地址。进一步的结论有:a[i]和*(a+i)等价、&a[i]和a+i等价。

另外,a[0]也可以看成是a[0]+0,是一维数组a[0]的第0个元素地址,而a[0]+1则是一维数组a[1]的第0个元素地址,由此可得出a[i]+j则是一维数组a[i]的第j个元素地址,它等于&a[i][j]。

由a[i]=*(a+i)得a[i]+j=*(a+i)+j。由于*(a+i)+j是二维数组a的i行j列元素地址,所以,a的i行j列元素可以通过*(*(a+i)+j)访问。

需要注意二维数组中各种不同指针表示方法的区别。例如二维数组a[10][10]中,指针a和指针a[i]指向不同的数据,因而是不同数据类型的指针。

【例8-6】读以下程序,分析运行结果,理解二维数组指针的概念。

源程序:

#include void main() { int a[2][2]={1,2,3,4}; int i; for (i=0; i<2; i++) { printf(\ printf(\ printf(\ printf(\ } for (i=0; i<2; i++) { printf(\ printf(\ } }

运行结果:

a+0 = 13ff70 *(a+0)= 13ff70 a[0]= 13ff70 *(*(a+0))= 1 a+1 = 13ff78 *(a+1)= 13ff78 a[1]= 13ff78 *(*(a+1))= 3 a[0]+0= 13ff70 *(a[0]+0)= 1 a[0]+1= 13ff74 *(a[0]+1)= 2

Press any key to continuePress any key to continue

程序说明:

对于二维数组a[2][2],指针常量a和a[0]是不同类型的指针。通过运行结果我们可以看到: (1)数组a的首地址为13ff70,整型数据长度为4字节。数组名a是指针常量,其值为13ff70,指向二维数组的第0行,每行长度为8,即比例因子为8;a+1指向数组的第1行,其值为13ff78。

(2)数组a第0行的名字为a[0]也是指针常量,其值为13ff70,指向二维数组第0行的第0个元素,长度为4,即比例因子为4;a[0]+1指向数组第0行的第一个元素,其值为13ff74。

(3)*(a+0)与a[0]等价,代表&a[0][0]的地址13ff70 ,*(a+1)与a[1]等价,代表&a[0][1]的地址13ff78。

(4)*(*(a+0))与数组元素a[0][0]等价其值为1,*(*(a+1)) 与数组元素a[1][0]等价其值为3。 (5)*(a[0]+0)也数组元素a[0][0]等价其值为1,*(a[0]+1) 与数组元素a[0][1]等价其值为2。

通过运行结果我们能清楚的看到二维数组的指针a,a+i,*(a+i),*(*(a+i))和一维数组的指针a[0],a[0]+i,*(a[0]+i)的意义与关系。

8.6指针与数组编程实例

8.6.1 数列的中位数

【例8-7】数列的中位数是数列中按照数值大小排在中间位置的数。例如:数列1,2,3,4,5的中位数为3。

显然,对于排序数列,很容易求得中位数。那么对于未排序的数列,有不同的算法进行计算。本例的算法是先对数列排序,然后寻找排序序列的中位数。我们利用排序函数对数列排序。

源程序:

/* 求数列的中位数 */ #include void main() { void sort(int a[], int n); int ser[100]; int i, num; printf(\ scanf(\ printf(\ for (i=0; i

}

/* 一维数组排序函数 */ void sort(int a[], int n) { int i, temp; int *p; for(i=0;i

运行结果:

Input length of serial: 5

input serial elements: 1 -3 23 67 15

The median of serial is 23 Press any key to continue

程序说明:

(1)在程序中,数列存储在数组int ser[100]中,数列长度num和数列元素ser[0],ser[1],. . . ser[num-1]需要从键盘读入。函数调用sort(ser, num)对数组ser[]排序。中位数位于排序数组中间位置,即第num/2个元素,通过*(ser+num/2)引用。

(2)函数void sort(int a[], int n)对长度为n的数组a[]按由小到大顺序排序,采用冒泡排序算法。

(3)排序函数void sort(int a[], int n)中第一个形式参数int a[],a是数组名,即一个指针,指向待排序数组;调用语句sort(ser,num)中,实参是数组名ser,是数组ser[]的首地址。在函数sort()的执行过程中,对数组a[]的任何访问和操作实际上就是对数组ser[]的访问和操作。函数void sort(int a[], int n)中第二个形式参数是int n,表示数组长度;调用语句sort(ser,num)中实际参数是num,是数组ser[]的实际长度。

如果有一个实参数组,需要在函数中能够引用该数组,访问或者改变数组元素的值,实参与形参可以有以下4种形式:

1 形参和实参都是数组名; ○

2 实参为数组名,形参为指针变量; ○

3 实参、形参都用指针变量; ○

4 实参为指针变量,形参为数组名。 ○

(4)利用指针操作数组元素

前边介绍过,如果p是指向数组元素的指针,*p对指针变量寻址后就是数组元素,在程序中,下列语句: temp=*p; *p=*(p+1); *(p+1)=temp;

完成*p所表示的数组元素与*(p+1)所表示的数组元素的交换,如图8-13所示。

p p+1

42(56) (2) 56(42) (1) 56 temp

(3)

图8-13 利用指针交换数组元素

8.6.2 求矩阵的最大元素

【例8-8】编写出程序读入矩阵元素,并且利用指针遍历矩阵所有元素,要求输出矩阵和矩阵元素的最大值。

源程序:

#include #include

void main() { int a[10][10]; int *p; int num, max, i; printf(\ scanf(\ /*输入数组的阶数*/ if (num<1 || num>10) { printf(\ exit(0); } printf(\ for (i=0; i

}

max=a[0][0];

for (i=0; i

for (i=0; i

printf(\输出最大元素*/

运行结果:

Input the element of column: 3

Input the element of matrix: 1 2 7 4 5 3 9 8 0 1 2 7 4 5 3 9 8 0

The max element is: 9 Press any key to continue

程序说明:

本例首先读入矩阵的列数num(在上述运行中,列数为3),并且假设矩阵为方阵;然后按行读入矩阵的各个元素,计算矩阵的最大值max;最后打印输出矩阵和矩阵的最大值。

(1)在程序中定义了二维数组a[10][10]和一个指向整形变量的指针p。

(2)语句for (p=a[i]; p<=&a[i][num-1]; p++), 使p的初值为a[i],a[i]与&a[i][0]等价,指针p得到赋值后,指向数组a[i][10]的0个元素。在循环中用scanf(\读入数据,因为p为int *类型的指针,因此比例因子是4,p++操作后,则p跳过4个字节,指向下一个数组元素。&a[i][num-1]是第i行最后一个数组元素的地址。

(3)p为指向数组元素的指针,在语句max=*p;和printf(\中,都是用*p访问数组元素的。

(4)C语言允许把一个二维数组分解为多个一维数组来处理,因此数组a[10][10]被分解为10个一维数组,即a[0],a[1],a[2]. . . a[9],每一个一维数组又含有10个元素。

8.7 指针与字符串

8.7.1字符型指针、字符数组与字符串常量的概念

前边我们已经介绍过在C语言中可以用字符数组表示一个字符串。例如: char str1[6] = \

该语句定义了一个字符数组str1[6],并且将数组元素str1[0],str1[1],str1[2],str1[3],str1[4]赋值

为'h','e','l','l','o',编译器在字符串的末尾str1[5]自动插入空字符'\\0'作为字符串结束符。

本节我们介绍另外一种创建字符串的方法:使用char类型的指针变量。例如: char *str2 = \

该语句定义了一个char类型指针变量str2,另外创建了一个字符串常数\,将其地址赋值给指针变量str2。于是,指针str2指向字符串的首字符,如图8-14所示:

str2

o r l d \\0 w

图8-14 字符串指针

也可以在运行时给字符串指针赋值。例如: char *str2; str3 = \

这个赋值操作不是复制\中的字符,而仅仅是使str2指向字符串”world”的第一个字符。

需要注意的是:

(1)在C语言中,\表示字符串常量,该字符串不能被改变。 例如: *str2='d'; 试图给字符指针str2所指向的字符赋值是错误的。 (2)数组名str1是指针常量,不能为指针常量赋值。 例如: str1=str2; 是错误的。语句: str1=\ 也是错误的。

(3) str2=str1; 是合法的赋值语句,使指针str2指向字符数组str1的首元素字符。对指向字符数组的指针str2进行赋值*str2=?d?;也是合法的,因为字符数组表示字符串变量。

8.7.2编程实例判断回文单词和回文句子

1.判断回文单词

字符指针指向字符串后,便可以通过指针访问和操作字符串中的各个字符,具体操作见下面例题。

【例8-9】写程序,判断一个单词是否为回文。回文指的是单词按从前到后与从后到前的顺序读都相同,例如“level”就是是一个回文单词。单词用字符串表示。

源程序:

#include #include

#define MAXLEN 80 /*符号常量MAXLEN,代表一行的字符数 */

void main()

{ char *str; int flag; int pal(char *);

/* 动态申请内存空间*/ if ((str = (char *) malloc(MAXLEN)) == NULL) { printf(\ exit(0); } printf(\ scanf(\ while (*str != '#') { flag=pal(str); if (flag) printf(\ else

printf(\ printf(\ scanf(\ } free(str); }

/*判断是否为回文单词*/ int pal(char *str) { char *p, *q; p = str; q = str; /* p指向字符串的首部*/ while (*q != '\\0') q++; /* q指向字符串的尾部*/ q--; while (p < q) if (*p == *q) { p++; q--; } else break; return (p >= q); }

运行结果: Input a word: level

level is a palindrome!

Input a word: ever

ever isn't a palindrome! Input a word: #

Press any key to continue

程序说明:

(1)程序通过循环,重复从键盘读入单词,直到遇到'#'字符,则跳出循环。

在程序中,定义了字符指针str指向单词字符串,但由于str只是一个指针,所指向的字符串并没有分配内存空间,所以需要调用动态分配内存函数str=(char *) malloc(MAXLEN)为字符串str分配内存空间。

函数void *malloc(unsigned t)申请分配长度为t字节的连续内存,函数的返回值类型为指针。如果申请得到满足,则函数返回值为指向该内存块的首地址;否则,如果申请分配失败,则返回值为NULL。

在程序中,str=(char *) malloc(MAXLEN)请求分配长度为MAXLEN字节的内存,并将其首地址赋值给str,这样str指向该块内存。

对于动态分配得到的内存,在程序结束之前,必须调用函数void free(void *ptr)释放该内存,ptr是动态分配所得内存的指针。在本程序中,free(str)释放指针str所指向的内存。

有关动态内存的分配和管理,将在第十一章中进行详细说明。

(2)类似于通过指针访问数组元素,可以通过对指针的加减访问字符串中的字符。

程序中函数int pal(char *str)为判断str所指向的字符串是否为回文,如果是回文,返回1,否则返回0。

函数int pal(char *str)内部定义了两个局部变量: char *p, *q; 函数开始时,使p是指向字符串首部(第0个字符),q指向字符串尾部(结束符'\\0'之前的字符)。比较*p与*q是否相等,如果不相等,显然单词不是回文;如果相等,将p指针后移,q指针前移,继续比较,直到指针p和指针q指向同一个字符,或者指针p指向指针q之后的字符,该单词为回文。如图8-15所示。

p

p

p++ p

s a i p p u a k a u p p i a s q-- q

图8-15 指针访问字符串的字符

q q

p和q为字符类型指针,因此p递增和q递减的比例因子为1个字节。

2.判断回文句子 通过上边的例题,我们介绍了利用指针访问字符串中的字符,下面的例题将要介绍综合运用指针表示的字符串与数组表示的字符串。

【例8-10】写程序,判断一个句子是否为回文句。回文句指的是一个句子按从前到后与从后到前的顺序读都相同(不计标点符号和空格),例如“a man, a plan, a canal, panama!” 是一个回文句。句子是一个字符串。

源程序:

#include #include

#define MAXLEN 300 /*符号常量MAXLEN,代表一行的字符数 */

void main() { char str2[MAXLEN]; char *str; int flag; int pal(char *); void trim(char *, char *);

/* 动态申请内存空间*/ if ((str = (char *) malloc(MAXLEN)) == NULL) { printf(\内存分配错误!\\n\ exit(0); } printf(\请输入一个句子:\\n\ gets(str); while (*str != '#') { trim(str, str2); flag = pal(str2); printf(\ if (flag) puts(\是一个回文句!\ else puts(\不是一个回文句!\ printf(\请输入一个句子:\\n\ gets(str); } free(str); }

/*判断是否为回文单词*/ int pal(char *str) { char *p, *q; p = str; q = str; /* p指向字符串的首部*/

while (*q != '\\0') q++; /* q指向字符串的尾部*/ q--; while (p < q) if (*p == *q) { p++; q--; } else break; return (p >= q); }

/* 去掉句子中的其它符号 */ void trim(char * str1, char * str2) { while (*str1 !='\\0') { if (*str1 >= 'a' && *str1 <= 'z') { *str2 = *str1; str2++; } str1++; } *str2='\\0'; }

运行结果: 请输入一个句子:

a man, a plan, a canal, panama!

\!\是一个回文句! 请输入一个句子: a man

\不是一个回文句! 请输入一个句子: #

Press any key to continue

程序说明:

类似例8-9,该程序通过循环,重复读入句子,直到遇到'#'字符,则跳出循环。程序中读入的句子放到动态分配的字符串str中。因为判断回文句不计标点、空格等符号,所以需要将str字符串中的除字母以外的符号去除,处理后的字符串放到数组字符串中。在本例中有一个前提,即在输入的句子中所有单词都是小写字母,去除句子中其它符号和空格的过程由函数trim(str, str2)完成,函数中的第一个参数str是指向源字符串的指针,第二个参数str2是数组名,该数组存放处理后的字符串。对于去除了其它符号的字符串str2,可以调用函数pal(str2)判断其是否为回文。

函数int pal(char *str)在例8-9中介绍过,在此不再赘述。

(1)利用指针访问数组字符串

函数void trim(char * str1, char * str2)有两个形式参数,其类型都是字符型指针。str1指向源字符串,str2指向目的字符串。

调用语句trim(str,str2);,第一个实参为str,第二个实参为str2。其类型是:

char str2[MAXLEN]; char *str;

参数传递时,指针str赋值给指针str1,数组名str2赋值给指针str2。

(2)在函数trim(char * str1, char * str2)中,通过 str1++和str2++运算,使str1和str2依次指向下一个数组元素。*str2=*str1将动态分配字符串中的字符赋值给数组元素字符。

需要注意的是,指针str1和指针str2并不是同步递增,遇到str1中的非字母字符,需要跳过(str1++,str2不变)。

因此该函数的功能为:只将字符串str1中的字母数组赋值给字符串str2,如图8-15所示。

str1

str1++

str1

a m a n a p l a n .. .. .. a m a n a p l a n .. .. .. str2 str2++ str2

图8-15 指针访问数组字符串

(3)字符数组与字符指针变量的区别

字符数组和字符指针变量都可表示字符串,并且实现对字符串的存储和运算。但在使用时需要注意:

1 字符指针变量本身是一个变量,可以存放字符串的首地址,从而对字符串寻址。字符串实○体则是存放在一块连续的内存空间中的字符序列,并以'\\0'作为字符串结束符。如果仅仅定义了字符指针,在没有分配连续的内存空间之前,字符串实体并不存在。要存储字符串实体,必须获得相应的内存空间。

而字符数组是由于若干个数组元素组成的,它可用来存放整个字符串。在定义字符数组的同时,已经得到了相应的内存空间。因此,在例8-10的程序中,需要为字符串str动态申请内存,而不需要对字符串str2申请内存。

2 定义字符指针时,可以直接用字符串常量赋初值。例如: ○ char *str=\也可以写为:

char *str; str=\

但不能用赋值方式如*str='K',以字符'K'改变字符串\的首字符'P'。

而对数组方式: char sa[]={\也可以写为:

char sa[]={'P', 'r', 'o', 'g', 'r', 'a', 'm', 'm', 'e', 'r', '\\0'}; 不能写为:

char sa[20]; sa={\

但可以对字符数组的各元素逐个赋值。如sa[0]='K'。

请读者思考:若输入的句子中包含大写字母,程序将如何改写。

类似地,两个字符串的比较操作,不能直接利用关系运算符?>?,?

char str[80], str1[80]; char *pstr=str, pstr1=str1; int i; pstr>pstr1; /* 指针比较,而非字符串比较 */ *pstr>*pstr1; /* 字符串首字符比较,而非整个字符串比较 */ strcpy(pstr, pstr1)>0 /* 正确用法,按字典序比较两个字符串pstr和pstr1 */

字符串需要有结束符。例如下面程序将键盘读入的一行字符串放到str中: char str[80],ch; int i=0; while ((ch=getchar())!='\\n') { str[i]=ch; i++; } str[i]='\\0'; /* 重要,为字符串str添加结束符?\\0? */

6.指针加减运算的意义

在标准C语言中,指针的加减运算只对指向数组元素的指针才有意义。对于指向非数组元素的指针,指针的运算为“未定义”。“未定义”的指针运算的结果是未知的。由于字符串等同于字符数组,因此对指向字符串的指针的加减运算是有意义的。

另外,指向数组的指针变量可以通过加法或减法运算使其指向前驱或者后继元素,但进行运算时需要注意指针所指向的元素在数组范围之内,不能越界使用。越界使用的结果也是未知的。例如:

int arr[80]; int *ptarr=arr; *ptarr=0; /* 正确,ptarr指向数组元素arr[0] */ *(ptarr+1)=1; /* 正确,ptarr+1指向数组元素arr[1] */ *(ptarr+80)=80; /* 危险用法,ptarr+80指向数组范围外元素,会产生运行错误 */

下面看一个关于字符串的例子,程序复制将字符串str复制到字符串str1中: char str[40]=\ char *pstr=str, *pstr1=str1; char ch; int i=0; while ((ch=getchar())!='\\n') { str[i]=ch; i++; if (i>=39) break; /* 条件判断不能省,防止输入过长而越界 */ } str1[i]='\\0'; i=0; while (*pstr != '\\0') { *pstr1=*pstr; i++; pstr++; pstr1++; if (i>=39) break; /* 条件判断不能省,防止字符串str1无结束符而出现越界 */

} *pstr='\\0'; /* 为字符串str添加结束符 */

7.正确使用运算符

在指针应用中,对涉及运算符的语句,需要正确理解各运算符的优先级和关联性。特别是*和++算符,例如:

#include void main() { int a[10] = {0,1,2,3,4,5,6,7,8,9}; int *p = a, test; test = *p++; /* 取p所指向的整数的值(0),p增加1,指向a[1] */ printf(\ /* 结果test is 0 */ printf(\ /* 结果*p is 1 */ }

8.9小结

1. 指针类型变量用下面语句定义:

基类型 * 指针变量名;

其中,基类型为指针所指向的数据类型,指针变量名应为合法标识符,*表示变量为指针。 2. 与指针变量有关的运算:

(1)取址运算

运算符&表示取变量的地址,假设a为一个整型变量,&a表示取变量a的地址。 (2)间接寻址运算

运算符*表示对指针变量进行间接寻址,即取指针变量所指向的变量。假设p是一个指向整型的指针变量,则*p表示取p所指向的整数。

(3)赋值运算

可以对指针变量进行赋值运算,可以把一个变量的地址赋给指针变量,也可以把一个指针变量的值赋给另一个指针变量。不同类型的指针赋值需要注意类型转换。

(4)指针加减

指针变量可以与整数值进行加减运算。

指针变量不能与整数作乘除运算,另外,两个指针变量相加无意义。 (5)指针比较

当两个指针指向相同数据类型变量时,可以使用关系运算符对它们进行比较操作。 3. 指针与函数

(1)指针作为函数参数

函数的参数可以是指针,例如: int f(int *a1, char *c1)

函数f的参数a1是指向整数的指针,参数c2是指向字符型的指针。调用函数f时,相应的实参必须是地址。这种函数调用方式称为“按引用调用”。

(2)指针作为函数返回值

函数的返回值可以是指针类型,例如: int *f()

函数f的返回一个地址,指向整型变量。需要注意的是,所返回的地址必须是调用函数中的变

量地址,如果返回的值是指向被调用函数中局部变量的地址,将产生错误。 4. 指针与数组

如果p=a,即指针p指向数组a的第0个元素,并且可以通过在p上执行指针算术运算访问数组a的其它元素。如*(p+2)与a[2]等价,p[2]与*(a+2)等价,它们都代表a[2]。 5. 指针与字符串

C语言中,字符数组可以表示一个字符串,指向字符的指针也可以表示一个字符串。

习题八

一、判断题

1.可以将任意整数赋值给指针。 2.两个指针变量不可以做加法运算。 3.两个指针变量不可以做减法运算。

4.数组名作为函数参数,实际上传递的是指针。

5.函数中局部变量的值在其他函数中没有办法加以改变。

6.定义 int (*p)[4]声明p为有四个整数元素的数组的指针。 7.i[a] 与 a[i]是一样的。

二、填空题

1.指针变量的值是内存单元的 。 2. 是取地址操作符。 3. 是取值操作符。

4. 和 是可以赋给指针变量的常数。 5.不能对 类型指针进行取值操作。 6.给定以下语句: int a=5, b=20; int *p=&a, *q=&b; 下列表达式 (*p)++的值是 ; --(*q)的值是 ; *p+(*p)--的值是 ; ++(*q)-*p的值是 。

7.下面程序是把从终端读入的一行字符作为字符串放在字符数组中,然后输出。请填空。

main()

{int i; char s[80], ﹡p;

for ( i=0; i<79; i++ ) { s[i]=getchar( ); if ( s[i]= =ˊ\\nˊ) break; } s[i]= ; p= ; while ( ﹡p ) putchar ( ﹡p++);}

8.下面程序是判断输入的字符串是否是“回文”,(顺读和倒读都一样的字符串称“回文”,如level)。请填空。

# include # include main ( )

{ char s[81], ﹡p1, ﹡p2; int n;

gets ( s ); n=strlen ( s ); p1=s; p2= ; while ( ) { if ( ﹡p1!= ﹡p2 ) break;

else { p1++; ; } }

if (p1

9.以下函数把b字符串连接到a字符串的后面,并返回a中新字符串的长度。请填空。

int strcen(char a[], char b[]) { int num=0,n=0;

while(*(a+num)!= ) num++;

while(b[n]){*(a+num)=b[n]; num++; } ;} return(num); }

10.以下函数的功能是删除字符串s中的所有数字字符。请填空。

viod dele(char *s) { int n=0,i;

for(i=0;s[i];i++) if( ) s[n++]=s[i];

s[n]= ; }

11.以下程序的执行结果是 。

# include main ( )

{ char s[ ]=”abcdefg”; char ﹡p; p=s;

printf(〞ch=%c\\n〞, ﹡(p+5)); }

12.以下程序的执行结果是 。

# include main ( )

{ int a[ ]={2,3,4}; int s, i,﹡p; s=1; p=a; for (i=0; i<3; i++) s﹡=﹡(p+i);

printf(〞s=%d\\n〞, s); }

13.以下程序的执行结果是 。

# include main ( )

{ char p1[20]= ”abcd”,﹡p2, str[20]= ”xyz ”; p2=”ABCD”; strcpy (str+1, strcat (p1+1,p2+1)); printf(〞%s〞, str); }

14.阅读程序

main()

{char str1[]=\,str2[10]; char *p1=str1,*p2=str2; scanf(\printf(\printf(\}

运行上面的程序,输入字符串PEOPLE AND COMPUTER,程序的输出结果是 。 15.以下程序的执行结果是 。

#include

int fun(int x,int y,int *cp,int *dp) { *cp=x+y; *dp=x-y; } void main(void) { int a, b, c, d; a=30; b=50; fun(a,b,&c,&d);

printf(\

三、选择题

1.下面几个字符串处理表达式中能用来把字符串str2连接到字符串str1后的一个是 。

A.strcat(str1,str2) ; B.strcat(str2,str1); C.strcpy(str1,str2) ; D.strcmp(str1,str2);

2.设有两字符串“Beijing”、“China”分别存放在字符数组str1[10],str2[10]中,下面语句中能把“China”连接到“Beijing”之后的为 。

A.strcpy(str1,str2); B.strcpy(str1, “China”); C.strcat(str1,“China”); D.strcat(“Beijing”,str2); 3.下列字符串赋值语句中,不能正确把字符串C program赋给数组的语句是 。

A.char a[]={?C?,? ?,?p?,?r?,?o?,?g?,?r?,?a?,?m?}; B.char a[10]; strcpy(a, “C program”); C.char a[10]; a= “C program”; D.char a[10]=“C program”;

4.判断字符串a和b是否相等,应当使用 。

A.if (a= =b) B.if (a=b)

C.if (strcpy(a,b)) D.if(strcmp(a,b)==0)

5.选择出 i的正确结果 。

int i; char ﹡s=”a\\045+045\\tb”; for ( i=0;s++;i++); A.5 B.8 C.11 D.12 6.如下程序的执行结果是 。

# include

main() { int a[ ]={1,2,3,4,5,6}; int *p; p=a;

*(p+3)+=2; printf(\ }

A.1,3 B.1,6 C.3,6 D.1,4

7.若有以下定义,则对a数组元素的正确引用是 。

int a[5],*p=a;

A.*(++a) B.a+2 C.﹡(p+5) D.﹡(a+2)

8.若有以下定义,则对a数组元素地址的正确引用是 。

int a[5], ﹡p=a;

A.p+5 B.﹡a+1 C.&a+1 D.&a[0]

9.若有定义: int a[2][3];则对a数组的第i行第j列(假设i,j已正确说明并赋值)元素值的正确引用为 。

A.﹡(﹡(a +i) +j ) B.(a+i) [j] C.﹡(a+i+j) D.﹡(a +i)+j

10.若有定义: int a[2][3];则对a数组的第i行第j列(假设i,j已正确说明并赋值)元素地址的正确引用为 。

A.﹡(a [i] +j ) B.(a+i) C.﹡(a+j) D.a[i]+j

本文来源:https://www.bwwdw.com/article/oev8.html

Top