分享
 
 
 

指针与数组

王朝other·作者佚名  2008-06-01
窄屏简体版  字體: |||超大  

变量在内存存放是有地址的,数组在内存存放也同样具有地址。对数组来说,数组名就是数组在内存安放的首地址。指针变量是用于存放变量的地址,可以指向变量,当然也可存放数组的首址或数组元素的地址,这就是说,指针变量可以指向数组或数组元素,对数组而言,数组和数组元素的引用,也同样可以使用指针变量。下面就分别介绍指针与不同类型的数组。

6.4.1指针与一维数组

假设我们定义一个一维数组,该数组在内存会有系统分配的一个存储空间,其数组的名字就是数组在内存的首地址。若再定义一个指针变量,并将数组的首址传给指针变量,则该指针就指向了这个一维数组。我们说数组名是数组的首地址,也就是数组的指针。而定义的指针变量就是指向该数组的指针变量。对一维数组的引用,既可以用传统的数组元素的下标法,也可使用指针的表示方法。

inta[10],*ptr;/*定义数组与指针变量*/

做赋值操作:ptr=a;或ptr=&a[0];

则ptr就得到了数组的首址。其中,a是数组的首地址,&a[0]是数组元素a[0]的地址,由于a[0]的地址就是数组的首地址,所以,两条赋值操作效果完全相同。指针变量ptr就是指向数组a的指针变量。

若ptr指向了一维数组,现在看一下C规定指针对数组的表示方法:

1)ptr+n与a+n表示数组元素a[n]的地址,即&a[n]。对整个a数组来说,共有10个元素,n的取值为0~9,则数组元素的地址就可以表示为ptr+0~ptr+9或a+0~a+9,与&a[0]~&a[9]保持一致。

2)知道了数组元素的地址表示方法,*(ptr+n)和*(a+n)就表示为数组的各元素即等效于a[n]。

3)指向数组的指针变量也可用数组的下标形式表示为ptr[n],其效果相当于*(ptr+n)。

[例6-5]/*以下标法输入输出数组各元素。

下面从键盘输入10个数,以数组的不同引用形式输出数组各元素的值。

#include<stdio.h>

main()

{

intn,a[10],*ptr=a;

for(n=0;n<=9;n++)

scanf("%d",&a[n]);

PRintf("1------output!\n");

for(n=0;n<=9;n++)

printf("%4d",a[n]);

printf("\n");

}

运行程序:

RUN

1234567890¿

1------output!

1234567890

[例6-6]采用指针变量表示的地址法输入输出数组各元素。

#include<stdio.h>

main()

{

int n,a[10],*ptr=a;/*定义时对指针变量初始化*/

for(n=0;n<=9;n++)

scanf("%d",ptr+n);

print f("2------output!\n");

for(n=0;n<=9;n++)

print f("%4d",*(ptr+n));

print f("\n");

}

运行程序:

RUN

1234567890¿

2------output!

1234567890

[例6-7]采用数组名表示的地址法输入输出数组各元素。

main()

{

int n,a[10],*ptr=a;

for(n=0;n<=9;n++)

scanf("%d",a+n);

print f("3------output!\n");

for(n=0;n<=9;n++)

print f("%4d",*(a+n));

print f("\n");

}

运行程序:

RUN

1234567890¿

3------output!

1234567890

[例6-8]用指针表示的下标法输入输出数组各元素。

main()

{

int n,a[10],*ptr=a;

for(n=0;n<=9;n++)

scanf("%d",&ptr[n]);

print f("4------output!\n");

for(n=0;n<=9;n++)

print f("%4d",ptr[n]);

print f("\n");

}

运行程序:

RUN

1234567890

4----output!

1234567890

[例6-9]利用指针法输入输出数组各元素。

main()

{

int n,a[10],*ptr=a;

for(n=0;n<=9;n++)

scanf("%d",ptr++);

print f("5------output!\n");

ptr=a;/*指针变量重新指向数组首址*/

for(n=0;n<=9;n++)

print f("%4d",*ptr++);

print f("\n");

}

运行程序:

RUN

1234567890¿

5-----output!

1234567890

在程序中要注重*ptr++所表示的含义。*ptr表示指针所指向的变量;ptr++表示指针所指向的变量地址加1个变量所占字节数,具体地说,若指向整型变量,则指针值加2,若指向实型,则加4,依此类推。而print f(“%4d”,*ptr++)中,*ptr++所起作用为先输出指针指向的变量的值,然后指针变量加1。循环结束后,指针变量指向如图6-6所示:

