800 likes | 937 Views
10.3 数组与指针. 一个变量有地址,一个数组包含若干元素,每个数组元素都在内存中占用存储单元,它们都有相应的地址。指针变量既然可以指向变量,当然也可以指向数组元素(把某一元素的地址放到一个指针变量中)。. 所谓 数组元素的指针就是数组元素的地址 。. 10.3.1 指向数组元素的指针. 定义一个指向数组元素的指针变量的方法,与以前介绍的指向变量的指针变量相同。 例如: int a[10] ; // 定义a为包含 10 个整型数据的数组 int* p; // 定义p为指向整型变量的指针变量. 对该指针变量赋值: p=&a[0] ; p=a;
E N D
10.3 数组与指针 一个变量有地址,一个数组包含若干元素,每个数组元素都在内存中占用存储单元,它们都有相应的地址。指针变量既然可以指向变量,当然也可以指向数组元素(把某一元素的地址放到一个指针变量中)。 所谓数组元素的指针就是数组元素的地址。
10.3.1 指向数组元素的指针 定义一个指向数组元素的指针变量的方法,与以前介绍的指向变量的指针变量相同。 例如:int a[10];//定义a为包含10个整型数据的数组 int*p; //定义p为指向整型变量的指针变量 对该指针变量赋值: p=&a[0]; p=a; 把a[0]元素的地址赋给指针变量p。也就是使p指向a数组的第0号元素,如图:
10.3.2通过指针引用数组元素 引用一个数组元素,可以用: (1) 下标法,如a[i]形式; (2) 指针法,如*(a+i)或*(p+i)。 其中a是数组名,p是指向数组元素的指针变量,其初值p=a。 p+i a+i &a[i] *(p+i) *(a+i) a[i] p[i] 即以下几个语句等效: a[i]=10; *(p+i)=10; *(a+i)=10; p[i]=10;
假设有一个a数组,整型,有10个元素。要输出各元素的值有三种方法: 例10.5 输出数组中的全部元素。 (1)下标法。 (2) 通过数组名计算数组元素地址,找出元素的值。 (3)用指针变量指向数组元素。 可否用多种方法读入数组元素值?如何实现?
从键盘输入10个数到数组a: int a[10],i,*p=a,s=0; for(i=0;i<10;i++) scanf(“%d”,&a[i]); for(i=0;i<10;i++) scanf(“%d”,a+i); for(i=0;i<10;i++) scanf(“%d”,p+i);
累加求和的各种用法: for(i=0;i<10;i++)s+=a[i]; for(i=0;i<10;i++)s+=*(a+i); for(i=0;i<10;i++)s+=*(p+i); for(i=0;i<10;i++)s+=p[i]; for(i=0;i<10;i++)s+=*p++; 等效于*(p++) for(p=a;p<a+10;p++)s+=*p;注意不能使用a++ 后两种用法效率高。
例10.6 通过指针变量输出a数组的10个元素。 #include <stdio.h> void main() {int*p,i,a[10]; p=a; for(i=0;i<10;i++) scanf(″%d″,p++); printf(″\n″); p=a;//指针重新指向数组的第一个元素 for(i=0;i<10;i++,p++ ) printf(″%d″,*p); }
10.3.3 用数组名作函数参数 在第8章8.7节中介绍过可以用数组名作函数的参数 如:void main() { if(int arr[ ],int n); int array[10]; … f(array,10); … } void f(int arr[ ],int n) { … }
例10.7 将数组a中n个整数按相反顺序存放。
#include <stdio.h> void main() { void inv(int x[ ],int n); 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]); printf(″\n″); inv(a,10); printf(″The array has been in verted:\n″); for(i=0;i<10;i++) printf (″%d,″,a[i]); printf (″\n″); }
A void inv(int x[ ],int n) /*形参x是数组名*/ {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; } 运行情况如下: The original array: 3,7,9,11,0,6,7,5,4,2 The array has been inverted: 2,4,5,7,6,0,11,9,7,3
对刚才的程序可以作一些改动。将函数inv中的形参x改成指针变量。对刚才的程序可以作一些改动。将函数inv中的形参x改成指针变量。 B void inv(int *x,int n) /*形参x为指针变量*/ {int*p,temp,*i,*j,m=(n-1)/2; i=x;j=x+n-1;p=x+m; for(;i<=p;i++,j--) {temp=*i;*i=*j;*j=temp;} return; } int temp,*i,*j; i=x;j=x+n-1; for(;i<j;i++,j--) {temp=*i;*i=*j;*j=temp;} return; 实际上,C编译都是将形参数组名作为指针变量来处理的。以上两种(A、B)形参表写法本质是等价的。
如果有一个实参数组,想在函数中改变此数组中的元素的值,实参与形参的对应关系有以下4种情况: (1) 形参和实参都用数组名,如: void main() void f(int x[],int n) { int a[10]; { … … f(a,10); } }
(2) 实参用数组名,形参用指针变量。如: void main() void f(int *x,int n) {int a[10]; { … … f(a,10); } } (3)实参形参都用指针变量。例如: void main() void f(int *x,int n) {int a[10] , *p=a; { … … f(p,10); } }
(4) 实参为指针变量,形参为数组名。如: void main() void f(int x[ ],int n) {int a[10] ,*p=a; { … … f (p,10); } } 如果用指针变量作实参,必须先使执针变量有确定值,指向已定义的单元。p240 以上四种方法,实质都是地址传递。
例10.9 用选择法对10个整数按由大到小顺序排序。 #include <stdio.h> void main() { void sort(int x[ ],int n); int*p,i,a[10]; p=a; for(i=0;i<10;i++) scanf(″%d″,p++); p=a; sort(p,10); for(p=a,i=0;i<10;i++) {printf(″%d″,*p);p++;} }
void sort(int x[ ],int n) { int i,j,k,t; for(i=0;i<n-1;i++) {k=i; for(j=i+1;j<n;j++) if(x[j]>x[k]) k=j; if(k!=i) {t=x[i]; x[i]=x[k]; x[k]=t;} } } 分段排序? main() { int a[10],j; for(j=0;j<10;j++)scanf(“%d”,a+j); sort(a,5); sort(a+5,5); for(j=0;j<10;j++)printf(“%5d”,a[j]); }
10.3.4 多维数组与指针 用指针变量可以指向一维数组中的元素,也可以指向多维数组中的元素。从本质上说,多维数组的指针与一维数组的指针相同,但在概念上和使用上,多维数组的指针比一维数组的指针要复杂一些。 1. 多维数组元素的地址 例 : 定义int a[3][4]={{1,3,5,7},{9,11,13,15},{17,19,21,23}}; 则二维数组a是由3个一维数组所组成的。设二维数组的首行的首地址为2000 ,则
例10.10 输出二维数组有关的值 #include <stdio.h> #define FROMAT″%d,%d\n″ void main() { int a[3][4]={1,3,5,7,9,11,13, 15,17,19,21,23}; printf(FORMAT,a,*a); printf(FORMAT,a[0] , *(a+0)); printf(FORMAT,&a[0],&a[0][0]); printf(FORMAT,a[1],a+1); printf(FORMAT,&a[1][0],*(a+1)+0); printf(FORMAT,a[2],*(a+2)); printf(FORMAT,&a[2],a+2); printf(FORMAT,a[1][0],*(*(a+ 1)+0)); }
某一次运行结果如下: 158,158 (0行首地址和0行0列元素地址) 158,158 (0行0列元素地址) 158,158 (0行0首地址和0行0列元素地址) 166,166 (1行0列元素地址和1行首地址) 166,166 (1行0列元素地址) 174,174 (2行0列元素地址) 174,174 (2行首地址) 9,9 (1行0列元素的值)
例:求数组a的所有元素之和,可有多种用法: for(i=0;i<3;i++) for(i=0;i<3;i++) for(j=0;j<4;j++) for(j=0;j<4;j++) s+=a[i][j]; s+=*(a[i]+j); for(i=0;i<3;i++) for(j=0;j<4;j++) s+=*(*(a+i)+j);
2 . 指向多维数组元素的指针变量 (1) 指向数组元素的指针变量 例10.11 用指针变量输出二维数组元素的值 #include <stdio.h> void main() { int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23}; int*p; for(p=a[0];p<a[0]+12;p++) {if((p-a[0])%4==0) printf(″\n″); printf(″%4d″,*p); } } 运行结果如下: 1 3 5 7 9 11 13 15 19 21 23
(2) 指向由m个元素组成的一维数组的指针变量 例10.13 输出二维数组任一行任一列元素的值 #include <stdio.h> void main ( ) { int a[3][4]={1,3,5,7,9,11, 13,15,17,19,21,23}; int (*p)[4],i,j; p=a; scanf(″ i=%d,j=%d″,&i,&j); printf(″a[%d,%d]=%d\n″,i, j,*(*(p+i)+j)); } 运行情况如下: i=1,j=2↙(本行为键盘输入) a[1,2]=13
3. 用指向数组的指针作函数参数 例10.13 有一个班,3个学生,各学4门课,计算总平均分数以及第n个学生的成绩。这个题目是很简单的。只是为了说明用指向数组的指针作函数参数而举的例子。用函数average求总平均成绩,用函数search找出并输出第i个学生的成绩。
思考两个形参p的区别,各自的应用场合及其优缺点???思考两个形参p的区别,各自的应用场合及其优缺点??? #include <stdio.h> void main() { void average(float *p,int n); void search(float (*p)[4],int n); float score[3][4]={{65,67,70,60},{80, 87,90,81},{90,99,100,98}}; average(*score,12);/*求12个分数的平均分*/ search(score,2);/*求序号为2的学生的成绩*/ } 必须为常量
void average(float *p,int n) { float*p_end; float sum=0,aver; p_end=p+n-1; for(;p<=p_end;p++) sum=sum+(*p); aver=sum/n; printf(″average=%5.2f\n″,aver); }
void search(float (*p)[4],int n) / * p是指向具有4个元素的一维数组的指针 */{int i; printf(″the score of No. %d are:\n″,n); for(i=0;i<4;i++) printf(″%5.2f ″,*(*(p+n)+i)); } p[n][i] 程序运行结果如下: average=82.25 The score of No.2 are: 90.00 99.00 100.00 98.00
例10.14 在上题基础上,查找有一门以上课程不及格的学生,打印出他们的全部课程的成绩。 #include <stdio.h> void main() {void search(float (*p)[4],int n);/*函数声明*/ float score[3][4]={{65,57,70,60},{58,87, 90,81},{90,99,100,98}}; search(score,3); } 注意形参与实参类型的一致性
程序运行结果如下: No.1 fails, his scores are: 65.0 57.0 70.0 60.0 No.2 fails, his scores are: 58.0 87.0 90.0 81.0 void search(float (*p)[4],int n) {int i,j,flag; for(j=0;j<n;j++) {flag=0; for(i=0;i<4;i++) if(*(*(p+j)+i)<60)flag=1; if(flag==1) { printf("No.%d fails,his scores are:\n",j+1); for(i=0;i<4;i++) printf(″%5.1f″,*(*(p+j)+i) ); printf(″\n″); } } } p[j][i]
10.4 字符串与指针 10.4.1字符串的表示形式 (1) 用字符数组存放一个字符串。例 10.15 (2) 用字符指针指向一个字符串。例10.16 #include <stdio.h> void main() {char * string=″I love China!″; printf(″%s\n″,string); printf( ″%c\n ″,*(string+3)); }
例10.17 将字符串a复制为字符串b。 也可以设指针变量,用它的值的改变来指向字符串中的不同的字符。 例10.18 用指针变量来处理例10.17问题。 #include <stdio.h> void main() {char a[ ] =″I am a boy. ″,b[20],*p1,*p2; int i; p1=a;p2=b; for(;*p1!=′\0′;p1++,p2++)
*p2=*p1; *p2=′\0′; printf(″string a is:%s\n″,a); printf(″string b is:″); for(i=0;b[i]!=′\0′;i++) printf(″%c″,b[i]); printf(″\n″); } 程序必须保证使p1和p2同步移动
10.4.2 字符指针作函数参数 (1) 用字符数组作参数 例10.19 用函数调用实现字符串的复制 #include <stdio.h> void main() { void copy_string(char from[ ], char to[ ]); char a[ ]=″I am a teacher.″; char b[ ]=″you are a student.″; printf(“string a=%s\n string b=%s\n″,a,b); printf(“copy string a to string b:\n ”); copy_string (a,b); printf("\nstring a=%s\nstring b=%s\n",a,b); }
void copy_string(char from[ ], char to[ ]) { int i=0; while(from[i]!=′\0′) {to[i]=from[i];i++;} to[i]=′\0′; } 程序运行结果如下: string a=I am a teacher. string b=you are a student. copy string a to string b: string a =I am a teacher. stringb=I am a teacher.
(2) 形参用字符指针变量 #include <stdio.h> void main() { void copy_string(char * from, char *to); char *a=″I am a teacher .″; char *b=″you are a student .″; printf("string a=%s\nstring b=%s\n″,a,b); printf("copy string a to string b:\n "); copy_string(a,b); printf("\nstring a=%s\nstring b=%s\n" ,a ,b); }
void copy_string(char *from,char *to) { for(;*from!=′\0′;from++,to++) *to=*from; *to=′\0′; } (3) 对copy_string函数还可作简化 将copy_string函数改写为 void copy_string (char *from,char *to) {while((*to=*from)!=′\0′) {to++;from++;} }
• copy_string函数的函数体还可改为 { while((*to++=*from++)!=′\0′); } •copy_string函数的函数体还可写成 { while(*from!=′\0′) *to++=*from++; *to=′\0′; }
•上面的while语句还可以进一步简化为下面的while语句:•上面的while语句还可以进一步简化为下面的while语句: while(*to++=*from++); 它与下面语句等价: while((*to++=*from++)!=′\0′); 将*from赋给*to,如果赋值后的*to值等于′\0′则循环终止(′\0′已赋给*to) •函数体中while语句也可以改用for语句: for(;(*to++=*from++)!=0;); 或 for(;*to++=*from++;);
•也可用指针变量,函数copy_string可写为 void copy_string (char from[ ],char to[ ]) {char*p1,*p2; p1=from;p2=to; while((*p2++=*p1++)!=′\0′); } •甚至,函数copy_string可写为 void copy_string (char from[ ],char to[ ]) {while((*to++=*from++)!=′\0′); }
10.4.3 对使用字符指针变量和字符数组的讨论 字符数组和字符指针变量二者之间的区别: (1) 字符数组由若干个元素组成,每个元素中放一个字符,而字符指针变量中存放的是地址(字符串第1个字符的地址),决不是将字符串放到字符指针变量中。 (2)赋值方式。对字符数组只能对各个元素赋值,不 能用以下办法对字符数组赋值。 char str[14]; str=″I love China!″; 而对字符指针变量,可以采用下面方法赋值: char*a; a=″I love China!″;
(3)对字符指针变量赋初值: char *a=″I love China!″;等价于 char*a; a=″I love Chian!″; 而对数组的初始化: char str[14]={″I love China!″}; 不能等价于 char str[14]; str[ ]=″I love China!″;
(4) 定义了一个字符数组,在编译时为它分配内存单元,它有确定的地址。而定义一个字符指针变量时,给指针变量分配内存单元,在其中可以放一个字符变量的地址。 例如:char str[10]; scanf(″%s″,str); 没有特定的指向 例如:char *a; scanf(″%s″,a); ×
(5) 指针变量的值是可以改变的,例如: 例10.20 改变指针变量的值 #include <stdio.h> void main() {char*a=″I love China!″; a=a+7; printf(″%s″,a); }
若定义了一个指针变量,并使它指向一个字符串,就可以用下标形式引用指针变量所指的字符串中的字符。例10.21: (6)用指针变量指向一个格式字符串,可以用它替代printf中的格式字符串。
内存空间的动态分配 在程序设计中,对于要处理的批量数据,我们往往是选用数组作为存放这些数据的数据结构,然而,数组有一个明显的缺点,就是在定义数组时,其长度必须是常值,无法根据需要动态地定义。这样,在很多情况下,不是定义的数组长度不够,就是定义太长以至于浪费。 采用动态分配可以克服这一缺点,并且可以随时释放。
动态分配内存空间步骤: ①定义一指针变量。 ②申请一片内存空间,并将其首地址赋给指针变量。此时便可通过指针变量访问这片内存。 ③用完后释放这片内存空间。 int *p; p=malloc(byte); …… free(p); //malloc函数的原形在stdio.h中。 //void * malloc(unsigned size);
例:对n个学生的分数排序后输出。 #include “stdio.h” void sort(int *a, int n) { ……} main() { int *a,j,n; scanf(“%d”,&n); a=malloc(n*sizeof(int)); if(!a) exit(0); for(j=0;j<n;j++) scanf(“%d”,a+j); sort(a,n); for(j=0;j<n;j++) printf(“%5d”,a[j]); free(a); }