簡體   English   中英

從 C 中的 function 返回一個數組

[英]Returning an array from function in C

我寫了一個 function 返回一個數組,雖然我知道我應該返回一個動態分配的指針,但我仍然想知道當我返回一個在 function 中本地聲明的數組時會發生什么(沒有將其聲明為靜態),當我注意到我的 function 中內部數組的 memory 沒有被釋放時,我感到很驚訝,我把我的數組恢復到 main。 主要的:

int main()
{
    int* arr_p;
    arr_p = demo(10);

    return 0;
}

和 function:


int* demo(int i)
{
    int arr[10] = { 0 };
    for (int i = 0; i < 10; i++)
    {
        arr[i] = i;
    }
    return arr;
}

當我取消引用arr_p時,我可以看到demo function 中設置的 0-9 整數。兩個問題:

  1. 為什么當我檢查arr_p時發現它的地址與demo function 中的arr相同?
  2. demo_p指向已在demo中的未釋放數據(0-9 數字)? 我預計當我們離開demo scope 時, demo中的arr將被釋放。

編程時必須注意的一件事是要注意規則所說的內容,而不僅僅是看起來有效的內容。 規則說你不應該返回一個指向本地分配數組的指針,這是一個真實的規則。

如果您在編寫返回指向本地分配數組的指針的程序時沒有收到錯誤消息,這並不意味着它沒問題。 (雖然,這意味着您確實應該獲得更新的編譯器,因為任何體面的現代編譯器都會對此發出警告。)

如果您編寫的程序返回一個指向本地分配數組的指針並且它似乎可以工作,那也不意味着它沒問題。 對此要非常小心:一般而言,在編程中,尤其是在 C 中,看似有效並不能證明您的程序沒問題。 您真正想要的是讓您的程序以正確的理由運行

假設你租了一套公寓。 假設,當您的租約到期並且您搬出時,您的房東沒有從您那里取回您的鑰匙,但也沒有更換鎖。 假設,幾天后,你意識到你在一個壁櫥的后面忘記了一些東西。 假設你不經詢問就偷偷溜回去試圖收集它。 接下來發生什么?

  • 碰巧的是,您的鑰匙仍然可以在鎖中使用。 這是完全出乎意料,還是有點出乎意料,還是保證有效?
  • 碰巧,你忘記的項仍然在衣櫃里。 它尚未清除。 這是完全出乎意料,還是有點出乎意料,還是保證會發生?
  • 最后,無論是你的老房東,還是警察,都沒有因為你的這種侵入行為而與你搭訕。 再說一次,這是完全出乎意料,還是有點出乎意料,還是幾乎完全在意料之中?

您需要知道的是,在 C 中,重用您不再允許使用的內存幾乎完全類似於潛入您不再租用的公寓。 它可能有效,也可能無效。 你的東西可能還在那里,也可能不在。 你可能會遇到麻煩,也可能不會。 沒有辦法預測會發生什么,也沒有(有效的)結論可以從發生或不發生的任何事情中得出。

回到你的程序:像arr這樣的局部變量通常存儲在調用堆棧中,這意味着即使在函數返回之后它們仍然存在,並且可能不會被覆蓋,直到下一個函數被調用並使用堆棧上的該區域它自己的目的(甚至可能不是)。 因此,如果您返回一個指向本地分配內存的指針,並立即取消引用該指針(在調用任何其他函數之前),它至少有可能“工作”。 這再次類似於公寓的情況:如果還沒有其他人搬進來,您遺忘的物品很可能還在那里。 但這顯然不是您可以依賴的東西。

arrdemo中的一個局部變量,當你從函數返回時它會被銷毀。 由於您返回一個指向該變量的指針,因此該指針被稱為dangling 取消引用指針會使您的程序具有未定義的行為

修復它的一種方法是malloc (內存分配)您需要的內存。

例子:

#include <stdio.h>
#include <stdlib.h>

int* demo(int n) {
    int* arr = malloc(sizeof(*arr) * n);  // allocate

    for (int i = 0; i < n; i++) {
        arr[i] = i;
    }

    return arr;
}

int main() {
    int* arr_p;
    arr_p = demo(10);
    printf("%d\n", arr_p[9]);
    free(arr_p)                 // free the allocated memory
}

輸出:

9

為什么demo_p指向demo已經沒有釋放的數據(0-9 數字)? 我預計, arr內部demo ,我們的下了車將被釋放demo范圍。

arr對象的生命周期已經結束,讀取arr先前占用的內存地址會使您的程序出現未定義的行為 您可能會看到舊數據,或者程序可能會崩潰——或者做一些完全不同的事情。 任何事情都可能發生。

……我注意到函數中內部數組的內存沒有被釋放……

內存的釋放不是您可以注意到或觀察到的,除非通過查看記錄內存預留的數據(在本例中為堆棧指針)。 當內存被保留或釋放時,這只是一個關於哪些內存可用或不可用的簿記過程。 釋放內存不一定會擦除內存或立即將其重新用於其他目的。 查看內存不一定會告訴您它是否在使用中。