指针变量的值在循环结束后,指向数组的尾部的后面。假设元素a[9]的地址为1000,整型占2字节,则ptr的值就为1002。请思考下面的程序段:

main()

{

int n,a[10],*ptr=a;

for(n=0;n<=9;n++)

scanf("%d",ptr++);

print f("4------output!\n");

for(n=0;n<=9;n++)

print f("%4d",*ptr++);

print f("\n");

}

程序与例6-9相比,只少了赋值语句ptr=a;程序的运行结果还相同吗?

6.4.2指针与二维数组

定义一个二维数组:

inta[3][4];

表示二维数组有三行四列共12个元素,在内存中按行存放,存放形式为图6-7:

其中a是二维数组的首地址,&a[0][0]既可以看作数组0行0列的首地址,同样还可以看作是二维数组的首地址,a[0]是第0行的首地址,当然也是数组的首地址。同理a[n]就是第n行的首址;&a[n][m]就是数组元素a[n][m]的地址。

既然二维数组每行的首地址都可以用a[n]来表示,我们就可以把二维数组看成是由n行一维数组构成,将每行的首地址传递给指针变量,行中的其余元素均可以由指针来表示。下面的图6-8给出了指针与二维数组的关系:

我们定义的二维数组其元素类型为整型,每个元素在内存占两个字节,若假定二维数组从1000单元开始存放,则以按行存放的原则,数组元素在内存的存放地址为1000~1022。

用地址法来表示数组各元素的地址。对元素a[1][2],&a[1][2]是其地址,a[1]+2也是其地址。分析a[1]+1与a[1]+2的地址关系,它们地址的差并非整数1,而是一个数组元素的所占位置2,原因是每个数组元素占两个字节。

对0行首地址与1行首地址a与a+1来说,地址的差同样也并非整数1,是一行,四个元素占的字节数8。

由于数组元素在内存的连续存放。给指向整型变量的指针传递数组的首地址,则该指针指向二维数组。

int *ptr,a[3][4];

若赋值:ptr=a;则用ptr++就能访问数组的各元素。

[例6-10]用地址法输入输出二维数组各元素。

#include<stdio.h>

main()

{

int a[3][4];

int i,j;

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++)

printf("%4d",*(a[i]+j));/**(a[i]+是j地)址法所表示的数组元素*/

printf("\n");

}

}

运行程序:

RUN

1 2 3 4 5 6 7 8 9 10 11 12

1 2 3 4

5 6 7 8

9 10 11 12

[例6-11]用指针法输入输出二维数组各元素。

#include<stdio.h>

main()

{

int a[3][4],*ptr;

int i,j;

ptr=a[0];

for(i=0;i<3;i++)

for(j=0;j<4;j++)

scanf("%d",ptr++);/*指针的表示方法*/

ptr=a[0];

for(i=0;i<3;i++)

{

for(j=0;j<4;j++)

printf("%4d",*ptr++);

printf("\n");

}

}

运行程序:

RUN

1 2 3 4 5 6 7 8 9 10 11 12

1 2 3 4

6.4.3 数组指针作函数的参数

学习了指向一维和二维数组指针变量的定义和正确引用后,我们现在学习用指针变量作

函数的参数。

[例6-12] 调用子程序,实现求解一维数组中的最大元素。

我们首先假设一维数组中下标为0的元素是最大和用指针变量指向该元素。后续元素与该

元素一一比较,若找到更大的元素,就替换。子程序的形式参数为一维数组,实际参数是指

向一维数组的指针。

# include <stdio.h>

m a i n ( )

{

int sub_max(); / * 函数声明* /

int n,a[10],*ptr=a; / *定义变量,并使指针指向数组* /

int max;

f o r ( n = 0 ; n < = i - 1 ; n + + ) / *输入数据* /

s c a n f ( " % d " , & a [ n ] ) ;

m a x = s u b _ m a x ( p t r , 1 0 ) ; / * 函数调用,其实参是指针* /

p r i n t f ( " m a x = % d \ n " , m a x ) ;

}

int sub_max(b,i) / * 函数定义,其形参为数组* /

int b[],i;

{

int temp,j;

t e m p = b [ 0 ] ;

f o r ( j = 1 ; j < = 9 ; j + + )

if(temp<b[j]) temp=b[j];

return temp;

}

