我有两个函数,意思是做同样的事情 - 从只有整数的文件读取每一行并将它们存储在一个数组上:
我在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;
}
对于 First ,我使用了动态内存分配和实际计算的大小:
X = malloc(0);
while ((ch = fgetc(f)) != EOF)
{
if (ch == '\n')
lines++;
}
size = &lines;
对于第二,我不能这样做。我尝试时Visual Studio代码崩溃。
所以我尝试*size = 0
然后StoreinArray2(X, size, f);
,但它也不起作用。
所以我的问题是关于第二个功能:
在扫描文件时是否计算尺寸?据说没有必要使用动态内存分配(我的老师说)。
如果是,那么我怎样才能正确传递一些“大小”参数?作为指针还是只是一个简单的整数?
提前谢谢!
编辑:
这是完整的第一个程序:
#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);
}
答案 0 :(得分:1)
程序第二版的问题是main中size
的声明。将其声明为int,而不是指向int的指针。您当前的程序崩溃是因为您没有为大小分配任何空间,而当StoreInArray尝试更新它时,您会遇到访问冲突。所以,main应该是这样的:
int main()
{
int X[100];
int size;
char *file = {"file.txt"};
size = 0;
StoreinArray(X, &size, file);
}
答案 1 :(得分:0)
好的,我会尝试解决它并解释我能找到的所有事情。
首先,我们需要谈论变量,指针和记忆,因为你似乎并没有非常牢固地掌握这些概念。一旦点击,其余的应该很容易跟随。
首先,简单的变量。那部分很容易,我想你或多或少都明白这一点。
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就像一个字节的huuuge数组。那里有字节编号0
,字节编号1
等。int
变量占用4个字节,因此它可以以字节数{{1}结尾},120
,121
和122
。
单个变量(或单个数组)中的所有字节将在RAM中彼此相邻。两个不同的变量最终会出现在RAM的两端,但每个变量中的字节将在一起。
现在我们来看一个指针的概念。指针基本上只是一个整数变量。它包含某个其他变量的第一个字节的RAM编号。让我们看一个例子:
123
假设变量int i = 42;
int *p = &i;
存储在字节数i
... 200
中(即4个字节)。在这些字节中,我们有值203
。然后让我们假设变量42
存储在字节数p
... 300
中(另外4个字节)。好吧,这4个字节将包含值303
,因为它是200
变量的第一个字节。
这也是程序员在说i
&#34;&#34;(内存)地址时的意思。或&#34;指向<variable>
的指针。它是<variable>
的RAM中第一个字节的编号。由于同一变量的所有字节都粘在一起,因此通过了解第一个字节(并知道变量的类型),您可以找出其余<variable>
在内存中的位置。
现在让我们在示例中添加一行:
<variable>
在这种情况下,计算机执行的操作是将*p = 5;
中存储的地址转到内存中的该位置,将以下4个字节视为整数,并将值p
放入那里。由于我们之前已将5
设置为&#34;指向&#34;在p
的地址,这与仅设置i
变量本身具有相同的效果。
好的,你得到了所有这些吗?这有点棘手,通常需要一段时间才能绕过它。您可以根据需要多次重新阅读它以便理解它。你需要它继续前进。
准备好了吗?好吧,让我们谈谈堆栈和动态内存。
程序启动时,操作系统会自动为其分配一些内存,以便更容易启动。它就像一大块字节,都在内存中。现在它通常大约1MB,但它可以变化。这个记忆被称为&#34; The Stack&#34;。为什么叫这个?呃,我会在其他时间解释一下。
无论如何,当你的i
函数启动时,操作系统会像#34;在这里你是我的好伙伴,指向The Stack的指针。您可以按照自己的意愿使用它!祝你有愉快的一天!&#34;
然后您的main()
函数会使用它来存储您在其中生成的所有变量。因此,当您转到main()
时,您存储在p = &i;
中的地址就在堆栈内。
现在当p
调用另一个函数(例如main()
)时,它还会给它一个指向堆栈的指针,然后说“好”,这里是指向堆栈的指针。小心点,我已经使用了它的前XXX个字节,但随意使用其余的&#34;。
然后StoreinArray()
使用堆栈将变量放在那里。当StoreinArray()
调用其他内容时,它会执行相同的操作,并且一直打开。
现在,有几点需要注意:
所以,对于这些情况,你使用&#34; dynamic&#34;记忆。在C中,这主要意味着StoreinArray()
。你告诉malloc()
你需要多少字节的内存,malloc()
在RAM中找到一个足够大的无人认领空间,将其标记为已使用,并为你提供一个指针。嗯,无论如何,这是对事物的简化看法。
当你事先不知道你需要多少记忆时,同样的方法也有效。
缺点是,当您完成内存时需要malloc()
内存,或者可能内存不足,然后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()
函数内发生变化。当函数返回时,X
函数中的变量main()
仍将具有其旧值。您可能尝试使用reallocate()
舞蹈解决此问题,但这不是它的工作原理。更糟糕的是 - 在realloc()
之后,无论X
曾经是什么价值,都不再是一个有效的指针,因为realloc()
释放了那个旧记忆!如果您之后尝试使用X
函数中的main()
变量执行任何操作,则您的程序将崩溃。
让我们看看你的程序如何看待我提出的更改(再加上一些小的美容调整):
#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中的循环和数组。
事实上,这种错误 - 在数组之外 - 有它自己的名字:a&#34; Buffer Overrun&#34;。它也是一种非常常见的安全漏洞。大型流行程序中的许多安全漏洞正是这个问题。
因此,最佳做法是告诉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;
}
所以,你有。这应该工作。还有其他问题吗? :)