C語言中文網 目錄
首頁 > 編程筆記 > C語言筆記 > 指針 閱讀:1,656

函數間傳遞動態內存,C語言函數間動態內存的傳遞詳解

跨函數使用動態內存很重要。所謂“跨函數使用動態內存”就是指“如何在主調函數中使用被調函數中動態分配的內存”。前面章節我們介紹了指針,其目的一是為了講“動態內存分配”。第二個目的就是為了講“跨函數使用動態內存”。

下面來寫一個程序:
# include <stdio.h>
# include <stdlib.h>
void DynamicArray(int **q);  //函數聲明
int main(void)
{
    int *p = NULL;
    DynamicArray(&p);  //函數調用
    printf("*p = %d\n", *p);
    return 0;
}
void DynamicArray(int **q)  //DynamicArray是“動態數組的意思”
{
    *q = malloc(sizeof*q);
    **q = 5;
    return;
}
輸出結果是:
*p = 5

程序說明:
1) “int*p;”表示定義了一個 int* 型的指針變量 p,它只能指向 int 型變量,里面只能存放 int 型變量的地址,但此時它里面還沒有內容,也就是說還沒有初始化。那么 p 是什么時候被初始化的?當調用完 DynamicArray 函數后,DynamicArray 函數構建了一個動態的內存空間,且 p 指向了這個內存空間,此時 p 才被初始化。

2) p 雖然是指針變量,但指針變量也是變量,只要是變量,在程序執行時系統就會為其分配內存單元,所以 p 也有自己的地址。系統為 p 分配內存單元是自動的,而給 p 初始化卻是程序員的事。所以不要把“p 的地址”和“p 里面存放的別的變量的地址”給弄糊涂了。

3) 函數調用時為什么傳遞的是 &p 而不是 p?我們可以試一試把實參改成 p,看看會怎么樣:
# include <stdio.h>
# include <stdlib.h>
void DynamicArray(int *q);  //函數聲明
int main(void)
{
    int i = 2;
    int *p = &i;
    DynamicArray(p);  //函數調用
    printf("*p = %d\n", *p);
    return 0;
}
void DynamicArray(int *q)
{
    q = malloc(sizeof*q);
    *q = 5;
    return;
}
輸出結果是:
*p = 2

首先,實參改成了 p,那么形參就不能再寫 int**q 了。因為 p 是 int* 型,所以 q 也必須是 int* 型。同理,“*q=malloc(sizeof*q);”中賦值號左邊的 q 前面的 * 也要掉。“**q=5;”也要改成“*q=5;”。

此時指針變量 p 指向變量 i,那么指針變量 q 也指向變量 i,即 q 中存放i的地址。但隨后構建了一個動態內存,且 q 指向這個內存。q 中原本存放的i的地址被新的地址取代了,此時 q 不再指向 i,所以 *q 中的值的改變不會影響 i 的值。所以程序最后執行的結果 *p 還是等于 2。

這個實際上同前面講的普通變量的傳遞是一樣的。對于普通變量,如果想在被調函數中直接修改主調函數中變量的值,那么就必須要傳遞該變量的地址。對于指針變量也是一樣的,如果想在被調函數中修改主調函數中定義的指針變量的指向,那么也要傳遞該指針變量的地址。因為修改指針變量的指向就是修改指針變量的值。

4) 為什么形參是 int**q,而不是 int*q?同樣可以用兩種方式理解:
  1. 因為實參傳遞的是“指針變量的地址”,而指針變量的地址是指針的指針,基類型為int*型,所以形參的基類型也必須是int*型才能進行傳遞。
  2. 因為指針變量p的類型是int*型,所以&p的類型為int**型,所以形參也必須是int**型才能進行傳遞。

5) 從該程序中也可以看出,動態分配的內存在函數調用結束后并沒有被釋放。通過輸出結果可以看出,它里面存放的仍然是 5。這就是動態內存分配。

多級指針就是比較“繞”,難度其實不是很大。到底哪個存放的是哪個的地址,這個如果你一開始想不清楚的話可以用筆在紙上畫一下。

最后需要講的是,“跨函數使用動態內存”也可以不用多級指針,即直接返回被調函數中指向動態內存的指針變量,然后賦給主調函數中的指針變量就行了。下面把函數寫下來。
# include <stdio.h>
# include <stdlib.h>
int * DynamicArray(void) ;  //函數聲明
int main(void)
{
    int *p = DynamicArray();  //函數調用
    printf("*p = %d\n", *p);
    return 0;
}
int * DynamicArray(void)  //DynamicArray是“動態數組的意思”
{
    int *q = malloc(sizeof*q);
    *q = 5;
    return q;
}
輸出結果是:
*p = 5

這種方式相比前面使用多級指針的方式更好理解。而且在實際編程中,多是使用這種方式。但是需要注意的是,只有動態分配的內存空間的地址才能返回。這個程序中指針變量 q 所指向的內存空間是用 malloc 定義的,即動態分配的。所以函數調用結束后這段內存空間也不會被釋放,因此返回它的地址才有意義。若 q 所指向的內存空間不是動態分配,而是在棧中靜態分配的,那么就不能返回它的地址。因為函數調用結束后這段內存空間已經被釋放了,不能使用,所以返回去也沒有意義。

因此,在 C 語言中,在講動態內存分配之前經常有一句話,叫作“永遠不要返回局部變量的地址”。

精美而實用的網站,提供C語言C++STLLinuxShellJavaGo語言等教程,以及socketGCCviSwing設計模式JSP等專題。

Copyright ?2011-2018 biancheng.net, 陜ICP備15000209號

底部Logo