[英]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.