int arr[10] = { 0 }; 出現在函數內部,它定義了一個數組,該數組在函數開始執行時自動分配(如果定義在某個嵌套范圍內,則在函數執行中的某些時間)。 這通常通過調整堆棧指針來完成。 在普通系統中,程序有一個稱為堆棧的內存區域,堆棧指針包含一個地址,該地址標記當前保留供使用的堆棧部分的末尾。 當函數開始執行時,堆棧指針會發生變化,以便為該函數的數據保留更多內存。 當函數的執行結束時,堆棧指針被更改以釋放該內存。

如果您保留指向該內存的指針(如何做到這一點是另一回事,將在下面討論),您將不會在函數返回后立即“注意到”或“觀察”該內存的任何更改。 這就是為什么您看到arr_p的值是arr擁有的地址,這就是為什么您會看到該內存中的舊數據。

如果調用其他函數,堆棧指針將針對新函數進行調整,該函數通常會出於自己的目的使用內存,然后該內存的內容將發生變化。 您在arr擁有的數據將消失。 初學者遇到的一個常見例子是:

int main(void)
{
    int *p = demo(10);
    // p points to where arr started, and arr’s data is still there.

    printf("arr[3] = %d.\n", p[3]);
    // To execute this call, the program loads data from p[3]. Since it has
    // not changed, 3 is loaded. This is passed to printf.

    // Then printf prints “arr[3] = 3.\n”. In doing this, it uses memory
    // on the stack. This changes the data in the memory that p points to.

    printf("arr[3] = %d.\n", p[3]);
    // When we try the same call again, the program loads data from p[3],
    // but it has been changed, so something different is printed. Two
    // different things are printed by the same printf statement even
    // though there is no visible code changing p[3].
}

回到如何獲得指向內存的指針的副本,編譯器遵循 C 標准中抽象指定的規則。 C 標准在demo定義了數組arr的抽象生命周期,並說生命周期在函數返回時結束。 它進一步說,當它指向的對象的生命周期結束時,指針的值變得不確定。

如果您的編譯器簡單地生成代碼,就像您使用帶有-O0 GCC 編譯以關閉優化時所做的那樣,它通常將地址保留在p ,您將看到上述行為。 但是,如果您打開優化並編譯更復雜的程序,編譯器會嘗試優化它生成的代碼。 它不是機械地生成匯編代碼,而是嘗試找到執行程序定義行為的“最佳”代碼。 如果您使用具有不確定值的指針或嘗試訪問生命周期已結束的對象,則程序沒有定義的行為,因此編譯器的優化可能會產生新程序員無法預料的結果。

親愛的,如您所知,在局部函數中聲明的變量的存在僅在該局部作用域內。 完成所需的任務后,函數將終止,然后銷毀局部變量。 當您嘗試從 demo() 函數返回一個指針時,問題是指針指向的數組將在我們退出 demo() 后被銷毀。 因此,您確實正在嘗試返回一個指向取消分配內存的懸空指針。 但是我們的規則建議我們不惜一切代價避免懸空指針。

因此,您可以通過在使用 free() 釋放內存后重新初始化來避免它。 您也可以使用 malloc() 分配一些連續的內存塊,或者您可以將 demo() 中的數組聲明為靜態數組。 當本地函數成功退出時,這也將存儲分配的內存常量。

謝謝你親愛的..

#include<stdio.h>
#define N 10

int demo();
int main()
{
   int* arr_p;
   arr_p = demo();
   printf("%d\n", *(arr_p+3));
}

int* demo()
{
   static int arr[N];

for(i=0;i<N;i++)
{
   arr[i] = i;
}

   return arr;
}

OUTPUT : 3

或者你也可以寫成......

#include <stdio.h>
#include <stdlib.h>
#define N 10

int* demo() {
   int* arr = (int*)malloc(sizeof(arr) * N);

   for(int i = 0; i < N; i++)
{
   arr[i]=i;
}

   return arr;
}

int main()
{
  int* arr_p;
  arr_p = demo();
  printf("%d\n", *(arr_p+3));
  free(arr_p);  
  
  return 0;
}

OUTPUT : 3

當我一直試圖從 function 返回 char 數組時遇到過類似的情況。但我總是需要一個固定大小的數組。

通過在其中聲明一個具有固定大小的 char 數組的結構並從 function 返回該結構來解決此問題:

#include <time.h>

typedef struct TimeStamp
{
    char Char[9];
} TimeStamp;

TimeStamp GetTimeStamp()
{
    time_t CurrentCalendarTime;

    time(&CurrentCalendarTime);

    struct tm* LocalTime = localtime(&CurrentCalendarTime);

    TimeStamp Time = { 0 };

    strftime(Time.Char, 9, "%H:%M:%S", LocalTime);

    return Time;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM