簡體   English   中英

動態內存和指針參數

[英]Dynamic memory and pointers arguments

我有這兩個功能,目的是做相同的事情-僅從整數讀取文件中的每一行並將它們存儲在數組中:

我在main()函數上這樣稱呼他們:

StoreinArray1(X, size, f);

StoreinArray2(X, size, f);

一個有效,但第二個無效。

第一

int StoreinArray1(int X[], int *size, char *file)
{
    int i=0;
    FILE *f;
    f = fopen(file, "r");

    X = (int*) realloc (X, *size * sizeof(int));

for (i=0;i<*size;i++)
{
    fscanf(f, "%d", &X[i]);
}

return 1;
}

第二

int StoreinArray2(int X[], int *size, char *file)
{
  FILE *f;
  f = fopen(file, "r");
  if (f == NULL)
     return -1;  // failed opening
   *size = 0;
   while (!feof(f))
   {
     if (fscanf(f, "%d", &X[*size]) == 1)
       *size = *size + 1;
   }
   fclose(f);
   return 1;
}

首先,我使用了動態內存分配和實際計算的大小:

 X = malloc(0); 

 while ((ch = fgetc(f)) != EOF)
{
    if (ch == '\n')
    lines++;
}

size = &lines;

對於第二,我不能做同樣的事情。 我嘗試時,Visual Studio代碼崩潰。

所以我嘗試做*size = 0 ,然后是StoreinArray2(X, size, f); 但它也不起作用。

所以我的問題是關於第二個功能:

它正在掃描文件時計算大小嗎? 據說沒有必要使用動態內存分配(我的老師說)。

如果是這樣,我如何正確傳遞一些“大小”參數? 作為一個指針還是一個簡單的整數?

先感謝您!


編輯:

這是完整的First程序:

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

int main()
{
FILE *f;
int *size=0, *X, lines=1;
char *file = {"file.txt"};
char ch;

X = malloc(0);

f = fopen(file, "r");

while ((ch = fgetc(f)) != EOF)
{
    if (ch == '\n')
    lines++;
}

size = &lines;

  StoreinArray(X, size, file);

}

int StoreinArray(int X[], int *size, char *file)
{
int i=0;
FILE *f;
f = fopen(file, "r");

X = (int*) realloc (X, *size * sizeof(int));

for (i=0;i<*size;i++)
{
    fscanf(f, "%d", &X[i]);
}

for (i=0;i<*size;i++)
    printf("%d\n",X[i]);

return 1;
}

第二:

int main()
{
    int X[100];
    int *size;
  char *file = {"file.txt"};

  *size = 0;

  StoreinArray(X, size, file);
}
int StoreinArray(int X[], int *size, char *file)
{
  FILE *f;
  f = fopen(file, "r");
  if (f == NULL)
    return -1;
  *size = 0;
  while (!feof(f))
 {
    if (fscanf(f, "%d", &X[*size]) == 1)
      *size = *size + 1;
  }
  fclose(f);
  return 1;
}

首先,我必須在main中打開文件以計算行數。 我知道我忘記了main中的fclose(f)和free(X),但是有了這些指令,VSC崩潰了。


  int StoreinArray (int X[], int *size, char *file)
    {
      FILE *f;
      int i=0;
      f = fopen(file, "r");
       if (f == NULL)
       return -1;
     *size = 0;
      while (!feof(f))
      {
        if (fscanf(f, "%d", &X[*size]) == 1)
        {
           *size = *size + 1;
           X = (int *) realloc (X , *size * sizeof(int));
        }
      }
      fclose(f);
      return 1;
      }

       int main() 
   {
        int *X, size=0;
        char *file = {"f.txt"};
        X=malloc(0);
        StoreinArray(X, &size, file);
        free(X); 
   }

程序第二個版本的問題是main中的size聲明。 將其聲明為int,而不是指向int的指針。 當前的程序崩潰是因為您沒有為大小分配任何空間,而StoreInArray嘗試更新它時,您遇到了訪問沖突。 因此,main應該看起來像這樣:

int main()
{
    int X[100];
    int size;
    char *file = {"file.txt"};

    size = 0;

    StoreinArray(X, &size, file);
}

好的,我將盡一切努力並解釋所有我能找到的東西。

首先,我們需要討論變量,指針和內存,因為您似乎對這些概念不太了解。 一旦點擊,其余的應該很容易地跟隨。

首先,簡單變量。 這部分很簡單,我想您或多或少都了解這一點。

int x; // This is an integer variable. It's not initialized, so its value could be anything
int meaning = 42; // This is another integer variable. Its value will be 42.
double pi = 3.14; // A variable with digits after the decimal point
char c = 15; // Another inte... well, yes, actually, char is also an integer.
char c2 = 'a'; // Nice, this also counts. It's converted to an integer behind the scenes.

等等。

與數組類似:

int arr[10]; // Array with 10 values. Uninitialized, so they contain garbage.
int arr2[3] = { 1, 2, 3 }; // Array with 3 values, all initialized
int arr3[] = {1, 2, 3, 4, 5}; // Array with 5 values.