程序的m a i n ( )函数部分,定义数组a 共有1 0个元素,由于将其首地址传给了p t r,则指针

变量ptr 就指向了数组,调用子程序,再将此地址传递给子程序的形式参数b,这样一来,b

数组在内存与a 数组具有相同地址,即在内存完全重合。在子程序中对数组b 的操作,与操

作数组a 意义相同。其内存中虚实结合的示意如图6 - 9所示。

m a i n ( )函数完成数据的输入,调用子程序并输出运行结果。s u b _ m a x ( )函数完成对数组元

素找最大的过程。在子程序内数组元素的表示采用下标法。运行程序:

R U N

1 3 5 7 9 2 4 6 8 0

m a x = 9

[例6-13] 上述程序也可采用指针变量作子程序的形式参数。

# include <stdio.h>

m a i n ( )

{

int sub_max();

int n,a[10],*ptr=a;

int max;

f o r ( n = 0 ; n < = 9 ; n + + )

s c a n f ( " % d " , & a [ n ] ) ;

m a x = s u b _ m a x ( p t r , 1 0 ) ;

p r i n t f ( " m a x = % d \ n " , m a x ) ;

}

int sub_max(b,i) / *形式参数为指针变量* /

int *b,i;

{

int temp,j;

t e m p = b [ 0 ] ; / *数组元素指针的下标法表示* /

f o r ( j = 1 ; j < = i - 1 ; j + + )

if(temp<b[j]) temp=b[j];

return temp;

}

在子程序中,形式参数是指针,调用程序的实际参数p t r为指向一维数组a的指针,虚实结

合,子程序的形式参数b得到p t r的值,指向了内存的一维数组。数组元素采用下标法表示,即

一维数组的头指针为b,数组元素可以用b [ j ]表示。其内存中虚实参数的结合如图6 - 1 0所示。

运行程序:

R U N

1 3 5 7 9 2 4 6 8 0¿

m a x = 9

[例6-14] 上述程序的子程序中,数组元素还可以用指针表示。

# include <stdio.h>

m a i n ( )

{

int sub_max();

int n,a[10],*ptr=a;

int max;

f o r ( n = 0 ; n < = 9 ; n + + )

s c a n f ( " % d " , & a [ n ] ) ;

m a x = s u b _ m a x ( p t r , 1 0 ) ;

p r i n t f ( " m a x = % d \ n " , m a x ) ;

}

int sub_max(b,i)/ *子程序定义* /

int *b,i;

{

int temp,j;

t e m p = * b + + ;

f o r ( j = 1 ; j < = i - 1 ; j + + )

if(temp<*b) temp=*b++;

return temp;

}

在程序中,赋值语句t e m p = * b + +;可以分解为:t e m p = * b;b + +;两句,先作t e m p = * b;后

作b + +;程序的运行结果与上述完全相同。

对上面的程序作修改,在子程序中不仅找最大元素,同时还要将元素的下标记录下来。

# include <stdio.h>

m a i n ( )

