【问题标题】:Dynamic memory and pointers arguments动态内存和指针参数
【发布时间】:2018-03-26 22:21:33
【问题描述】:

我有这两个函数,目的是做同样的事情——从一个只有整数的文件中读取每一行并将它们存储在一个数组中:

我在 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 Code 崩溃。

所以我尝试做*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); 
   }

【问题讨论】:

  • 请在发帖前检查您的问题。
  • 这两个函数都有几个错误。我会说你只是很幸运其中一个没有崩溃。但是,首先,您能否展示完整的程序?不仅是这两个函数,还有调用它们的代码。
  • 另外,您的真正问题是什么?您在询问一些无关紧要的微小细节,而代码中存在明显的明显错误。我猜你有一个家庭作业,你无法开始工作,需要帮​​助,因为程序一直在崩溃。你真正的问题是My program keeps crashing and doesn't do what I want it to do. What is my mistake?我说得对吗?如果是这样,那么还请提供作业的全文,以便我们知道代码应该做什么。
  • 只需要读取一个文件,其中每一行都是一个数字,然后将它们存储在 X 数组中。它不适用于我的老师要我们使用的功能,我不明白为什么。我用的是第一个。

标签: c function pointers


【解决方案1】:

您的程序的第二个版本的问题是在 main 中声明了 size。将其声明为 int,而不是指向 int 的指针。您当前的程序正在崩溃,因为您没有为 size 分配任何空间,并且当 StoreInArray 尝试更新它时,您遇到了访问冲突。所以,main 应该是这样的:

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

    size = 0;

    StoreinArray(X, &size, file);
}

【讨论】:

    【解决方案2】:

    好的,我会尽力了解它并解释所有我能找到的东西。

    首先,我们需要谈谈变量、指针和内存,因为您似乎对这些概念没有很牢靠的把握。一旦点击,其余的应该很容易。

    首先,简单的变量。这部分很简单,我想你或多或少都明白这一点。

    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个字节,因此它可以以字节数120121122和@结尾987654329@.

    单个变量(或单个数组)中的所有字节将在 RAM 中彼此相邻。两个不同的变量可能会出现在 RAM 的另一端,但每个变量中的字节会在一起。

    现在我们来了解指针的概念。指针基本上只是一个整数变量。它包含某个其他变量的第一个字节的 RAM 编号。我们来看一个例子:

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

    假设变量i 存储在字节数200...203(即4 个字节)中。在这些字节中,我们有值42。然后假设变量p 存储在字节数300...303 中(即另外4 个字节)。嗯,这 4 个字节将包含值 200,因为这是 i 变量的第一个字节。

    这也是程序员说“&lt;variable&gt;的(内存)地址”或“指向&lt;variable&gt;的指针”的意思。这是&lt;variable&gt;的RAM中第一个字节的编号。因为同一个变量粘在一起,然后通过知道第一个字节(并知道变量的类型),您可以找出 &lt;variable&gt; 的其余部分在内存中的位置。

    现在让我们在示例中再添加一行:

    *p = 5;
    

    在这种情况下,计算机所做的是获取存储在p 中的地址,转到内存中的该位置,将后面的 4 个字节视为整数,并将值 5 放在那里。由于我们之前已将p 设置为“指向”i 的地址,这与简单地设置i 变量本身的效果相同。

    好的,你明白了吗?这有点棘手,通常需要一段时间才能理解它。如有必要,请随意重新阅读它多次以理解它。你需要它继续前进。

    准备好了吗?好的,我们来谈谈堆栈和动态内存。

    当程序启动时,操作系统会自动为它分配一点内存,只是为了让它更容易启动。它就像一个大字节数组,都在内存中。现在它通常约为 1MB,但它可能会有所不同。这个内存被称为“堆栈”。为什么这么叫?呃,我改天再解释。

    无论如何,当您的 main() 函数启动时,操作系统会显示“来,我的好伙伴,指向堆栈的指针。您可以随意使用它!祝您有美好的一天!”

    然后您的main() 函数使用它来存储您在其中创建的所有变量。因此,当您使用 p = &amp;i; 时,您存储在 p 中的地址就在堆栈中的某个位置。

    现在当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;
    }
    

    那么,你来了。这应该有效。还有问题吗? :)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-07-12
      • 2015-02-05
      • 2015-06-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多