[英]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個字節,因此它可能,例如,以字節數結束120
, 121
, 122
和123
。
單個變量(或單個數組)中的所有字節在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;
}
這里有兩點可以改進。 首先- size
和lines
變量。 兩者都不需要。 特別是因為您將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.