第34课 多维数组和多维指针

1. 指向指针的指针(多维指针

(1)指针的本质变量,会占用一定的内存空间

(2)可以定义指针的指针来保存指针变量的地址值

(3)指针是个变量,同样也存在传值调用与传址调用

【实例分析】重置动态空间的大小

  1 #include <stdio.h>
  2 
  3 #include <malloc.h>
  4 
  5  
  6 
  7 int reset(char**p,int size,int new_size)
  8 
  9 {
 10 
 11     int ret = 1;
 12 
 13     int i = 0;
 14 
 15     int len = 0;
 16 
 17     char* pt = NULL;
 18 
 19     char* pp = *p;
 20 
 21  
 22 
 23     if((p != NULL)&&(new_size > 0))
 24 
 25     {
 26 
 27         pt = (char*)malloc(new_size);
 28 
 29        
 30 
 31         len = (size < new_size)?size : new_size;//取较小者
 32 
 33        
 34 
 35         //复制原内容
 36 
 37         for(i=0; i<len;i++){
 38 
 39             *pt = *pp++;
 40 
 41         }
 42 
 43        
 44 
 45         free(*p); //释放原空间
 46 
 47         *p = pt;  //*p指针指向新空间
 48 
 49     }
 50 
 51     else
 52 
 53     {
 54 
 55       ret = 0;
 56 
 57     }
 58 
 59    
 60 
 61     return ret;
 62 
 63 }
 64 
 65  
 66 
 67 int main(int argc,char* argv[], char* env[])
 68 
 69 {
 70 
 71    
 72 
 73     char*p =(char*)malloc(5);
 74 
 75  
 76 
 77     printf("%p
", p);//重置前二维数组的内存地址
 78 
 79    
 80 
 81     //重置大小后二维数组的内存地址
 82 
 83     //因为重置后的内存地址可以是新的地址,所以这里
 84 
 85     //需传入p指针的地址,以便在reset函数内部改变p,让
 86 
 87     //p指向新的地址。
 88 
 89     if (reset(&p, 5, 3))
 90 
 91     {
 92 
 93         printf("%p
", p);
 94 
 95     }
 96 
 97    
 98 
 99     free(p);
100 
101     return 0;
102 
103 }

2. 二维数组与二级指针 

(1)二维数组内存中一维的方式排布

(2)二维数据中的第一维是一维数组

(3)二维数组中的第二维才是具体的值

(4)二维数组数组名可看做常量指针

【实例分析】遍历二维数组

 1 #include <stdio.h>
 2 
 3  
 4 
 5 //以一维数组的方式来遍历二维数组
 6 
 7 void printfArray(int a[],int size)
 8 
 9 {
10 
11     int i = 0;
12 
13    
14 
15     printf("printfArray:%d
",sizeof(a)); //退化为指针
16 
17  
18 
19     for(i=0; i<size; i++)
20 
21     {
22 
23         printf("%d
",a[i]);
24 
25     }
26 
27 }
28 
29  
30 
31 int main(int argc,char* argv[], char* env[])
32 
33 {
34 
35     int a[3][3] = {{0, 1, 2},{3, 4, 5},{6, 7, 8}};
36 
37     int* p = &a[0][0];//指向a[0][0]元素
38 
39    
40 
41     int i;
42 
43     int j;
44 
45  
46 
47     for(i = 0;i < 3; i++)
48 
49     {
50 
51         for(j = 0;j < 3;j++)
52 
53         {
54 
55             printf("%d, ",*(*(a + i) + j));//以指针方式访问元素
56 
57         }
58 
59        
60 
61         printf("
");
62 
63     }
64 
65  
66 
67     printf("
");
68 
69    
70 
71     printfArray(p,9);//以一维数组的方式访问二维数组
72 
73  
74 
75     return 0;
76 
77 }

3. 数组名

(1)一维数组名代表数组首元素的地址:int a[5];   a的类型为int*

(2)二维数组名同样代表数组首元素的地址:如int a[3][5], a的类型为int(*)[5]

二维数组 

含义及类型

取地址符(&)

sizeof

数组名:a

二维数组名a指向数组首元素的地址,即第1行(a[0])的地址(注意,不是a[0][0]的地址)。因此,a被称为行指针,指向第1行元素(一维数组)的地址。这个元素的类型是int(*)[5](即一维数组)。所以a的类型为int(*)[5];a可以看作是行指针,

②a + 1表示第二行的指针,a + i表示第i + 1行的指针(也是int(*)[5]类型)……

&a表示整个二维数组的地址,所以&a + 1指向这个二维数组最后一个元素的后面。

 

①sizeof(a)表示整个二维数组的大小。

②sizeof(&a)为指针大小4字节。

③sizeof(*&a)等于sizeof(a)

a[i]

①a[i]的类型:如果把二维数组看作是由三个元素(一维数组)组成的数组,那么这三个一维数组的数组名分别为a[0]、a[1]、a[2]。因此,a[0]可以看作是指向第1行(一维数组)首元素(a[0][0])的地址,a[i]是指向第i + 1行(一维数组)首元素的地址,所以a[i]为int*型

②a[i] + 1表示这行数组第2个元素的地址,即a[i] + 1是指向a[i][1]元素的地址,*(a[i] + 1)是a[i][1]元素的值。同理,a[i] + j是指向a[i][j]的地址,*(a[i] + j)是a[i][j]的值。

&a[i]表示第i + 1行这行整个一维数组的地址。因此,&a[i] + 1是指向这行数组的下一行的指针。

①sizeof(a[i]):a[i]是一个一维数组。sizeof(a[i])为这个数组的大小。

②sizeof(&a[i])为指针大小4字节。

③sizeof(*&a[i])等于sizeof(a[i])

a[i][j]

与a、a[i]类型的含义不同,a[i][j]不再是一个指针,而是元素的类型,即int

&a[i][j]表示这个元素的地址,即int*型

①sizeof(a[i][j]):a[i][j]表示元素的类型。

②sizeof(&a[i][j])为指针大小。

③sizeof(*&a[i][j])等于sizeof(a[i][j])

备注

①通常情况下,数组名可看作是首元素的地址,而表格中所说的数组名a、a[i]的类型是指当他们代表各自数组的首元素时的类型。

②但当对数组名取地址符(&)或sizeof时,它不能看作是首元素的地址,而代表的是整个数组。请注意表格中&和sizeof两列的分析。

【实例分析】数组的信息

#include <stdio.h>

 

int main(int argc,char* argv[], char* env[])

{

   

    int a[3][5] = {0};

    int c;

   

    printf("Information for array:a[3][5]:
");

    printf("a = 0x%08X, a + 1 = 0x%08X, sizeof(a) = %d
", a, a + 1, sizeof(a));

    printf("&a = 0x%08X, &a + 1 = 0x%08X, sizeof(&a) = %d, sizeof(*&a) = %d
",

               &a, &a + 1, sizeof(&a),sizeof(*&a));

 

    printf("
");


    //a[i]指向一个一维数组的首元素,a[i]+1指向该行第2个元素。sizeof(a[i])时不能看成首元素,而是这行整个一维数组

    for(c=0;c< 5;c++)

    {

      printf("a[%d] = 0x%08X, a[%d] + 1 = 0x%08X, sizeof(a[%d]) = %d,
",

                c, a[c], c, a[c] + 1,c, sizeof(a[c]));

    }

 

    printf("
");


    //对a[i]进行&取地址符时,a[i]不能看作这一行的首元素,而是整个一维数组。即&a[i]表示第i+1的整个数组
    //&a[i]+1表示下一行。

for(c=0;c< 5;c++)

    {

      printf("&a[%d] = 0x%08X, &a[%d] + 1 = 0x%08X, sizeof(&a[%d]) = %d, sizeof(*&a[%d]) = %d
",

               c, &a[c],c, &a[c] + 1,c, sizeof(&a[c]), c, sizeof(*&a[c]));

    }

 

    return 0;

}


/*

输出结果:

Information for array:a[3][5]:

a = 0x0023FE80, a + 1 = 0x0023FE94, sizeof(a) = 60

&a = 0x0023FE80, &a + 1 = 0x0023FEBC, sizeof(&a) = 4, sizeof(*&a) = 60

a[0] = 0x0023FE80, a[0] + 1 = 0x0023FE84, sizeof(a[0]) = 20,

a[1] = 0x0023FE94, a[1] + 1 = 0x0023FE98, sizeof(a[1]) = 20,

a[2] = 0x0023FEA8, a[2] + 1 = 0x0023FEAC, sizeof(a[2]) = 20,

a[3] = 0x0023FEBC, a[3] + 1 = 0x0023FEC0, sizeof(a[3]) = 20,

a[4] = 0x0023FED0, a[4] + 1 = 0x0023FED4, sizeof(a[4]) = 20,

&a[0] = 0x0023FE80, &a[0] + 1 = 0x0023FE94, sizeof(&a[0]) = 4, sizeof(*&a[0]) = 20

&a[1] = 0x0023FE94, &a[1] + 1 = 0x0023FEA8, sizeof(&a[1]) = 4, sizeof(*&a[1]) = 20

&a[2] = 0x0023FEA8, &a[2] + 1 = 0x0023FEBC, sizeof(&a[2]) = 4, sizeof(*&a[2]) = 20

&a[3] = 0x0023FEBC, &a[3] + 1 = 0x0023FED0, sizeof(&a[3]) = 4, sizeof(*&a[3]) = 20

&a[4] = 0x0023FED0, &a[4] + 1 = 0x0023FEE4, sizeof(&a[4]) = 4, sizeof(*&a[4]) = 20

*/

(3)二维数元素的访问方式int a[i][j];

  ①a[i][j]

  ②*(*(a + i) + j);  //a + i是第i+1行首元素(一维数组)的地址,即保存一个一维数的地址,*(a + i)取出当中保存的一维数组地址。注意这也是一个地址,而*(a + i)+j表示在这个一维数组地址基础上加j的偏移处,然后*(*(a + I)+ j)取出该元素出来。

【实例分析】如何动态申请二维数组

#include <stdio.h>

#include <malloc.h>

 

int** malloc2d(int row,int col)

{

    int** ret = NULL;

   

    if((row >0) && (col >0))

    {

        int* p = NULL;

 

        //先申请第一维数组,用于存放行指针

        ret = (int**)malloc(row * sizeof(int*));

 

        //再申请整个数组存储空间的大小,用于存放所有元素

        p = (int*)malloc(row * col * sizeof(int));

 

        //将数组空间分为二维

        if ((ret != NULL) && (p != NULL))

        {

            int i=0;

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

            {

                ret[i] = p + i*col;

            }

        }

        else

        {

            free(ret);

            free(p);

           

            ret = NULL;

        } 

    }

 

    return ret;

}

 

void free2d(int** p)

{

    if(*p != NULL) //p指向二维数组

    {

        //*p指向p[0],而这里保存的即是

        //第1个元素的地址,也是整个数

        //组元素空间地址。

        free(*p);  //释放元素所占空间

    }

 

    free(p);//释放行指针所占空间

}

 

int main()

{

    int** a = malloc2d(3, 3);

    int i = 0;

    int j = 0;

 

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

    {

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

        {

            printf("%d, ",a[i][j]);

        }

        printf("
");

    }

 

    free2d(a);

 

    return 0;

}

【实例分析】无法将指针变量本身传递给一个函数,须传递指针的地址

#include <stdio.h>

#include <malloc.h>

#include <string.h>

 

void GetMemory(char* p,int num)

{

    //由于p是函数参数,当函数返回时,会被释放

    p = (char*)malloc(num*sizeof(char));

}

 

void GetMemoryEx(char** p,int num)

{

    //p指向一个指针,将该指针的值指向新的开辟的内存空间。

    *p = (char*)malloc(num * sizeof(char));

}

 

int main()

{

    char* str = NULL;

   

/*

    //错误的做法

    GetMemory(str,10);//试图让str让指向新开辟的内存空间。因为须改变

                      //指针str的值,所以得传递指针的地址过去。否则

                      //在传参时,GetMemory只是复制str的值过去。即,

                      //将NULL复制给参数。

   

    strcpy(str,"hello");

 

    free(str);//free并没有起作用,内存泄漏

*/

 

    //正确的做法

    GetMemoryEx(&str,10);//将str指针的地址传递到函数里,函数内部就

                         //可以改变str指针的值,让其指向新的地址。

   

    strcpy(str,"hello");

 

    free(str);//free并没有起作用,内存泄漏

 

    return 0;

}

4. 小结

(1)C语言只支持一维数组

(2)C语言中的数组大小必须在编译期就作为常数确定

(3)C语言中的数组元素可以是任何类型的数据

(4)C语言中的数组的元素可以是另一个数组

原文地址:https://www.cnblogs.com/hoiday/p/10074012.html