{

int *max();/* 函数声明* /

int n,a[10],*s,i;

f o r ( i = 0 ; i < 1 0 ; i + + ) / * 输入数据* /

scanf("%d",a+i);

s = m a x ( a , 1 0 ) ; / *函数调用* /

p r i n t f ( " m a x = % d , i n d e x = % d \ n " , * s , s - a ) ;

}

int *max(a,n) / *定义返回指针的函数* /

int *a,n;

{

int *p,*t; / * p 用于跟踪数组,t用于记录最大值元素的地址* /

f o r ( p = a , t = a ; p - a < n ; p + + )

if(*p>*t) t=p;

return t;

}

在m a x()函数中,用p - a < n来控制循环结束, a是数组首地址, p用于跟踪数组元素的地址,p - a正好是所跟踪元素相对数组头的距离,或者说是所跟踪元素相对数组头的元素个数,所以在m a i n ( )中,最大元素的下标就是该元素的地址与数组头的差,即s - a。运行程序:

R U N

1 3 5 7 9 2 4 6 8 0¿

m a x = 9 , i n d e x = 4

[例6-15] 用指向数组的指针变量实现一维数组的由小到大的冒泡排序。编写三个函数用于输入数据、数据排序、数据输出。

在第5章的例题中,我们介绍过选择法排序及算法,此例再介绍冒泡排序算法。为了将一组n个无序的数整理成由小到大的顺序,将其放入一维数组a [ 0 ]、a [ 1 ]. . .a [ n - 1 ]。冒泡算法如下:

(开序)

① 相邻的数组元素依次进行两两比较,即a [ 0 ]与a [ 1 ]比、a [ 1 ]与a [ 2 ]比. . . a [ n - 2 ]与a [ n - 1 ]比,通过交换保证数组的相邻两个元素前者小,后者大。此次完全的两两比较,能免实现a [ n - 1 ]成为数组中最大。

② 余下n - 1个元素,按照上述原则进行完全两两比较,使a [ n - 2 ]成为余下n - 1个元素中最大。

③ 进行共计n - 1趟完全的两两比较,使全部数据整理有序。

下面给出一趟排序的处理过程:

4个元素进行3次两两比较,得到一个最大元素。若相邻元素表示为a [ j ]和a [ j + 1 ],用指针

变量P指向数组,则相邻元素表示为* ( P + j )和* ( P + j + 1 )程序实现如下:

# include<stdio.h>

#define N 10

m a i n ( )

{

void input(); / *函数声明* /

void sort();

void output();

int a[N],*p; / *定义一维数组和指针变量* /

i n p u t ( a , N ) ; / *数据输入函数调用,实参a是数组名* /

p = a ; / *指针变量指向数组的首地址* /

s o r t ( p , N ) ; / *排序,实参p是指针变量* /

o u t p u t ( p , N ) ; / *输出,实参p是指针变量* /

}

void input(arr,n) / *无需返回值的输入数据函数定义,形参a r r 是数组* /

int arr[],n;

{

int i;

printf("input data:\n");

for ( i = 0 ; i < n ; i + + ) / *采用传统的下标法*/

s c a n f ( " % d " , & a r r [ i ] ) ;

}

void sort(ptr,n) / *冒泡排序,形参ptr 是指针变量* /

int *ptr,n;

{

int i,j,t;

for ( i = 0 ; i < n - 1 ; i + + )

for ( j = 0 ; j < n - 1 - i ; j + + )

if (*(ptr+j)>*(ptr+j+1))/相*临两个元素进行比较*/

{

t = * ( ptr + j ) ; / *两个元素进行交换* /

* ( ptr + j ) = * ( ptr + j + 1 ) ;

* ( ptr + j + 1 ) = t ;

}

}

void output(arr,n) / *数据输出* /

int arr[],n;

{

int i,*ptr=arr; / *利用指针指向数组的首地址* /

printf("output data:\n");

for ( ; ptr - a r r < n ; ptr + + ) / *输出数组的n个元素* /

printf ( " % 4 d " , * ptr ) ;

printf ( " \ n " ) ;

}

由于C程序的函数调用是采用传值调用,即实际参数与形式参数相结合时,实参将值传给形式参数,所以当我们利用函数来处理数组时,假如需要对数组在子程序中修改,只能传递数组的地址,进行传地址的调用,在内存相同的地址区间进行数据的修改。在实际的应用中,

假如需要利用子程序对数组进行处理,函数的调用利用指向数组(一维或多维)的指针作参数,无论是实参还是形参共有下面四种情况:

我们知道,二维数组在内存中是按行存放,假定我们定义二维数组和指针如下:

int a[3][4],* p = a [ 0 ] ;

则指针p就指向二维数组。其在内存的存放情况如图6 - 11所示。

从上述存放情况来看,若把二维数组的首地址传递给指针p,则映射过程如图6 - 11

所示。我们只要找到用p所表示的一维数组中最大的元素及下标,就可转换为在二维数组中的

行列数。

# include<stdio.h>

m a i n ( )

{

int a[3][4],*ptr,i,j,max,maxi,maxj;

/ * m a x 是数组的最大, m a x i 是最大元素所在行, m a x j 是最大元素所在列* /

f o r ( i = 0 ; i < 3 ; i + + )

f o r ( j = 0 ; j < 4 ; j + + )

s c a n f ( " % d " , & a [ i ] [ j ] ) ;

p t r = a [ 0 ] ; / * 将二维数组的首地址传递给指针变量* /

m a x _ a r r ( p t r , & m a x , & m a x i , 1 2 ) ;

m a x j = m a x i % 4 ; / * 每行有四个元素,求该元素所在列* /

m a x i = m a x i / 4 ; / * 求该元素所在行* /

printf("max=%d,maxi=%d,maxj=%d",max,maxi,maxj);

}

