繁体   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