第 11讲数组与指针感觉越来越难的原因是对前面的知识没有及时理解和掌握。
看懂、听懂并不等于会用。
课堂上时间有限,大家一定要及时预习和复习。
复习时注意只需要把我课上讲的几个例题理解透彻并掌握即可。
编程步骤:
分析?程序框架 (粗算法 )?细算法?程序?调试另:贵班某些人的学习情况令人担忧!
2
统计总分和平均分
算法:
输入全班学生的成绩
统计总分
计算平均分
输入总分和平均分
#include <stdio.h>
#define N 30
void main()
{int a[N],i;
float sum=0,ave;
puts(“please input %d score:”,N);
for(i=0;i<N;i++)
{ scanf("%d",&a[i]);
sum+=a[i];
}/*输入并统计总分 */
ave=sum/N;
printf(" Sum=%.1f,Average=%.1f.\n",sum,ave);
}
将,求一个数组所有元素之和,写成函数,怎样写?
3
写成多函数形式
#include <stdio.h>
#define N 30
/*函数功能:求一个长度为 n的数组中所有元素的总和 */
float Sum(float a[],int n)
{int i;
float sum=0;
for(i=0;i<n;i++)
{ sum+=a[i];
}/*统计总分 */
return sum;
}
void main()
{int score[N],i;
float sum=0,ave;
puts(“please input %d score:”,N);
for(i=0;i<N;i++)
{ scanf("%d",& score[i]);
}/*输入 */
sum=Sum(score,N);
/*函数调用,注意实参的格式 */
printf("Sum=%.1f,Average=%.1f.\n",
sum,sum/N);
}
重点:数组名做函数参数
调用过程?
4
难点:函数调用过程形参数组 a
sum函数参数传递传过来的是数组首地址实参数组与形参数组共占内存即两者是同一个数组只是名称和作用域各不相同
6000
实参数组 score
主函数
67
78
97
79
6000
67
78

79
97
实参数组
5
涉及语法
-数组名作函数参数
传递整个数组到另一个函数内,只是 将数组的首地址作为参数传过去
用数组名作为函数参数
只拷贝一个地址自然比拷贝全部数据效率高
由于首地址相同,故 实参数组与形参数组占用同一段内存
这样,在该函数内,不仅可以读这个数组的元素,
还可以修改它们
普通变量 做参数,形参另占 一个 存储空间,存储由实参传递过来的 数值 。
数组 做参数,形参另占 一个 存储空间,存储由实参传递过来的 地址 。
6
写函数,计算字符串长度
算法:
输入字符串
计算其中的字符个数
从第一个字符数起,只要不到末尾,就加 1
输出
#include<stdio.h>
unsigned int My_strlen(char a[]);
/*函数功能:求一个字符串中字符个数 */
unsigned int My_strlen(char a[])
{int i,number=0;
for(i=0;a[i]!=0;i++)
{number++;
}
return number; /*直接返回 i(不用 number)即可 */
}
void main()
{
char str[50];
int n;
puts(“please input a string”);
scanf("%s",s);
n=My_strlen(s);
printf("strlen(\" %s\")=%d.\n",s,n);
}
7
写函数,比较字符串大小
算法:
输入两个字符串 a,b
比较字符串 a,b的大小
从第一对字符比起,当两个对应的字符不相等或者字符串结束时,循环结束,并计算两者之差,否则继续比较。
输出
z h a n g l i n g \0 \0
z h a n g y u \0 \0 \0 \0
#include<stdio.h>
int My_strcmp(char a[],char b[]);
/*函数功能:比较两个字符串的大小,返回它们的差 */
int My_strcmp(char a[],char b[])
{int i;
for(i=0; a[i]==b[i]&&(a[i]!=?\0?||b[i] !=?\0?);i++);
/*找第一个不相等的字符 */
return a[i]-b[i]; /*返回它们的差 */
}
void main()
{char str1[50],str2[50];
int minus;
printf(" please input two strings,");
scanf("%s%s",str1,str2);
minus=My_strcmp(str1,str2);
if(n>0)
printf(" %s>%s.\n",str1,str2);
else if(n<0)
printf(" %s<%s.\n",str1,str2);
else
printf(" %s=%s,n",str1,str2);
}
8
写函数,连接两个字符串
算法:
输入两个字符串 a,b
连接字符串 a,b
找到第一个字符串的末尾
将第二个字符串接入
输出
h e l l o \0
l i \0
#include<stdio.h>
void My_strcat(char a[],char b[]);
/*函数功能:将两个字符串连接后存在 a中 */
void My_strcat(char a[],char b[])
{int i;
for(i=0; a[i]!=?\0?;i++);
/*i指向 a串最后一个字符的下一个位置,这句也可直接改为
i=strlen(a);*/
for(j=0; b[j] !=?\0?;j++)
a[i+j]=b[j];
/*将 b串中的字符一个一个接入 */
a[i+j]=?\0?;
/*在 a串中写入一个结束标记 */
}
void main()
{
char str1[100],str2[50];
int n;
printf(" please input two strings,");
scanf("%s%s",str1,str2);
My_strcat(str1,str2);
printf("The result:%s.\n",str1);
}
9
调用过程形参数组 a
My_strcat函数参数传递传过来的是数组首地址实参数组与形参数组共占内存
6000
67
78
97
79
l
l
o
h
e
l
i
……
实参数组 str1
主函数实参数组 str2 形参数组 b
6200
H
E
L
l
o
l
i
6000
6200
实参数组主函数实参数组
更多例题请参数例 6- 14至 6- 17
10
新的概念,指针( Pointer)
指针也是一种数据类型
一种特殊的数据类型,这种类型存储的是 地址
C/c++的特色
有些复杂但很实用
指针变量与指针常量
指针变量
专门存放地址数据的变量
指针常量
指一个固定的地址,例如:数组名
11
两种寻址方式
如何读写内存中的数据?
通过变量的地址访问变量所在的存储单元
两种寻址方式
直接 (寻址)访问
直接按变量地址来存取变量内容的访问方式
间接 (寻址)访问
通过指针变量来间接存取它所指向的变量的访问方式房间 1
东西房间 2
房间 1
的钥匙直接 寻址 间接 寻址
12
间接寻址访问:
int i;
int *p;
p=&i;
*p=3;
重点:直接寻址与间接寻址的表示
直接寻址访问:
int i;
i=3;
2000
0