int max_arr(b,p1,p2,n)

int *b,*p1,*p2,n;

/ * b 指向二维数组的指针, p 1指向最大值,p 2 指向最大值在一维数组中的位置, * /

/ * n 是数组的大小* /

{

int i;

*p1=b[0]; *p1=0;

f o r ( i = 1 ; i < n ; i + + ) / * 找最大* /

if (b[i]>*p1) {*p1=b[i]; *p2=i;}

}

运行程序:

6.4.4 指针与字符数组

在前面的课程中,我们用过了字符数组,即通过数组名来表示字符串,数组名就是数组的首地址,是字符串的起始地址。下面的例子用于简单字符串的输入和输出。

#include<stdio.h>

main()

{

char str[20];

gets(str);

printf("%s\n",str);

}

现在,我们将字符数组的名赋予一个指向字符类型的指针变量,让字符类型指针指向字

符串在内存的首地址,对字符串的表示就可以用指针实现。其定义的方法为:charstr[20],

*P=str;这样一来,字符串str就可以用指针变量P来表示了。

#include<stdio.h>

main()

{

char str[20],*p=str;/*p=str则表示将字符数组的首地址传递给指针变量p*/

gets(str);

printf("%s\n",p);

}

RUN

good morning!

goodmorning!

需要说明的是,字符数组与字符串是有区别的,字符串是字符数组的一种非凡形式,存储时以“\0”结束,所以,存放字符串的字符数组其长度应比字符串大1。对于存放字符的字符数组,若未加“\0”结束标志,只能按逐个字符输入输出。

[例6-18]字符数组的正确使用方法。

#include<stdio.h>

main()

{

charstr[10],*p=str;

int i;

scanf("%s",str);/*输入的字符串长度超过10*/

for(i=0;i<10;i++)

printf("%c",*p++);/*正确输出*/

printf("\n");

p=str;

printf("%s",p);/*字符数组无'\0'标志,输出出错*/

puts(str);/*字符数组无'\0'标志,输出出错*/

}

对上述程序中字符数组以字符串形式输出,若无“\0”标志,则找不到结束标志,输出出

错。

[例6-19]用指向字符串的指针变量处理两个字符串的复制。

字符串的复制要注重的是:若将串1复制到串2,一定要保证串2的长度大于或等于串1。

#include<stdio.h>

main()

{

char str1[30],str2[20],*ptr1=str1,*ptr2=str2;

printf("inputstr1:");

gets(str1);/*输入str1*/

printf("inputstr2:");

gets(str2);/*输入str2*/

printf("str1------------str2\n");

printf("%s.......%s\n",ptr1,ptr2);

while(*ptr2)*ptr1++=*ptr2++;/*字符串复制*/

*ptr1='\0';/*写入串的结束标志*/

printf("str1------------str2\n");

printf("%s.......%s\n",str1,str2);

}

在程序的说明部分,定义的字符指针指向字符串。语句while(*ptr2)*ptr1++=*ptr2++;先测试表达式的值,若指针指向的字符是“\0”,该字符的ASCII码值为0,表达式的值为假,循环结束,表达式的值非零,则执行循环*ptr1++=*ptr2++。语句*ptr1++按照运算优先级别,先算*ptr1,再算ptr1++。

现在,我们修改程序中语句printf("%s.......%s\n",str1,str2)为printf("%s.......%s\n",ptr1,ptr2);

会出现什么结果呢?请思考。

[例6-20]用指向字符串的指针变量处理两个字符串的合并。

#include<stdio.h>

main()

{

char str1[50],str2[20],*ptr1=str1,*ptr2=str2;

printf("inputstr1:");

gets(str1);

printf("inputstr2:");

gets(str2);

printf("str1------------str2\n");

printf("%s.......%s\n",ptr1,ptr2);

while(*ptr1)ptr1++;/*移动指针到串尾*/

while(*ptr2)*ptr1++=*ptr2++;串/*连接*/

*ptr1='\0';/*写入串的结束标志*/

ptr1=str1;ptr2=str2;

printf("str1------------------str2\n");

printf("%s.......%s\n",ptr1,ptr2);

}

需要注重的是,串复制时,串1的长度应大于等于串2;串连接时,串1的长度应大于等于串1与串2的长度之和。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有