I come, I see, I conquer

                    —Gaius Julius Caesar

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

 

     对于数组int a[5],a[0]表示的是数组中的第1个元素,那a指的又是什么呢?由于数组名表示数组从什么地方开始存放。因此,数组名a表示的是一个地址。指针存放的也是地址值,那么数组名与指针之间似乎存在某种相似性。

 

1、一维数组与指针

 

例1. 初识数组和指针 - 单个字符的输出与多个字符的输入

#include <stdio.h>

int main()
{
    
char single;
    
char multiple[10];

    scanf(
"%c"&single);  
    fflush(stdin); 
//清除键盘缓冲区,准备下一次输入
    scanf("%s", multiple);

    printf(
"single: %c \n", single);
    printf(
"multiple: %s \n", multiple);

    
return 0;
}

 

例2. 使用数组名和指针分别访问数组中的元素

#include <stdio.h>

int main()
{
    
int pos[5= { 13579 };
    
int *= pos;

    
// &pos[0], pos, p三者都是数组第1个元素的首地址
    for(int i=0; i<5; i++)
        printf(
"%d %d %d %d \n", pos[i], /* 元素访问方式:数组名+索引 */
        
        
/* 元素访问方式:首地址+偏移量 */
        
*(&pos[0+ i),
        
*(pos + i),
        
*(p + i)

        );

    
return 0;
}

 

运行结果:

1 1 1 1
3 3 3 3
5 5 5 5
7 7 7 7
9 9 9 9
Press any key to continue

分析:数组名pos表示的是数组中元素从什么地方开始存储,它实际上是一个常量指针,它指向数组中第一个元素的地址。它的类型取决于数组元素的类型。在本实例中,数组元素的类型为int类型,那pos的类型是“指向int的常量指针”。这样我们可以声明一个与pos同类型的变量p,然后将pos赋给该变量p。因此p的声明方法必须是:int *p;

 

例3. 数组名作为函数参数传递 - 形参的类型必须与实参一致

#include <stdio.h>

/* 函数声明时,变量名,数组名,指针名可省略 */
void print_v1(int [], int);
void print_v2(int *int);

int main()
{
    
int pos[5= { 13579 };

    
/* 输出数组元素以及各元素地址 */
    
for(int i=0; i<5; i++)
        printf(
"%p pos[%d]:%d *(pos+%d):%d\n", pos+i, i, i, pos[i], *(pos+i) );

    print_v1(pos, 
5); //调用形参为版本1的函数
    print_v2(pos, 5); //调用形参为版本2的函数

    
return 0;
}

void print_v1(int x[], int count) //形参为数组
{
    printf(
"\n");
    
/* 输出数组元素以及各元素地址 */
    
for(int i=0; i<count; i++)
        printf(
"%p x[%d]:%d *(x+%d):%d\n", x+i, i, i, x[i], *(x+i) );
}

void print_v2(int *p, int count) //形参为指针
{
    printf(
"\n");
    
/* 输出数组元素以及各元素地址 */
    
for(int i=0; i<count; i++)
        printf(
"%p p[%d]:%d *(p+%d):%d\n", p+i, i, i, p[i], *(p+i) );
}

 

输出结果:

0012FF6C pos[0]:0 *(pos+1):1
0012FF70 pos[1]:1 *(pos+3):3
0012FF74 pos[2]:2 *(pos+5):5
0012FF78 pos[3]:3 *(pos+7):7
0012FF7C pos[4]:4 *(pos+9):9

0012FF6C x[0]:0 *(x+1):1
0012FF70 x[1]:1 *(x+3):3
0012FF74 x[2]:2 *(x+5):5
0012FF78 x[3]:3 *(x+7):7
0012FF7C x[4]:4 *(x+9):9

0012FF6C p[0]:0 *(p+1):1
0012FF70 p[1]:1 *(p+3):3
0012FF74 p[2]:2 *(p+5):5
0012FF78 p[3]:3 *(p+7):7
0012FF7C p[4]:4 *(p+9):9
Press any key to continue

分析:该程序中的三种输出都是针对同一地址进行。传递的是数组名pos,形参的声明必须和pos的类型相同。pos为一个“指向int型的指针”,因此形参也必须是一个“指向int型的指针”。这样可以将形参声明为一维int类型的数组,也可以声明为一个指向int型的指针int *。

 

 

2、二维数组与指针

 

与一维数组相比,二维数组名与指针之间的差异更大一些。为了理解二维数组与指针之间的对应关系,往往把二维数组看成一维数组,该一维数组中的元素也是一元数组。如:二维数组int pos[2][3]可以理解为一维数组int3 pos[2],int3是我们为方便理解而定义的一种新数据类型,一个int3包含3个int型元素。

 

例1. 二维数组的输出

#include <stdio.h>

int main()
{
    
int pos[2][3= { 1357'9' };

    printf(
"元素访问方式:数组名+索引:pos[i][j] \n");
    
for(int i=0; i<2; i++)
        
for(int j=0; j<3; j++)
            printf(
"pos[%d][%d]: %p %d \n", i, j, &pos[i][j], pos[i][j]);

    printf(
"\n");
    printf(
"元素访问方式:首地址+元素偏移地址:*(&pos[0][0]+3*i+j) \n");

    
for(i=0; i<2; i++)
        
for(int j=0; j<3; j++)
            printf(
"pos[%d][%d]: %p %d \n", i, j, &pos[i][j], *(&pos[0][0]+3*i+j));

    
return 0;
}

 

程序输出:

元素访问方式:数组名+索引:pos[i][j]
pos[0][0]: 0012FF68 1
pos[0][1]: 0012FF6C 3
pos[0][2]: 0012FF70 5
pos[1][0]: 0012FF74 7
pos[1][1]: 0012FF78 57
pos[1][2]: 0012FF7C 0

元素访问方式:首地址+元素偏移地址:*(&pos[0][0]+3*i+j)
pos[0][0]: 0012FF68 1
pos[0][1]: 0012FF6C 3
pos[0][2]: 0012FF70 5
pos[1][0]: 0012FF74 7
pos[1][1]: 0012FF78 57
pos[1][2]: 0012FF7C 0
Press any key to continue

 

例2. 二维数组的地址情况

#include <stdio.h>

int main()
{
    
int boo[3= { 102030 };
    
int pos[3][4= { 1357911131517192123 };

    printf(
"元素地址(方法一): \n");
    printf(
"&pos[0][0~3]: %p %p %p %p\n"&pos[0][0], &pos[0][1], &pos[0][2], &pos[0][3]);
    printf(
"&pos[1][0~3]: %p %p %p %p\n"&pos[1][0], &pos[1][1], &pos[1][2], &pos[1][3]);
    printf(
"&pos[2][0~3]: %p %p %p %p\n"&pos[2][0], &pos[2][1], &pos[2][2], &pos[2][3]);

    
/* 三种方法获取二维数组中各行的首地址 */
    printf(
"行地址(方法一): \n");
    printf(
"pos+0: %p \n", pos+0);
    printf(
"pos+1: %p \n", pos+1);
    printf(
"pos+2: %p \n", pos+2);

    printf(
"行地址(方法二): \n");
    printf(
"*(pos+0): %p \n"*(pos+0));
    printf(
"*(pos+1): %p \n"*(pos+1));
    printf(
"*(pos+2): %p \n"*(pos+2));

    printf(
"行地址(方法三): \n");
    printf(
"pos[0]: %p \n", pos[0]);
    printf(
"pos[1]: %p \n", pos[1]);
    printf(
"pos[2]: %p \n", pos[2]);

    
/* 对比:对一维数组进行的相同操作 */
    printf(
"一维数组: \n");
    printf(
"*(boo+0): %d \n"*(boo+0));
    printf(
"*(boo+1): %d \n"*(boo+1));
    printf(
"*(boo+2): %d \n"*(boo+2));

    printf(
"boo[0]: %d \n", boo[0]);
    printf(
"boo[1]: %d \n", boo[1]);
    printf(
"boo[2]: %d \n", boo[2]);

    printf(
"元素地址(方法二): \n");
    printf(
"pos[0]+0~3: %p %p %p %p\n", pos[0], pos[0]+1, pos[0]+2, pos[0]+3);
    printf(
"pos[1]+0~3: %p %p %p %p\n", pos[1], pos[1]+1, pos[1]+2, pos[1]+3);
    printf(
"pos[2]+0~3: %p %p %p %p\n", pos[2], pos[2]+1, pos[2]+2, pos[2]+3);

    printf(
"元素地址(方法三): \n");
    printf(
"*(pos+0)+0~3: %p %p %p %p\n"*(pos+0), *(pos+0)+1*(pos+0)+2*(pos+0)+3);
    printf(
"*(pos+1)+0~3: %p %p %p %p\n"*(pos+1), *(pos+1)+1*(pos+1)+2*(pos+1)+3);
    printf(
"*(pos+2)+0~3: %p %p %p %p\n"*(pos+2), *(pos+2)+1*(pos+2)+2*(pos+2)+3);

    
return 0;
}

 

运行结果:

元素地址(方法一):
&pos[0][0~3]: 0012FF44 0012FF48 0012FF4C 0012FF50
&pos[1][0~3]: 0012FF54 0012FF58 0012FF5C 0012FF60
&pos[2][0~3]: 0012FF64 0012FF68 0012FF6C 0012FF70
行地址(方法一):
pos+0: 0012FF44
pos+1: 0012FF54
pos+2: 0012FF64
行地址(方法二):
*(pos+0): 0012FF44
*(pos+1): 0012FF54
*(pos+2): 0012FF64
行地址(方法三):
pos[0]: 0012FF44
pos[1]: 0012FF54
pos[2]: 0012FF64
一维数组:
*(boo+0): 10
*(boo+1): 20
*(boo+2): 30
boo[0]: 10
boo[1]: 20
boo[2]: 30
元素地址(方法二):
pos[0]+0~3: 0012FF44 0012FF48 0012FF4C 0012FF50
pos[1]+0~3: 0012FF54 0012FF58 0012FF5C 0012FF60
pos[2]+0~3: 0012FF64 0012FF68 0012FF6C 0012FF70
元素地址(方法三):
*(pos+0)+0~3: 0012FF44 0012FF48 0012FF4C 0012FF50
*(pos+1)+0~3: 0012FF54 0012FF58 0012FF5C 0012FF60
*(pos+2)+0~3: 0012FF64 0012FF68 0012FF6C 0012FF70
Press any key to continue

有了例2中的多种地址获取方式,就很容易通过这些方式获得数组中的元素了。注意:p[i] 与 *(p+i) 是等价的。

 

例3. 在例2的基本上使用解除引用操作符*输出二维数组中的元素

#include <stdio.h>

int main()
{
    
int pos[3][4= { 1357911131517192123 };
    
int i, j;

    printf(
"数组的输出(方法一): \n");
    
for(i=0; i<3; i++)
    {
        
for(j=0; j<4; j++)
            printf(
"pos[i][j]: %d\n", pos[i][j]);
        printf(
"\n");
    }

    printf(
"行首元素的输出(方法一): \n");
    
for(i=0; i<3; i++)
        printf(
"**(pos+i): %d \n"**(pos+i));

    printf(
"行首元素的输出(方法二): \n");
    
for(i=0; i<3; i++)
        printf(
"*pos[i]: %d \n"*pos[i]);

    printf(
"数组的输出(方法二): \n");
    
for(i=0; i<3; i++)
        
for(j=0; j<4; j++)
            printf(
"*(pos[i]+j): %d\n"*(pos[i]+j));

    printf(
"数组的输出(方法三): \n");
    
for(i=0; i<3; i++)
        
for(j=0; j<4; j++)
            printf(
"*(*(pos+i)+j): %d\n"*(*(pos+i)+j));
    
return 0;
}

 

程序输出:

数组的输出(方法一):
pos[i][j]: 1
pos[i][j]: 3
pos[i][j]: 5
pos[i][j]: 7

pos[i][j]: 9
pos[i][j]: 11
pos[i][j]: 13
pos[i][j]: 15

pos[i][j]: 17
pos[i][j]: 19
pos[i][j]: 21
pos[i][j]: 23

行首元素的输出(方法一):
**(pos+i): 1
**(pos+i): 9
**(pos+i): 17
行首元素的输出(方法二):
*pos[i]: 1
*pos[i]: 9
*pos[i]: 17
数组的输出(方法二):
*(pos[i]+j): 1
*(pos[i]+j): 3
*(pos[i]+j): 5
*(pos[i]+j): 7
*(pos[i]+j): 9
*(pos[i]+j): 11
*(pos[i]+j): 13
*(pos[i]+j): 15
*(pos[i]+j): 17
*(pos[i]+j): 19
*(pos[i]+j): 21
*(pos[i]+j): 23
数组的输出(方法三):
*(*(pos+i)+j): 1
*(*(pos+i)+j): 3
*(*(pos+i)+j): 5
*(*(pos+i)+j): 7
*(*(pos+i)+j): 9
*(*(pos+i)+j): 11
*(*(pos+i)+j): 13
*(*(pos+i)+j): 15
*(*(pos+i)+j): 17
*(*(pos+i)+j): 19
*(*(pos+i)+j): 21
*(*(pos+i)+j): 23
Press any key to continue

 

例4:用指向数组的指针输出数组元素

指向数组的指针,或数组指针是多维数组中的一个概念。对于一维数组的情况往往是:

int pos[10];
int *p = pos;

如果要声明一个指向二维数组的指针,该怎么办?这时候要用到数组指针。

我们知道,数组名是一个地址。对于一维数组pos[10]而言,对数组名pos使用*号解除引用后可直接得到该数组的第一个元素,如:int b = *pos;对pos+1解除引用后得到该数组的第二个元素,如可以这样操作:int c = *(pos+1); 这些元素的类型为int型。因此,声明的p指针也必须是int型,从而保持类型的一致性。

假设有二维数组int pos[3][4]; 应该怎样声明一个指向数组名pos的指针呢?对数组名pos使用*号解除引用后实际上是数组第一行元素的首地址,该行是一个包含4个int元素的数组。因此,声明的指针其类型必须也是一个指向包含4个int元素的类型。

所以声明方法为:

int pos[3][4];

int (*p)[4] = pos;

以下实例声明一个指向二维数组名的数组指针,其中的sizeof操作符更有助于理解int (*p)[4]的含义。

#include <stdio.h>

int main()
{
    
int pos[3][4= { 1357911131517192123 };
    
int i, j;
    
int (*p)[4= pos;

    printf(
"sizeof(pos): %d bytes \n"sizeof(pos));

    printf(
"sizeof(pos[0]): %d bytes \n"sizeof(pos[0]));
    printf(
"sizeof(pos[1]): %d bytes \n"sizeof(pos[1]));
    printf(
"sizeof(pos[2]): %d bytes \n"sizeof(pos[2]));

    printf(
"sizeof(pos[0][0]): %d bytes \n"sizeof(pos[0][0]));
    printf(
"sizeof(pos[0][1]): %d bytes \n"sizeof(pos[0][1]));
    printf(
"sizeof(pos[0][2]): %d bytes \n"sizeof(pos[0][2]));

    printf(
"sizeof(p): %d bytes \n"sizeof(p));

    printf(
"数组的输出(方法一): \n");
    
for(i=0; i<3; i++)
    {
        
for(j=0; j<4; j++)
            printf(
"p[i][j]: %d\n", p[i][j]);
        printf(
"\n");
    }

    printf(
"行首元素的输出(方法一): \n");
    
for(i=0; i<3; i++)
        printf(
"**(p+i): %d \n"**(p+i));

    printf(
"行首元素的输出(方法二): \n");
    
for(i=0; i<3; i++)
        printf(
"*p[i]: %d \n"*p[i]);

    printf(
"数组的输出(方法二): \n");
    
for(i=0; i<3; i++)
        
for(j=0; j<4; j++)
            printf(
"*(p[i]+j): %d\n"*(p[i]+j));

    printf(
"数组的输出(方法三): \n");
    
for(i=0; i<3; i++)
        
for(j=0; j<4; j++)
            printf(
"*(*(p+i)+j): %d\n"*(*(p+i)+j));
    
return 0;
}

 

程序输出:

sizeof(pos): 48 bytes
sizeof(pos[0]): 16 bytes
sizeof(pos[1]): 16 bytes
sizeof(pos[2]): 16 bytes
sizeof(pos[0][0]): 4 bytes
sizeof(pos[0][1]): 4 bytes
sizeof(pos[0][2]): 4 bytes
sizeof(p): 4 bytes
数组的输出(方法一):
p[i][j]: 1
p[i][j]: 3
p[i][j]: 5
p[i][j]: 7

p[i][j]: 9
p[i][j]: 11
p[i][j]: 13
p[i][j]: 15

p[i][j]: 17
p[i][j]: 19
p[i][j]: 21
p[i][j]: 23

行首元素的输出(方法一):
**(p+i): 1
**(p+i): 9
**(p+i): 17
行首元素的输出(方法二):
*p[i]: 1
*p[i]: 9
*p[i]: 17
数组的输出(方法二):
*(p[i]+j): 1
*(p[i]+j): 3
*(p[i]+j): 5
*(p[i]+j): 7
*(p[i]+j): 9
*(p[i]+j): 11
*(p[i]+j): 13
*(p[i]+j): 15
*(p[i]+j): 17
*(p[i]+j): 19
*(p[i]+j): 21
*(p[i]+j): 23
数组的输出(方法三):
*(*(p+i)+j): 1
*(*(p+i)+j): 3
*(*(p+i)+j): 5
*(*(p+i)+j): 7
*(*(p+i)+j): 9
*(*(p+i)+j): 11
*(*(p+i)+j): 13
*(*(p+i)+j): 15
*(*(p+i)+j): 17
*(*(p+i)+j): 19
*(*(p+i)+j): 21
*(*(p+i)+j): 23
Press any key to continue

 

例5. 二维数组作函数参数

#include <stdio.h>

void print_v1(int p[][4]);
void print_v2(int (*p)[4]);
//void print_v1(int [][4]);
//void print_v2(int (*)[4]);

int main()
{
    
int pos[3][4= { 1357911131517192123 };
    
    print_v1(pos);
    print_v2(pos);

    
return 0;
}

void print_v1(int p[][4])
{
    printf(
"行首元素的输出: \n");
    
for(int i=0; i<3; i++)
        printf(
"**(p+i): %d \n"**(p+i));

    printf(
"数组的输出: \n");
    
for(i=0; i<3; i++)
        
for(int j=0; j<4; j++)
            printf(
"p[i][j]: %d\n", p[i][j]);
}

void print_v2(int (*p)[4])
{
    printf(
"数组的输出: \n");
    
for(int i=0; i<3; i++)
        
for(int j=0; j<4; j++)
            printf(
"*(*(p+i)+j): %d\n"*(*(p+i)+j));
}

 

注意:函数声明时,参数列表中的数组名,变量名等可以省略。形参可以为一个省略最左边维数的二维数组,也可以是一个数组指针。

 

 

3、解引用方法输出三维数组

 

弄懂了二维数组的解引用方法输出,三维数组就很容易了。

例1. 三维数组的输出

#include <stdio.h>
void main()
{
    
int a[2][3][4]={1,2,3,4,5,6,7,8,9,10,11,12,\
    
13,14,15,16,17,18,19,20,21,22,23,24};

    
for(int i=0;i<2;i++)
        
for(int j=0;j<3;j++)
            
for(int k=0;k<4;k++)
                printf(
"a[%d][%d][%d]: %d: %d \n", i, j, k, &a[i][j][k], a[i][j][k]);

    printf(
"\na:%d \t ***a: %d \t **a: %d\n", a, ***a, **a);
    printf(
"a[0]:%d \t **a[0]: %d \n", a[0], **a[0]);
    printf(
"a[1]:%d \t **a[1]: %d \n", a[1], **a[1]);
    printf(
"a[0][1]:%d \t *a[0][1]: %d \n", a[0][1], *a[0][1]);
}

 

运行结果:
a[0][0][0]: 1244960: 1
a[0][0][1]: 1244964: 2
a[0][0][2]: 1244968: 3
a[0][0][3]: 1244972: 4
a[0][1][0]: 1244976: 5
   ...
a[0][1][3]: 1244988: 8

a[0][2][0]: 1244992: 9
   ...
a[0][2][3]: 1245004: 12

a[1][0][0]: 1245008: 13
a[1][0][1]: 1245012: 14
a[1][0][2]: 1245016: 15
a[1][0][3]: 1245020: 16

a[1][1][0]: 1245024: 17
   ...
a[1][1][3]: 1245036: 20
a[1][2][0]: 1245040: 21
   ...
a[1][2][3]: 1245052: 24

a:1244960      ***a: 1      **a: 1244960
a
a[0]:1244960   *a[0]: 1
a[1]:1245008   *a[1]: 13
a[0][1]:1244976   *a[0][1]: 5

 

 

4、应用举例

 

例1. 删除二维字符数组中所以长度超过k的字符串,返回所剩字符串个数

#include <stdio.h>
#include 
<string.h>
#define N 6 //二维数组中的字符串个数
#define M 10 //二维数组中字符串的最大长度

int func(char (*a)[M], int k) //形参为指向字符串数组指针
{
    
int i, j=0, len;
    
for(i=0; i<N; i++)
    {
        len
=strlen(a[i]);
        
if(len<=k)
            strcpy(a[j
++], a[i]); //字符串复制函数
    }
    
return j;
}

main()
{
    
char fruit[N][M]={"Pear""Banana""Apple""Orange""Grape""Lemon"}; //二维字符数组
    int i, f;
    printf(
"The original string: \n");
    
for(i=0; i<N; i++)
        puts(fruit[i]); 
//puts库函数输出字符串,原型为:int __cdecl puts(const char *);
    printf("\n");
    f
=func(fruit, 5); //实参为数组名
    printf("The string which length is less than or equal to 5: \n");

    
for(i=0; i<f; i++)
        puts(fruit[i]); 
//puts库函数输出字符串,原型为:int __cdecl puts(const char *);
    printf("\n");    

    
return 0;
}

 

运行结果:

The original string:
Pear
Banana
Apple
Orange
Grape
Lemon

The string which length is less than or equal to 5:
Pear
Apple
Grape
Lemon

总结:当我们声明一个指向二维数组的指针后,我们可以跟使用二维数组一样,使用该指针对数组进行相应操作。如上例如示:主函数中声明了一个字符型的二维数组char fruit[N][M];被调用函数参数为一个指向该数组的数组指针 char (*a)[M];当该指针指向了fruit数组,我们就可以使用puts(a[i]),它与puts(fruit[i])一样,都是输出数组中的第i个字符串,或者说二维数组的第i行元素。在这里a[i]和fruit[i]是一个指向字符串的指针,它与puts(const char *)函数的参数类型const char *是一致的,所以可以调用puts()函数输出相应的字符串。同理,可以将a[j++], a[i]作为参数字符串复制函数strcpy(char * dest, const char * src),因为strcpy函数的参数都是指向字符串的指针,与a[j++]和a[i]的类型是一致的。

 

posted on 2009-05-23 14:58  jcsu  阅读(2444)  评论(0编辑  收藏  举报