天天看點

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-多元數組和多元指針

由上圖可知:二維數組,記憶體與一維數組方式存儲,都是連續線性存儲。

小提示:

三維數組,一維中存儲的是二維數組。

執行個體分析(很重要的例子)
周遊二維數組
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語言中的數組的元素可以是另一個數組

c