C语言程序设计第四章 简单构造数据类型
——————————————————————————
——————————
济南大学第四章 简单构造数据类型
4.1 一维数组的引出及使用
4.2 二维数组的引出及使用
4.3 字符数组
4.4 数组与函数
4.5 数组与指针
4.6 字符串与指针
——————————————————————————
4.1 一维数组的引出及使用
4.1.1 一维数组的引出
4.1.2 一维数组的定义和引用
4.1.1 一维数组的引出例,计算一个班 40个学生 C语言考试成绩的平均分,每个学生的成绩由键盘输入,输出所有学生的考试成绩和平均成绩。
main ( )
{
int i;
float s,sum=0,ave;
for (i=0; i<40; i++)
{
scanf("%f",&s);
sum=sum+s;
printf(“%f,,s);
}
ave=sum/40 ;
printf("ave=%.2f\n",ave);
}
定义 40个变量 ;
定义 一个变量 +循环,新成绩覆盖旧成绩的值;
定义一个含有 40个元素的数组 +循环 。
main ( )
{
int i;
float s,sum=0,ave;
for(i=0; i<40; i++)
{
scanf("%f",&s);
sum=sum+s;
printf(“%f,,s);
}
ave=sum/40 ;
printf("ave=%.2f\n",ave);
}
main ( )
{
int i;
float s[40],sum=0,ave;
for(i=0; i<=39; i++)
{
scanf("%f",&s[i]);
sum=sum+s[i];
}
ave=sum/40 ;
for(i=0; i<40; i++)
printf("%.2f ",s[i]);
printf("ave=%.2f\n",ave);
}
例:计算一个班 40个学生 C语言考试成绩的最高分,并输出所有学生的考试成绩。
main ( )
{
int i;
float s[40],max=0;
for(i=0; i<40; i++)
scanf("%f",&s[i]);
for(i=0; i<40; i++)
{
printf("%.2f ",s[i]);
if(s[i]>max) max=s[i];
}
printf(“max=%.2f \n",max);
}
数组的概念为了处理一批 类型相同 的数据,引入了数组。
①,数组:由 相同类型 的 具有固定个数 的元素组成的集合。
数组中的 所有元素都属于同一个数据类型 。
②、每个数组元素都是一个变量,其类型为数组的类型。 与相同类型的普通变量 完全一样 。
③、数组元素在数组中的序号称为 下标 。 通过数组名和下标来唯一的确定每一个元素 。
4.1.2 一维数组的定义和引用
1 一维数组的定义
(1)格式,类型标识符 数组名 [常量表达式 ];
例,int a[10]; char c[20]; double b[5];
(2)说明,
①,数组名是用户自定义 标识符 ; 数组名表示数组所占内存区域的首地址 (即第 1个元素的地址);
②、数组元素的 下标从0开始 ;
例如:数组 a包含 10个元素,a[0],a[1],a[2],…,a[9]
③、常量表达式可以包含符号常量,但 不能包含变量 。也即是说 数组的大小在程序运行前已确定好,不依赖于程序运行中变量值的变化而变化;
#define M 20
int a[M];
int n;
scanf("%d",&n);
int a[n];
int n=5;
int a[n];
④、一维数组在内存中的存放:
整个数组占用一段连续的内存单元,各元素 按下标顺序存放如,float x[10];
2000 x[0]
2004 x[1]
2008 x[2]
2012 x[3]
2016 x[4]
2020 x[5]
2024 x[6]
2028 x[7]
2032 x[8]
2036 x[9]
2 一维数组元素的引用
(1) 引用形式:
数组名 [下标 ]
(2) 说明:
①、下标可以是 整型常量 或 整型表达式 ;
a[0] a[2] a[2*3] a[i]
②,下标从0开始 ;当数组长度为 N时,则数组的下标值可以是 0,1,2,……,N-1,如果用 a[N]是错误 的。
注意,若定义数组 int a[5]; 则下标范围为 0--4;但如果出现
a[5]=12;编译时不会指出错误,系统会将 a[4]后的下一个存储单元赋值为 12,这样就可能会破坏数组以外其他变量的值,
需要特别注意!
(3) 对于数组,int a[10];
①,数据输入:
for (i=0; i<=9; i++)
scanf("%d",&a[i]);
②,数据输出:
for (i=9; i>=0; i--)
printf("%4d",a[i]);
3 一维数组的初始化
(1)、在定义数组时对全部数组元素赋以初值(最好将数组的存储类型定义为 static或 extern)
static int a[10]={0,1,2,3,4,5,6,7,8,9};
(2)、可以只给一部分数组元素赋初值,系统自动对其余元素赋一缺省值,static int a[10]={1,3,5,7,9};
等价于,static int a[10]={1,3,5,7,9,0,0,0,0,0};
(3)、若对 static数组不赋值,系统自动对所有元素赋一缺省值
static int a[5];
等价于,static int a[5]={0,0,0,0,0};
(4)、对全部数组元素赋初值时,可以不指定数组长度,其长度由初值个数自动确定:
static int a[ ]={0,1,2,3,4};
等价于,static int a[5]={0,1,2,3,4};
(5)、不允许数组指明的元素个数小于初值个数:
static int a[5]={0,1,2,3,4,5}; 编译时出错
4 一维数组程序举例例 1:用数组来处理求 Fibonacci数列问题。
Fib1=1 (n=1)
Fib2=1 (n=2)
Fibn=Fibn-1+Fibn-2 (n≥3)
f[0]
f[1] 1
f[2] 1
f[3] 2
f[4] 3
f[5] 5
f[6] 8
f[7] 13

f[20] 6765
static int f[21]={0,1,1};
for (i=3; i<=20; i++)
f[i]=f[i-1]+f[i-2];
for (i=1; i<=20; i++)
{ printf("%12d",f[i]);
if (i%5==0)
printf("\n");
}
例 2:用起泡法将 10个整数由小到大排序。
起泡法的思路:将相邻两个数比较,将小的交换到前面。
9
4
3
5
8
0
4
9
3
5
8
0
4
3
9
5
8
0
4
3
5
9
8
0
4
3
5
8
9
0
4
3
5
8
0
9
3
4
5
0
8
9
3
4
0
5
8
9
3
0
4
5
8
9
0
3
4
5
8
9
结论:若有 n个数,则要进行 趟比较。n-1
在第 1趟比较中,要进行 次两两比较;n-1
在第 i 趟比较中,要进行 次两两比较;n-i
输入 n个数给 a[1]~ a[n]
for (i=1; i<=n-1; i++)
for (j=1; j<= ; j++)n-i
a[j]>a[j+1] 假真
a[j]←→ a[j+1]
输出 a[1]~ a[n]
#include <stdio.h>
void main( )
{ int a[11],i,j,t;
for ( i=1; i<=10; i++)
scanf("%d",&a[i]);
for ( i=1; i<=9; i++)
for ( j=1; j<=10-i; j++)
if (a[j]>a[j+1])
{ t=a[j];
a[j]=a[j+1];
a[j+1]=t;
}
for ( i=1; i<=10; i++)
printf("%-4d",a[i]);
}
4.2 二维数组的引出及使用
4.2.1 二维数组的引出
4.2.2 二维数组的定义和引用例:假设考试共有 5个科目,一个班有 40名学生。输入所有学生的各科成绩,求出每名学生的总成绩。
main ( )
{ int i,j; float s[40][5],sum[i];
for(i=0; i<40; i++)
{ sum[i]=0;
for(j=0; j<5; i++)
{ scanf(“%f”,&s[i][j]);
sum[i]=sum[i]+s[i][j];
}
}
for(i=0; i<40; i++)
{ for(j=0; j<5; i++) printf(“% -8f”,s[i][j]);
printf(“% -8f\n",sum[i]);
}
}
4.2.1 二维数组的引出
1 二维数组的定义
(1)、格式:
类型说明符 数组名 [常量表达式 ][常量表达式 ];
int a[3][4]; float b[5][10];
a[0][0] a[0][1] a[0][2] a[0][3]
a[1][0] a[1][1] a[1][2] a[1][3]
a[2][0] a[2][1] a[2][2] a[2][3]
(2)、存储方式:
在内存中 按行存放,即先顺序存放第一行的元素,再存放第二行的元素。
2000 a[0][0]
2002 a[0][1]
2004 a[0][2]
2006 a[0][3]
2008 a[1][0]
2010 a[1][1]
2012 a[1][2]
2014 a[1][3]
2016 a[2][0]
2018 a[2][1]
2020 a[2][2]
2022 a[2][3]
4.2.2 二维数组的定义和引用
(3)、说明:
根据数组的定义方式,可以将二维数组看作是一种特殊的一维数组,它的 每一个元素又是一个一维数组 。
int a[3][4]; a[0]
a[1]
a[2]
a[0][0] a[0][1] a[0][2] a[0][3]
a[1][0] a[1][1] a[1][2] a[1][3]
a[2][0] a[2][1] a[2][2] a[2][3]
2 二维数组的引用二维数组元素的表示形式,数组名 [下标 ][下标 ]
其中行号和列号可以是整型表达式或整型常量,其值从 0开始,不能超过数组定义的范围
a[1][2] a[2-1][2*2-1]
a[i][j] b[1][2]=a[2][3]/2
static int a[3][4];
……
a[3][4]=5; ×
3 二维数组的初始化
(1)、按行给二维数组赋初值:
static int a[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};
1 2 3 4
5 6 7 8
则结果为,9 10 11 12
(2)、按数组存储顺序依次给各元素赋初值,
static int a[3][4]={1,2,3,4,5,6,7,8,9,10,11,12};
注意,此方法数据没有明显的界限,当数据较多时容易出错
(3)、可以对部分元素赋初值:( 其余赋缺省值 )
static int a[3][4]={{1},{5},{9}};
1 0 0 0
5 0 0 0
则结果为,9 0 0 0
(4)、只对数组各行中的某些元素赋初值:
static int a[3][4]={{1},{0,5},{0,0,9}}; 1 0 0 00 5 0 0
则结果为,0 0 9 0
(5)、只对数组中前面几行赋初值,后面各行的元素自动赋缺省值:
static int a[3][4]={{1},{3,5}};
1 0 0 0
3 5 0 0
则结果为,0 0 0 0
(6)、当对全部数组元素赋初值时,第一维的长度可以不说明,但第二维长度不能省:
static int a[ ][3]={1,2,3,4,5,6,7,8,9};
1 2 3
4 5 6
则结果为,7 8 9
(7)、按行对部分元素赋初值,可以省略第一维的长度说明:
static int a[ ][3]={{0,0,3},{0,2},{1}}; 0 0 30 2 0
则结果为,1 0 0(8)、同一维数组一样,不允许所提供的初值个数多于数组的元素个数。
4 二维数组程序举例例 1、将二维数组 a[M][N]中的元素行列互换,存到另一个数组
b[N][M]中。
1 2 3 4
a= 5 6 7 8
9 10 11 12
1 5 9
b= 2 6 103 7 11
4 8 12
#include<stdio.h>
void main( )
{ int a[3][4],b[4][3],i,j;
printf("\nArray A(3*4):\n");
for (i=0; i<3; i++)
for (j=0; j<4; j++)
scanf("%d",&a[i][j]);
for (i=0; i<3; i++)
for (j=0; j<4; j++)
b[j][i]=a[i][j];
printf("Array B(4*3):\n");
for (i=0; i<4; i++)
{ for (j=0; j<3; j++)
printf("%5d",b[i][j]);
printf("\n");
}
}
例 2、有一个 3× 4的矩阵,求出其中最大的那个元素的值,以及其所在的行号和列号。
int row,colum,max;
max=a[0][0];
row=0; colum=0;
for (i=0; i<3; i++)
for (j=0; j<4; j++)
if (a[i][j]>max)
{ max=a[i][j];
row=i;
colum=j;
}
4.3 字符数组
4.3.1 字符数组的引出
4.3.2 字符数组的定义和使用
4.3.3 字符串的定义和使用
4.3.4 字符数组程序举例用户预先设置一个 N为长度的密码,密码由数字、字母、符号等组成。 3次之内,按用户输入的密码正确与否,输出?欢迎!?、密码错误!?;超过 3次后输出?退出!?。
#define N 10
main()
{ char code[11]=“ABC123*xyz”;
char user[11];
int i,k,t=0;
while(1)
{ k=1;
printf(“\nInput your key:”);
gets(user);
for(i=0;i<N;i++)
if(code[i]!=user[i]) k=0;
t++;
if(k==0)
printf(“\nKey error!”);
else
{ printf(“\nWELCOME!”);
break;
}
if(t>3)
{ printf(“\nExit!”);
break;
}
}
}
4.3.1 字符数组的引出用来存放字符型数据的数组叫 字符数组 。字符数组的每个下标变量中只能存放一个字符。
1 字符数组的定义
(1)、格式:
char 数组名 [字符个数 ];
(2)、例,char c[5];
c[0]='C'; c[1]='h'; c[2]='i'; c[3]='n'; c[4]='a';
(3)、说明:
C语言中没有字符串变量,而使用一维字符数组来存放一串字符。
4.3.2 字符数组的定义和使用
2 字符数组的初始化逐个字符赋给数组中的元素:
static char c[5]={'C','h','i','n','a'};
①,如果花括弧中的初值个数小于数组长度,按顺序赋值后,
其余元素自动赋空字符 '\0';
static char str[10]={'B','e','i','J','i','n','g'};
则,str[7]=str[8]=str[9]='\0';
②,若初值个数大于数组长度,按语法错误处理;
③、若提供的初值个数与预定的数组长度相同,在定义时可省略数组长度。
static char c[ ]={'C','h','i','n','a'};
3 字符数组的输入输出用 "%c"格式控制符实现逐个字符的输入输出:
#include <stdio.h>
void main( )
{ int i=0;
char str[20];
while ((str[i++]=getchar( ))!='\n') ;
str[i]='\0';
for (i=0; i<20; i++)
printf("%c",str[i]);
}
字符串 是由双引号括起来的字符序列,例如:
Hello World!”,China”,How are you?”
1 字符串的定义
C语言中没有字符串变量,而使用 一维字符数组 来存放一串字符。
程序在定义时会在每个字符串的后面自动加上一个空操作符
‘ \0’。‘ \0’不会计入字符串的长度中。但将会占用一个元素的存储空间,所以在定义字符数组时,应在字符串长度的基础上增加一个元素。
例如,? How are you?”有 12个字符,在内存中实际占 13
个字,包含一个字符串结束标志’ \0’。
4.3.3 字符串的定义和使用可以对字符串整体进行赋值初始化,例如:
static char c[ ]={"China"};
或,static char c[ ]="China";
等价于,static char c[6]="China";
也等价于,static char c[6]={'C','h','i','n','a','\0'};
不能写成,static char c[5]="China";
问题,static char c[5]={'C','h','i','n','a'}; 是否合法?
注意:
①、字符数组并不要求它的最后一个字符为 '\0',甚至可以不包含 '\0'。
②、初始化时数组的长度应足够大,确保可以容纳所有字符和结束标志 '\0'。
2 字符串的初始化
⑴ 用 "%c"格式控制符实现逐个字符的输入出。
#include <stdio.h>
main( )
{
int i=0;
char str[20];
while ((str[i++]=getchar( ))!='\n') ;
str[i]='\0';
for (i=0; str[i]!= '\0'; i++)
printf("%c",str[i]);
}
3 字符串的输入与输出
⑵ 用 "%s"格式控制符对数组进行整体输入和出:
#include <stdio.h>
main( )
{
char str[20];
scanf("%s",str);
printf("%s\n",str);
}
注意:
(1)、从键盘输入字符串时不需加双引号 "";
(2)、用 scanf( ) 输入字符串时,空格和回车符都会作为字符串的分隔符,即 scanf( ) 的? %s”格式不能用来输入包含有空格的字符串。
(3)、在用 printf( )函数的? %s”格式输出字符串时,输出项是字符数组名,而不是数组元素名。
(4)、在用 scanf( )函数的? %s”格式输入字符串时,输入项是字符数组名,既不是数组元素,也不要数组名前加取地址符号 '&'。因为 C语言中数组名代表该数组的起始地址。
(5)、如果数组长度大于字符个数时,只输出到字符串结束标志符为止。
如,static char c[10]= "China";
printf(“%s”,c);
则输出仅为 "China",而不是 "China "。
⑶ 用 gets( )和 puts( )函数实现字符串的输入输出:
#include <stdio.h>
main( )
{ char str[20];
gets(str);
puts(str);
}
注意:要使用 gets和 puts函数,需要添加? #include
<stdio.h>” 。输入有空格的字符串时应使用 gets函数,它可以读入包括空格在内的全部字符直到遇到回车符为止;用 gets
输入字符串时,若输入字符数大于字符数组的长度,则多出的字符会存放在数组的合法存储空间之外。 puts函数一次输出一个字符串,输出时将 '\0'自动转换成换行符。
说明:
① 输出字符不包括结束符 '\0';
② 注意输入输出格式
int str[20],ch;
scanf("%s",str); scanf("%c",&ch);
printf("%s",str); printf("%c",str[0]);
③ 如果一个字符数组中包含多个 '\0',则遇第一个 '\0'时输出就结束 。
static char str[10]={'a','b','c','\0','d','e','\0'};
printf(“%s”,str);
输出结果,abc
字符串数组 就是数组中的每一个元素是一个字符串,因为存放字符串的本身是一个字符数组,所以字符串数组实际上是一个 二维字符数组 。该二维字符数组的第一维表示字符串的个数,第二维表示每个字符串的存储的长度。
例如,char str[5][10];
数组 str[5][10]可以存储 5个字符串,每个字符串可以存储
9个字符,注意,需要将最后一个存储空间用于存储字符串结束标志’ \0’。
由于二维数组元素在内存中是按行连续分配存储空间的,
因此字符串数组也是按字符串在内存中按行连续分配存储空间。即按字符串一个一个存储,先存储完第一个字符串再存储第二个字符串,直到所有的字符串都存储完。
4 字符串数组
(1)、字符串拷贝函数 strcpy( )
strcpy(字符数组 1,字符串 2)
功能:将字符串 2复制到字符数组 1中例,static char str1[10],str2[ ]="China";
strcpy(str1,str2);
str1 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0
str2 C h i n a \0
C h i n a
说明:
①、字符数组 1的长度不应小于字符串 2的长度。
②、字符数组 1”必须写成 数组名 形式或 字符型指针变量 。
字符串 2”可以是 字符数组名,字符型指针变量,或 字符串常量
。 strcpy(str1,str2); strcpy(str1,"China");
5 字符串处理函数
③,拷贝时连同 '\0' 一起拷贝 ;
例,char s1[5]="abc",s2[3],s3[8];
strcpy(s3,s1);
strcpy(s2,s1);
s1 a b c \0
s3 a b c \0
s2 a b c \0
④,不能用赋值语句 将一个字符串常量或字符数组直接赋给另一个字符数组。
static char str1[20],str2[ ]="computer";
str1="computer"; ×
str1=str2; ×
strcpy(str1,str2); 或,strcpy(str1,"computer");
函数,strncpy(字符数组 1,字符串 2,n )
作用,将字符串 2的前 n个字符复制到字符数组 1中例,char c1[10],c2[ ]="abcdef";
strncpy(c1,c2,3);
(2)、字符串连接函数 strcat( )
strcat(字符数组 1,字符数组 2)
功能:把字符串 2连接到字符串 1的后面,仍存放在字符数组 1
中例,static char str1[20]="China",str2[ ]="Bei jing";
strcat(str1,str2);
str1 C h i n a \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0
str2 B e i j i n g \0
B e i j i n g
上面的若改为,static char str1[ ]="China",可否?
说明:①、字符数组 1必须定义得足够大,以便容纳连接后的新字符串;②、连接前两个字符串后面都有一个 '\0',连接时将字符串 1后面的 '\0'去掉,只在新串最后保留一个 '\0'
(3)、字符串比较函数 strcmp( )
int strcmp(字符串 1,字符串 2)
功能:比较字符串 1和字符串 2(从左到右逐个字符比较
ASCII值的大小,直到出现的字符不一样或遇到 '\0'为止,比较结果由函数返回)
①、若字符串 1=字符串 2,函数的返回值为 0
②、若字符串 1>字符串 2,函数的返回值为一正整数
③、若字符串 1<字符串 2,函数的返回值为一负整数例,strcmp("China","China") strcmp("35+78","4")
strcmp(str,"China")
strcmp("computer","compare")
if (str1==str2)
printf("equal");×
if (strcmp(str1,str2)==0)
printf("equal");
(4)、测试字符串长度函数 strlen( )
int strlen(字符串 )
功能:测试字符串长度(函数的值为字符串的实际长度,不包括 '\0'在内)
static char str[20]="China";
printf("%d\n",strlen(str));
也可以直接测字符串常量的长度。 strlen("China")
(5)、大小写转换函数 strupr( ),strlwr( )
strupr(字符串 ) strlwr(字符串 )
功能,strupr( )函数将字符串中的小写字母转换为大写字母;
strlwr( )函数将字符串中的大写字母转换为小写字母。
例,strupr("abC")=="ABC" strlwr("abC")=="abc"
例 1:输入 5个字符串,输出其中最大者。
char max[20],str[5][20];
for (i=0; i<5; i++)
gets(str[i]);
strcpy(max,str[0]);
for (i= ; i<5; i++)1
if (strcmp(max,str[i])<0)
strcpy(max,str[i]);
printf("The largest string is,%s\n",max);
4.3.4 字符数组程序举例例 2:输入一行字符,统计单词个数,单词之间用空格分隔开判断方法,前一字符为空格 同时 当前字符不是空格 时,表示新单词开始,单词数应该加 1
num用来统计单词个数,word作为判别是否单词的标志;
若 word=0表示未出现单词,如出现单词 word就置成 1。
word=0;
for (i=0; (ch=str[i])!='\0'; i++)
{ if (ch==' ')
word=0;
if (ch!=' ' && word==0)
{ num++; word=1; }
}
例 3:编写一个程序,将字符数组 str2中的全部字符拷贝到字符数组 str1中。不要使用 strcpy函数。
注意,'\0'也要拷贝,'\0'后面的字符不拷贝。
char str1[30],str2[30];
gets(str2);
for (i=0; i<= ; i++)
str1[i]=str2[i];
puts(str1);
strlen(str2)
将 str2中的字符串连接到 str1后
for (i=0; i<=strlen(str2); i++)
str1[ ]=str2[i];i+strlen(str1)
例 4:已有一个排好序的数组,今输入一个数,要求按原来排序的规律将它插入数组中。
3 8 11 17 20 28 40 58 67 85
num=15 3 8 11 15 17 20 28 40 58 67 85
static int a[11]={3,8,11,17,20,28,40,58,67,85};
int num,i;
i=9;
while (num<a[i] )
{ a[i+1]=a[i];
i--;
}
a[ ]=num;
&& i>=0
i+1
4.4 数组与函数
4.4.1 数组元素作为函数参数
4.4.2 数组名作为函数参数
4.4.3 多维数组作为函数参数
4.4.1 数组元素作为函数参数数组元素是一个变量,可以作为函数的实参;它与形参之间是以? 值传递?的形式进行参数传递,即 单向传递 。
例:有两个数组 a,b,各有 10个元素,将它们对应地逐个相比
,分别统计出两个数组相应元素大于、等于、小于的次数
for (i=0; i<10; i++)
result=large(a[i],b[i]);
int large(int x,int y)
{ int flag;
if (x>y)
flag=1;
else if (x==y)
flag=0;
else flag=-1;
return(flag);
}
if (result==1)
m++;
else if (result==0)
n++;
else
k++;
{
}
数组作为函数参数,即用 数组名作为函数实参,要 求实参和形参都应为数组名 。此时 主调函数将实参数组的起始地址传递给被调函数的形参数组,而 不是传递数组元素的值 。
例:一维数组 score,内放 10个学生的成绩,求平均成绩
float sc[10];
aver=average(sc);
float average(float a[10])
{
}
int i;
float aver,sum=0.0;
for (i=0; i<10; i++)
sum+=a[i];
aver=sum/10.0;
return(aver);
sc[0]
sc[1]
sc[2]
sc[3]
sc[4]
sc[5]
sc[6]
sc[7]
sc[8]
sc[9]
sca a[0]
a[1]
a[2]
a[3]
a[4]
a[5]
a[6]
a[7]
a[8]
a[9]
4.4.2 数组名作为函数参数说明:
1、用数组名作函数参数,必须 在主调函数和被调函数中分别定义数组 ;且实参数组和形参数组 类型必须一致
float sc[10];
ave=average(sc); float average(float a[10])
2、形参数组和实参数组的大小可不一致;C语言编译时对形参数组大小不作检查,只将实参数组的首地址传给形参数组
3、形参数组可以不指定大小,仅给出 数组类型,数组名 和一对方括号,另设一个参数传递数组的实际长度
float average(float a[ ],int n)
{
……
}
aver=average(sc,10);
4、实参数组和形参数组占同一段内存单元,即表示同一个数组,因此 形参数组的变化等同于实参数组的变化例,用 选择法 对数组中 10个整数按由小到大排序。
void main( )
{ int x[10],i;
printf("\n\nInput array x:\n");
for (i=0;i<10;i++)
scanf("%d",&x[i]);
sort(x,10);
printf("\nThe sorted array:\n");
for (i=0;i<10;i++)
printf("%d ",x[i]);
}
void sort(int a[ ],int n)
{ int i,j,k,t;
for (i=0; i<n-1; i++)
{ k=i;
for (j=i+1;j<n;j++)
if (a[j]<a[k])
k=j;
t=a[k]; a[k]=a[i]; a[i]=t;
}
}
1、多维 数组元素 可以作为实参,形参需定义为 同类型的变量 ;
多维 数组名 也可以作实参,形参需定义为 多维数组
2、定义形参数组时可以指定每一维的大小,也可省略第一维的大小说明(但 不能省略第二维及其它高维的大小说明 )
int fun(int array[3][4]) 或,int fun(int array[ ][4])
但不能写成,int fun(int array[ ][ ])
也不能写成,int fun(int array[3][ ])
3、实参数组可以大于形参数组;
int score[5][10]; int array[3][10];
此时实参中只有前面数据起作用。
4.4.3 多维数组作为函数参数例,编写一个函数,求 3× 4矩阵中的最大元素并返回该值。
int max_value(int a[ ][4])
{
int i,j,max;
max=a[0][0];
for (i=0; i<3; i++)
for (j=0; j<4; j++)
if (a[i][j]>max)
max=a[i][j];
reutrn(max);
}
4.5 数组与指针
4.5.1 一维数组与指针
4.5.2 多维数组与指针
4.5.3 数组名作为函数参数
4.5.1 一维数组与指针一个变量有地址,一个数组包含若干个元素,每个数组元素都在内存中占存储单元,它们都有相应的地址。指针变量既然可以指向变量,当然也可以指向数组元素。所谓数组元素的指针就是数组元素的地址。
数组名代表数组在内存中的首地址,表示的是地址信息,因此数组名也可以理解为一个指针,不过数组名是一个指针常量,不能改变。可以用数组名来将数组的首地址赋给指针变量,找到了数组的首地址,就相当于找到了数组中的每一个元素。
例如,int a[10],*p;
p=a;? p=&a[0];
注意:数组名 a不代表整个数组,它代表数组中第一个元素的地址指向数组元素的指针变量的定义:
定义一个指向数组元素的指针变量的方法,与前面指向变量的指针变量相同。
1 指向数组元素的指针如果 p的初值为 &a[0],则:
如果指针变量 p已指向数组中的一个元素,则 p+1指向同一个数组中的下一个元素(不是简单的加 1)
(1),p+i和 a+i就是 a[i]的地址,或者说它们 指向 a数组的第 I
个元素
(2),*(p+i)?a[i]?p[i]
*(a+i)? a[i]
(3)、引用一个数组元素,可以用:
下标法,a[i]
指针法,*(a+i),*(p+i)
其中,a是数组名指针常量,p是指针变量
2 通过指针引用数组元素用多种方法访问一维数组各元素
#include <stdio.h>
main( )
{
int a[5]={1,3,5,7,9},i,*p=a;
for (i=0;i<5;i++) printf(“%d”,a[i]);
for (i=0;i<5;i++) printf(“%d”,*(a+i));
for (i=0;i<5;i++) printf(“%d”,p[i]);
for (i=0;i<5;i++) printf(“%d”,*(p+i));
for (;p<a+5;p++) printf(“%d”,*p);
p=a; while (p<a+5) printf(“%d”,*p++);
}
在使用指针变量时,要注意,
(1)、可以改变指针变量的值,但指针常量不可改变。
例如,p++合法,但 a++不合法
(2)、要注意指针变量的当前值。
(3)、在使用指针变量指向数组元素时,应切实保证指向数组中有效的元素。
(4)、注意指针变量的运算:
p++ 是 p指向下一个元素
*p++?*(p++)
*(p++)与 *(++p)不同
(*p)++使 p所指向的元素加 1
4.5.2 多维数组与指针数组是具有相同?数据类型?的数据的顺序集合,而数组本身也是C语言的一种数据类型,同样可以作为数组的元素类型。当一个一维数组的元素类型为数组时,便构成了多维数组。
熟记下面两组等价式:
x[i]? *(x+i) &x[i]?x+i
思考,a+1代表哪个元素的地址?
a[0],a[1],a[2]分别代表什么?
先回顾一下多维数组的性质。以二维数组为例设有数组定义为,int a[3][4]; 则有:
(1) a表示数组在内存中的首地址,也就是数组中第 1
个元素(也是第一行)的首地址,它是一个地址常量,其值由系统在编译时确定,程序运行期间不能改变。
(2) 该二维数组可以理解为:它是一个一维数组,含有 3个元素,每个元素又是一个一维数组,该一维数组含有 4个元素,每个元素是 int类型。
1 多维数组元素的地址二维数组的逻辑结构图如下:
a[0][0] a[0][1] a[0][2] a[0][3]
a[1][0] a[1][1] a[1][2] a[1][3]
a[2][0] a[2][1] a[2][2] a[2][3]
a[0]
a[1]
a[2]
a+0
a+2
a+1
a[i]? *(a+i) a[i][j]?*(*(a+i)+j)
不是 *( *( a+i+j))
该程序中 p是一个指向整形变量的指针变量把二维数组看作一维数组对待
main()
{
int a[3][4]={1,3,5,7,9,11,13,15,16,19,21,21};
int *p;
for(p=a[0];p<a[0]+12;p++)
{
if (p-a[0])%4==0) printf(“\n”);
printf(“%4d”,*p);
}
}
2 指向多维数组元素的指针变量指向由 m个元素组成的一维数组的指针变量的 定义,
类型名 ( *指针变量名) [长度 ];
例如,int (*pa)[4];
pa是指针变量,它指向一个数组,数组含有 4个元素,每个元素的类型是 int。
说明:
a,与定义 int *pa; 以及 int *pa[4]; 含义不同。
b,如果执行 pa++,则 pa实际增加了多少呢?
4.5.3 数组名作为函数参数用数组名作函数参数时,如果形参数组中各元素的值发生变化,实参数组元素的值也随之变化。这是为什么?
实参数组名代表该数组首元素的地址。而形参是用来接收从实参传 递过来的数组首元素的地址。因此,形参是一个指针变量。它的内容是实参数组首元素的地址。也就是说它指向实参数组的首地址。所以如果形参数组(其实是指针的下标表示法)中各元素的值发生变化,实参数组元素的值也随之变化。
注意:实参数组名是指针常量,形参数组名是指针变量。
函数的定义形式,void f(int arr[ ],int n)
也可写为,void f(int *arr,int n)
例如:将数组 a中的 n个数按相反顺序存放
void inv(int x[ ],int n)
{int temp,i,j,m=(n-1)/2;
for(i=0;i<=m;i++)
{j=n-1-i;
temp=x[i];x[i]=x[j];x[j]=temp;}
return;}
main()
{int i,a[10]={3,7,9,11,0,6,7,5,4,2}
printf(“the original array:\n”);
for(i=0;i<10;i++)
printf(“%d,”,a[i]);
inv(a,10);
for(i=0;i<10;i++)
printf(“%d,”,a[i]);
printf(“\n”);
}
3 7 9 11 0 6 7 5 4 22 34 7 35 97 0 6 11 9 7 36 0
a
x
&a[0]
子函数的变量
X[i]? *(x+i)
主函数的变量举例
void inv(int *x,int n)
{
int *p,*i,*j;
int temp,m=(n-1)/2;
i=x;j=x+n-1;p=x+m;
for(;i<=p;i++,j--)
{
temp=*i;
*i=*j;
*j=temp;
}
}
a[0]
a[1]
a[2]
a[3]
a[4]
a[5]
a[6]
a[7]
a[8]
a[9]
x
i
j
p
3
7
9
11
0
6
7
5
4
2
i
j
归纳起来:如果有一个实参数组,想在函数中改变此数组的元素的值,实参与形参的对应关系有以下四种:
( 1)、形参与实参都用数组名
( 2)、实参用数组名,形参用指针变量
( 3)、实参形参都用指针变量
( 4)、实参为指针变量,形参为数组名
4.6 字符串与指针
4.6.1 字符串的指针和指针变量
4.6.2 用字符指针访问字符串
4.6.3 字符指针和字符数组的区别
4.6.4 字符指针作为函数参数
4.6.1 字符串的指针和指针变量
main( )
{
char s[ ]="I love China!";
printf("%s\n",s);
}
S[0] S[13]
I l o v e c h i n a !\0
(1) 用字符数组存放一个字符串,然后输出字符串。
(2) 用字符指针指向一个字符串。
main()
{
char *s="I love China!";
printf("%s\n",s);
}
程序在定义字符指针变量 s时,把字符串首地址(即存放字符串的字符数组的首地址)赋给 s。
I l o v e c h i n a !\0S
例:将字符数组 a中的字符串复制到字符数组 b中。
①,下标法:
#include <stdio.h>
main( )
{ char a[ ]="Hello,World!";
char b[20];
int i;
for (i=0; a[i]!='\0'; i++)
b[i]=a[i];
b[i]='\0';
printf("%s\n",b);
}
②,指针法:
#include <stdio.h>
main( )
{ char a[ ]="Hello,World!",b[20];
char *pa=a,*pb=b;
for ( ; *pa!='\0'; pa++,pb++)
*pb=*pa;
*pb='\0';
printf("%s\n",b);
}
对字符串中字符的存取,可用下标方法,也可用指针方法。
4.6.2 用字符指针访问字符串
4.6.3 字符指针与字符数组的区别字符数组和字符指针变量也有区别,主要有以下几点:
①,字符数组由若干个元素组成,每个元素中存放着一个字符;而字符指针变量中存放的是地址,不是将字符串放到字符指针变量中;
②,赋值方式:对于字符数组只能对各个元素赋值。
char str[20];
str[20]="I love China!";
char *ps;
ps="I love China!";
③,对字符指针变量赋初值:
char *s="I love China!"; char *s;s="I love China!";
char str[ ]="I love China!";
char str[14];
strcpy(str,"I love China!");
④,如果定义了一个字符数组,在编译时为它分配内存单元,
它有确定的地址。而定义一个字符指针变量时,给指针变量分配内存单元,在其中可以放一个地址,也就是说,该指针变量指向一个字符型数据,但如果未对它赋予一个地址值,
则它并未具体指向一个确定的字符数据。例如:
char str[10];
scanf(“%s“,str);
char *a;
scanf(“%s“,a); 应改为:
char *a,str[10];
a=str;
scanf(“%s“,a);
先使 a有一个确定值,也就是使 a指向一个数组的开头,然后输入一个字符串,把它存放在以该地址开始的若干单元中。
⑤ 指针变量的值是可以改变的。
main( )
{char *a=“I love china!”;
a=a+7;
printf(“%s“,a);
}
⑥ 用指针变量指向一个格式字符串,可以用它代替 printf函数中的格式字符串。
char *format;
format=“a=%d,b=%f”;
printf(format,a,b);
printf(“a=%d,b=%f”,a,b);
只需改变指针变量 format所指向的字符串,就可以改变输入输出的格式。这种 printf函数称为可变格式输出函数。也可用字符数组实现。
4.6.4 字符指针作为函数参数将一个字符串从一个函数传递到另一个函数,可以用地址传递的方法,即用字符数组名作参数或用指向字符串的指针变量作参数。在被调用的函数中可以改变字符串的内容,
在主调函数中可以得到改变了的字符串。
例:用函数调用实现字符串的复制。
①,用字符数组作参数
main( )
{ char a[ ]="abcdefg";
char b[ ]="12345";
……
copy_string(a,b);
……
}
void copy_string(char to[ ],char from[ ])
{ int i=0;
while (from[i]!='\0')
{ to[i]=from[i]; i++; }
to[i]='\0';
}
在 main函数中也可以不定义字符数组,而用字符型指针变量。
main( )
{ char *a="abcdefg";
char *b="12345";
……
copy_string(a,b);
……
}
main( )
{char *a="abcdefg";
char *b="12345";
……
copy_string(a,b);
……}
void copy_string(char *to,char *from)
{
for ( ; *from!='\0'; from++,to++)
*to=*from;
*to='\0';
}
②,形参用字符指针变量可以对 copy_string函数作下列简化:
void copy_string(char *to,char *from)
{
while ( (*to=*from)!='\0' )
{ to++; from++; }
}
while (*to=*from)
void copy_string(char *to,char *from)
{
while ( (*to++=*from++)!='\0' )
}
while (*to++=*from++)
①,
②,
void copy_string(char *to,char *from)
{
while (*from!='\0' )
*to++=*from++;
}
while (*from)
③,
④,void copy_string(char *to,char *from)
{
for ( ; (*to++=*from++)!='\0'; );
}
⑤,void copy_string(char *to,char *from)
{
for ( ; (*to++=*from++) ; );
}
附录 本章常见错误
1、数组下标越界
static int a[10]={1,2,3,4,5,6,7,8,9,10};
for (i=1; i<=10; i++)
printf("%d",a[i]);
C语言规定定义时用 a[10],表示数组有 10个元素,而不是可以用的最大下标值为 10。数组只包括 a[0]到 a[9]10 个元素,
因此用 a[10]超出范围。
for (i=0; i<=9; i++)
printf("%d",a[i]);
2、数组整体赋值
int a[10];
static int b[10]={1,2,3,4,5,6};
a=b;
C语言不允许对数组进行整体操作,如果把数组 a 赋值给数组 b,需要用循环语句来实现。
for (i=0; i<10; i++)
a[i]=b[i];
3、误以为数组名代表数组中全部元素
void main( )
{
static int a[4]={1,2,3,4};
printf("%d%d%d%d\n",a);
}
企图用数组名代表全部元素。
C语言中,数组名代表数组首地址,不能通过数组名输出 4个整数。
printf("%d%d%d%d\n",a[0],a[1],a[2],a[3]);
或,for (i=0; i<=3; i++)
printf("%d",a[i]);
4、对非静态、非外部的数组进行初始化
float a[5]={1.1,2.2,3.3,4.4,5.5};
C语言规定,如果要对数组进行初始化,需要将其定义为静态存储( static)的数组或外部存储 (extern)的数组。
static float a[5]={1.1,2.2,3.3,4.4,5.5};
5、引用数组元素时使用圆括号
printf("%d\n",a(2));
6、输入字符串时用了地址运算符 '&'
char str[20];
scanf("%s",&str);
数组名本身就代表地址,不应再加地址符。
scanf("%s",str);
7、向一个字符数组赋字符串
char str[20];
str="I am a boy.";
这种错误和第二种错误为一种错误,即不支持对数组的整体操作。
strcpy(str,"I am a bpy.");
8、构造字符串时忘记在末尾应加 '\0'
i=0;
while ((ch=getchar( ))!='\n')
str[i++]=ch;
printf("%s\n",str);
由于构造的字符串没有加结束标志,当用 printf函数输出
str时,从 str的起始地址开始一个个的输出,输出完读入的字符后,没有遇到 '\0',继续输出,这时的内容已不再是字符串中的字符。因此,在自己构造一个字符串时,一定不要忘记在末尾加上 '\0'。 i=0;
while ((ch=getchar( ))!='\0')
str[i++]=ch;
str[i]='\0';
printf("%s\n",str);
本章作业
1、输入 10个无序的整数,存放在数组中,找出其中最小数所在的位置。
2、输入一个 4× 4矩阵,并求两条对角线元素之和及四周元素之和。
选作:随机产生 0到 50之间的 15个整数,存在一个 3× 5的二维数组中,求出最小的元素的值及其所在的行号和列号。
T h e E n d