变量 i

3010 变量 pi
地址 数据 变量名
如果能直接寻址那当然就不用间接寻址了,
但有时,只能用间接寻址的方法解决问题

数据类型,int *(指向整型变量的指针)
变量名称,p
变量的初值,&i
含义:向系统申请一个动态区的内存空间,用来存储整型指针变量 p的初值 &i,即将 p指向了变量 i。
通过 *p这种形式,实现对变量 i的 间接引用 。
①②2000
3
13
为什么引入指针的概念
指针有如下好处:
为函数提供修改变量值的手段
为 C的动态内存分配系统提供支持
可以改善某些子程序的效率
为动态数据结构(如例链表、队列、二叉树等)提供支持
14
int i,*p;
p=&i;
*p=100;
int i,*p=&i;
float j,*q=&j;
p=q;
int i;
float *p;
p=&i;
int *p;
p=100;必须进行赋值才能引用 !否则
p指向了哪呢?
一个指针变量不能指向与其类型不同的变量 !float又称变量 P的基类型应在类型相同的指针变量之间赋值
int *p;
scanf(“%p”,&p);
*p=100;
指针 变量 只存放地址 !
int *p;
*p=100;
对指针变量赋值只能通过 &求得!
15
例:读程序main(){int a=5,b=10;
int *pa=&a,*pb=&b;
*pb=-1;
printf(“b=%d\n”,*pb);
pa=pb;
printf(“*pa=%d,*pb=%d”,*pa,*pb);
}
读程序的关键:
–指针变量指向哪儿?
–所指向的变量里存储的数据是多少?
–更改的是指针还是指针所指向的变量?
&a &b
pa pb
5 10
a b
-1
16
主调函数 被调函数
main()
{ int a,b;
a = 15;
b = 8;
Swap(a,b);
printf
("a=%d,b=%d",a,b);
}
void Swap(int x,int y)
{ int temp;
temp = x;
x = y;
y = temp;
}
55
a b
实 参 形 参
99
x ya b
传数值调用
x y
5
temp
9 5
17
主调函数 被调函数
main()
{ int a,b;
a = 15;
b = 8;
Swap(&a,&b);
printf("a=%d,b=%d",a,b);
}
void Swap(int *x,int *y)
{
int temp;
temp = *x;
*x = *y;
*y = temp;
}
&a
实 参 形 参
&b
x ya b
传地址调用
x y
5
temp
5
a b
99 5 地址值
指针的好处之一,
通过 将主调函数变量的地址传给形参 实现了 改变主调函数中变量的值 。
当计算结果不只一个时,
可以用这种方法,实现数据的“返回”。
18
指针变量与其它类型变量的对比
共性
在内存中占据一定大小的存储单元
先定义,再使用
指针变量的特殊性
指针变量只能存放地址,而不能存放数据
必须初始化后才能使用,否则指向不确定的存储单元
只能指向同一基类型的变量,否则 warning.
可参与的运算:加、减、关系、赋值
19
指针运算-地址运算
单个指针的运算
加减运算:每加(减) 1表示指针指向后(前)一个数据单元
例,p++;--p;p=p+n;
两个指针的运算
比较:用来比较两个指针的前后位置
例,(p1>p2)
相减:两个指针相距多远?
主要用于对数组的处理
20
读程序- 1
main ( )
{ int a[]={2,4,6,8,10};
int y=0,x,*p;
p=&a[0];
for(x=0;x<5;x++)
y + = * (p+x);
printf("%d\n",y);
}
两个重要内容:
指针指向了哪个数组元素?
是指针运算还是数组元素运算
6000
6001
6002
6003
6004
6005
6006
6007
6008
6009
a
a[0]
a[1]
a[2]
a[3]
a[4]
2
4
6
8
10
p
6000
p+1
p+2
p+3
p+4
21
用数组名(指针常量)
引用数组元素
6000
6001
6002
6003
6004
6005
6006
6007
a[0]
a[1]
a[2]
a[3]
a
a+1
a+2
main()
{int a[10],i;
puts(“please input %d score:”,N);
for (i=0; i<10; i++)
scanf("%d",&a[i]);
for (i=0; i<10; i++)
printf("%d ",a[i]);
}
输入输出数组元素:
方法 1:数组名+下标
ain()
{int a[10],i;
puts(“please input %d score:”,10);
for (i=0; i<10; i++)
scanf(“%d”,? );
for (i=0; i<10; i++)
printf("%d ",? );
}
输入输出数组元素:
方法 2:数组名 +偏移量
a+i
*(a+i)
22
用指针变量引用数组元素
main()
{int a[10],i;
int *p=a;
puts(“please input %d score:”,10);
for (i=0; i<10; i++)
scanf("%d",&p[i] );
for (i=0; i<10; i++)
printf("%d ",p[i] );
}
输入输出数组元素:
方法 3:指针变量+下标
也可以用指针变量引用一维数组元素
int a[10],*p=a;
6000
6001
6002
6003
6004
6005
6006
6007
a[0]
a[1]
a[2]
a[3]
a
p
p+1
p+2
main()
{int a[10],i;
int *p=a;
puts(“please input %d score:”,10);
for (i=0; i<10; i++)
scanf(“%d”,? );
for (i=0; i<10; i++)
printf("%d ",? );
}
输入输出数组元素:
方法 4:指针变量 +偏移量
p+i
*(p+i)
23
用指针变量引用数组元素
main()
{int a[10],i;
int *p;
for (p=a; p<(a+10); p++)
scanf("%d",p);
for (p=a; p<(a+10); p++)
printf("%d ",*p);
}
输入输出数组元素:
方法 5:指针变化
也可以用指针变量引用一维数组元素
int a[10],*p=a;
6000
6001
6002
6003
6004
6005
6006
6007
a[0]
a[1]
a[2]
a[3]
a
p
p++
p++
通过数组名的变化引用数组元素行不行?
数组元素的引用形式
a[i]或 *(a+i)
p[i]或 *(p+i)
*(p++)
24
区分几个表达式
P++
*p++
*(p++)
*(++p)
(*p)++
指向下一个地址单元
间接引用 p指向的数据单元,然后,p指向下一个地址单元
同上
先将 p指向下一个地址单元,然后间接引用
p指向的数据单元
P指向的数据 +1
25
读程序 -2
#include <stdio.h>
main()
{
int a[] = {1,2,3,4,5};
int *p = NULL;
p = a;
printf("%d,",*p);
printf("%d,",*(++p));
printf("%d,",*++p);
printf("%d,",*(p--));
printf("%d,",*p++);
printf("%d,",*p);
printf("%d,",++(*p));
printf("%d,",*p);
}
1,2,3,3,2,3,4,4
两个重要内容:
指针指向了哪个数组元素?
是指针运算还是数组元素运算
26
小结
数组做函数参数
定义和调用格式
调用过程和参数传递过程
指针的基础知识
寻址方式
指针的概念、定义、赋值、间接引用、运算
指针与一维数组的关系
27
例 6- 7 判断回文数
例,123454321是回文数
再例,12345321不是回文数
算法:
1 2 3 4 5 3 2 1 \0 \0 \0
i=0 j=strlen(str)-1
str
i=1 j=strlen(str)-2……
作业:
将例 6- 7改写成多函数形式,即写一个函数:判断一个字符串是否是回文,是返回 1,否则返回 0。然后在主函数中调用。