數組基本上只是一次創建的一堆變量。 創建數組時,C需要知道大小,並且大小必須為固定數字-您不能使用其他變量。 這是有原因的,但它是技術性的,我將不贅述。

現在關於內存。 這些變量中的每一個都將存儲在計算機RAM中的某個位置。 精確的位置是無法預測的,並且每次運行程序時都可能會有所不同。

現在,RAM就像一個巨大的字節數組。 有字節號0 ,字節號1等的int變量占用4個字節,因此它可能,例如,以字節數結束120121122123

單個變量(或單個數組)中的所有字節在RAM中將彼此相鄰。 兩個不同的變量可能會出現在RAM的另一端,但是每個變量中的字節將在一起。

現在我們來談談指針的概念。 指針基本上只是一個整數變量。 它包含其他一些變量的第一個字節的RAM號。 讓我們看一個例子:

int i = 42;
int *p = &i;

假設變量i存儲在字節數200 ... 203 (即4個字節)中。 在這些字節中,我們的值為42 然后,假設變量p存儲在字節數300 ... 303 (這是另外4個字節)中。 好吧,這4個字節將包含值200 ,因為這是i變量的第一個字節。

這也是什么程序員的意思,當他們說“的(內存)地址<variable> ”或”指針<variable> ,它是在RAM中的第一個字節的數量<variable> 。因為同一個變量棒的所有字節一起,然后通過了解第一個字節(並了解變量的類型),可以弄清楚<variable>其余部分在內存中的位置。

現在,在示例中再添加一行:

*p = 5;

在這種情況下,計算機要做的是獲取存儲在p的地址,然后轉到內存中的該位置,將隨后的4個字節視為整數,然后將值5放入其中。 由於我們先前已將p設置為“指向” i的地址,因此其效果與簡單地設置i變量本身相同。

好吧,你明白了嗎? 這有點棘手,通常需要一段時間才能將頭纏住。 隨意重新閱讀它以理解它。 您將需要它繼續前進。

准備? 好的,讓我們談談堆棧和動態內存。

程序啟動時,操作系統會自動為其分配一些內存,以使其更易於啟動。 就像一個大字節數組,所有字節都在內存中。 如今,它通常約為1MB,但可能會有所不同。 該存儲器稱為“堆棧”。 為什么這樣稱呼它? 嗯,我再解釋一遍。

無論如何,當您的main()函數啟動時,操作系統就像“在這里,您是我的好朋友,一個指向The Stack的指針。您將視需要使用所有這些!祝您愉快!”

然后,您的main()函數將使用它來存儲您在其中所做的所有變量。 所以當你像p = &i; 那么您存儲在p的地址在The Stack中。

現在,當main()調用另一個函數(例如StoreinArray() ,它還會為它提供一個指向堆棧的指針,並說:“好,這是指向堆棧的指針。小心,我已經使用了它的前XXX個字節,但請隨時使用其余的內容。”

然后, StoreinArray()使用堆棧將其變量放在此處。 StoreinArray()調用其他內容時,它會不斷執行相同的操作。

現在,這里有幾件事要注意:

  • 這種方案很好,因為沒有人需要“分配”或“取消分配”任何內存。 更簡單,更快。
  • 但是,當一個函數返回時,它的所有變量都被認為已經消失,並且該內存對於以后想要它的其他任何人都是公平的。 所以要小心指向它的指針。 它們僅在函數運行時才有效。 當它返回時-指針仍然會“工作”,但是誰知道什么時候什么東西會覆蓋該內存...而如果您寫入該內存,誰能告訴您您將弄糟什么呢? 通過這種方式創建了許多細微的錯誤。
  • 堆棧是相當有限的,可以耗盡。 當其中的所有字節用完時,您的程序將崩潰。 發生這種情況的典型方式是,當您嘗試創建一個非常大的數組時,或者您進入某種無限循環時,該函數不斷地反復調用自身。 試試看 :)

因此,對於這些情況,您使用“動態”內存。 在C語言中,這主要意味着malloc() 您告訴malloc()需要多少字節的內存,然后malloc()在RAM中找到足夠大的未聲明空間,將其標記為已使用,並為您提供一個指向它的指針。 好吧,這還是事物的簡化視圖。

當您事先不知道需要多少內存時,也可以使用相同的方法。

不利的一面是,使用完內存后,您需要free()內存,否則可能會耗盡可用內存,然后malloc()將失敗。 還應注意,釋放內存后,所有指向該指針的指針都應視為無效,因為您不再是該特定內存的所有者。 如果您不停地進行任何操作,則可能會發生任何事情。

哎呀,很多。 好,我需要休息一下。 稍后,我將返回並分析您的程序。 但是,如果您已經了解了所有這些內容,現在應該可以在程序中發現錯誤。 嘗試逐行瀏覽它們,並向自己講述每行的功能。

許多小時后:

好的,讓我們看一下您的程序。 第一個:

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

