34-多维数组和多维指针

注:博客中内容主要来自《狄泰软件学院》,博客仅当私人笔记使用。

测试环境:Ubuntu 10.10

GCC版本:4.4.5

 

一、指向指针的指针

1) 指针的本质是变量

2) 指针会占用一定的内存空间

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

int main()
{
    int i = 0;
    int* p = NULL;  //p指向int类型变量
    int** pp = NULL;
        
    pp = &p;
 
    *pp = &i;           //*pp=p=&i
 
    return 0;
}

 

二、问题

1) 为什么需要指向指针的指针

         - 指针在本质上也是变量

         - 对于指针也同样存在传值调用传址调用

实例分析
重置动态空间大小
34-1.c
 
#include <stdio.h>
#include <malloc.h>
 
/*
p:申请的内存地址(指向指针的指针,传址操作)
size:申请的内存大小
new_size:实际需要的内存大小
*/
int reset(char** p, int size, int new_size)  //动态内存过大,减小内存浪费
{
    int ret = 1;
    int len = 0;        //存储新内存和输入内存二者长度最小值
    char* pt = NULL;    //指向新申请的内存
    char* tmp = NULL;   //存储新申请的内存(tmp = pt)
    char* pp = *p;      //pp指针存储输入的内存p地址
   
    if( (p != NULL) && (new_size > 0) )
    {
        //申请新内存
        pt = (char*)malloc(new_size);    
        //尽量不要操作原始数据,为了防止数据丢失,用一个变量tmp存储,后续操作tmp
        tmp = pt;
        //对比新申请的内存和要修改的内存大小,找出二者最小值
        len = (size < new_size) ? size : new_size;  
        
        int i = 0;    //循环计数
        for(i=0; i<len; i++)
        {   //复制数据。任何指向这块内存地址的指针,解引用(*)都会获取新值
            *tmp++ = *pp++;  
        }
      
        free(*p);  //释放掉输入的**p内存
        *p = pt;   //pt和tmp最初指向的都是新申请内存其实位置,上边操作tmp修改了内存数据,而pt仍然存储内存的起始地址,只要将pt赋值给*p就能使用新的内存
    }
    else
    {
        ret = 0;
    }
   
    return ret;
}
 
int main()
{
    char* p = (char*)malloc(5);
   
    printf("%p\n", p);
   
    if( reset(&p, 5, 3) )
    {
        printf("%p\n", p);
    }
 
    free(p);
   
    return 0;
}

操作:

1) gcc 34-1.c -o 34-1.out编译正确,打印结果:

0x8de2008
0x8de2018
分析:
        原来申请的内存被修改。
 
修改指针指向的数据:(安全操作指针指向的数据)
1. 定义临时指针变量tmp
2. 指向数据指针p赋值给tmp
3. 通过操作tmp修改p指向内存中的数据,防止丢失指向内存起始地址的指针(用p记录)
4. 修改数据完毕,将p传递给使用者。(p指向存储数据内存的起始地址)
 

 

三、二维数组与二级指针

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

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

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

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

由上图可知:二维数组,内存与一维数组方式存储,都是连续线性存储。

 

小提示:

三维数组,一维中存储的是二维数组。

实例分析(很重要的例子)
遍历二维数组
34-2.c
#include <stdio.h>
#include <malloc.h>

//打印数据:效率O(n)
void printArray(int a[], int size)
{
    int i = 0;
    //打印数组a的大小,结果只打印指针大小,数组退化为指针
    printf("printArray: %d\n", sizeof(a));       
 
    for(i=0; i<size; i++)
    {
       printf("%d\n", a[i]);          //内存都是线性表示
    }
}
 
int main()
{
    int a[3][3] = {{0, 1, 2}, {3, 4, 5}, {6, 7, 8}};
    int* p = &a[0][0];    //获取二维数组首地址
   
    int i = 0;
    int j = 0;
   
    for(i=0; i<3; i++)        //一维
    {
      for(j=0; j<3; j++)        //二维
      {
        printf("%d, ", *(*(a+i) + j));      //*(a+i) ==> a[i],*(a[i] + j)       ==>a[i][j] 数组方式可读                                                                                    //性好
      }
      printf("\n");    //打印3个换行
    }
   
    printf("\n");
   
    printArray(p, 9);
   
    return 0;
}

操作:

1) gcc 34-2.c -o 34-2.out编译正确,打印结果:

0, 1, 2, 
3, 4, 5, 
6, 7, 8, 

printArray: 4
0
1
2
3
4
5
6
7
8

分析:

1. 内存都是线性表示。

 

四、数组名

1) 一维数组名代表数组首元素的地址

                   int a[5]     a的类型为int*

2) 二维数组名同样代表数组首元素的地址

                   int m[2][5]        m的类型为int(*)[5]

         结论:

         1、二维数组名可以看做是指向数组的常量指针

         2、二维数组可以看做是一维数组(内存分布是线性的)

         3、二维数组中的每个元素都是同类型的一维数组

实例分析
如何动态申请二维数组
34-3.c
 
#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;
            //填充顺序:在ret[0]中填充列内存。以此类推ret[1]、ret[2]……
            for(i=0; i<row; i++)
            {   
                //p + i * col ==> 每次随着i的改变指向新的一列内存首地址
                ret[i] = p + i * col;    //ret[i]是解引用了一次,一级指针
            }
        }
        else
        {
            free(ret);
            free(p);
           
            ret = NULL;
        }  
    }   
    return ret;
}
 
//从外向内释放内存(这里是先释放列,在释放行)
void free2d(int** p) 
{
    if( *p != NULL )
    {
        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("\n");
    }
   
    free2d(a);
   
    return 0;
}

操作:

1) gcc 34-3.c -o 34-3.out编译正确,打印结果:

0, 0, 0, 
0, 0, 0, 
0, 0, 0, 

 

五、小结:

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

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

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值