int main()
{
FILE *f;
int *size=0, *X, lines=1;
char *file = {"file.txt"};
char ch;

X = malloc(0);

f = fopen(file, "r");

while ((ch = fgetc(f)) != EOF)
{
    if (ch == '\n')
    lines++;
}

size = &lines;

  StoreinArray(X, size, file);

}

int StoreinArray(int X[], int *size, char *file)
{
int i=0;
FILE *f;
f = fopen(file, "r");

X = (int*) realloc (X, *size * sizeof(int));

for (i=0;i<*size;i++)
{
    fscanf(f, "%d", &X[i]);
}

for (i=0;i<*size;i++)
    printf("%d\n",X[i]);

return 1;
}

這里有兩點可以改進。 首先- sizelines變量。 兩者都不需要。 特別是因為您將size設置為始終指向lines 只要保持lines ,一切都會好起來的。 當您將其傳遞給StoreinArray() ,將其作為一個簡單的整數傳遞。 不需要指針。

其次, X數組。 您正在做一些奇怪的事情,似乎您在黑暗中摸索。 不需要malloc(0)然后再需要realloc(X, *size*sizeof(int) 。保持簡單-首先計算行數,然后分配內存(根據需要)。在main()方法中,然后將最后的X傳遞給StoreinArray 。這樣可以避免另一個細微的錯誤-在StoreinArray()函數內部時,執行X = (int*) realloc (X, *size * sizeof(int)); X的值僅在StoreinArray()函數內部更改。當函數返回時, main()函數中的變量X仍將具有其舊值。您可能試圖通過reallocate()跳舞,但這不是它的工作方式,更糟糕的是-在realloc()X過去的任何值都不再是有效的指針,因為realloc()釋放了舊內存!使用main()函數中的X變量執行任何操作,您的程序將崩潰。

讓我們看看您的程序在我提出的更改后的外觀(以及一些其他的外觀上的細微調整):

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

int main()
{
    char *file = "file.txt";
    FILE *f = fopen(file, "r");
    int *X, lines=1;
    char ch;

    while ((ch = fgetc(f)) != EOF)
    {
        if (ch == '\n')
            lines++;
    }
    fclose(f);

    X = (int *)malloc(lines * sizeof(int));

    StoreinArray(X, lines, file);
}

void StoreinArray(int X[], int lines, char *file)
{
    int i=0;
    FILE *f = fopen(file, "r");

    for (i=0;i<lines;i++)
    {
        fscanf(f, "%d", &X[i]);
    }
    fclose(f);

    for (i=0;i<lines;i++)
        printf("%d\n",X[i]);
}

好,現在是第二個程序。

int main()
{
    int X[100];
    int *size;
  char *file = {"file.txt"};

  *size = 0;

  StoreinArray(X, size, file);
}
int StoreinArray(int X[], int *size, char *file)
{
  FILE *f;
  f = fopen(file, "r");
  if (f == NULL)
    return -1;
  *size = 0;
  while (!feof(f))
 {
    if (fscanf(f, "%d", &X[*size]) == 1)
      *size++;
  }
  fclose(f);
  return 1;
}

馬上, size變量將使程序崩潰。 這是一個未初始化的指針,因此它指向內存中的某個隨機位置。 當您嘗試將其寫入的內存略低時,它指向( *size = 0 ),這將崩潰,因為您很可能不會擁有該內存。 同樣,您實際上不需要在這里指向指針。 實際上,您根本不需要該變量。 如果您需要在主程序中知道StoreinArray()讀取了多少個整數,可以簡單地讓它返回它。

還有一個細微的問題-由於X數組的大小是固定的,因此您不能負擔得起從文件中讀取100個以上的整數。 如果這樣做,您將走出陣列,程序將崩潰。 或更糟糕的是-它不會崩潰,但是您將覆蓋屬於其他變量的內存。 奇怪的事情將會發生。 C是寬大的,它不會檢查您是否超出允許的范圍-但是,如果您這樣做,則所有賭注都無效。 我花了很多時間試圖找出導致程序異常的原因,卻發現某個完全不相關的地方的其他一些代碼超出了它的范圍,並給我的變量帶來了嚴重破壞。 這很難調試。 請非常小心C中的循環和數組。

實際上,這種錯誤-超出數組范圍-擁有自己的名稱:“緩沖區溢出”。 這也是一個非常常見的安全漏洞。 大型流行程序中的許多安全漏洞正是這個問題。

因此,最佳實踐是告訴StoreinArray()它最多可以在X數組中存儲100個整數。 讓我們這樣做:

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

#define MAX_X 100

int main()
{
    int X[MAX_X];
    char *file = "file.txt";
    int lines;

    lines = StoreinArray(X, MAX_X, file);
}
int StoreinArray(int X[], int maxLines, char *file)
{
    FILE *f;
    int lines;

    f = fopen(file, "r");
    if (f == NULL)
        return -1;

    while (!feof(f))
    {
        if (fscanf(f, "%d", &X[lines]) == 1)
            lines++;
        if (lines == maxLines)
            break;
    }
    fclose(f);
    return lines;
}

所以,你在那里。 這應該工作。 還有其他問題嗎? :)

暫無
暫無

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

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