【问题标题】:Correct way to construct a 2D array from user input从用户输入构造二维数组的正确方法
【发布时间】:2017-08-01 05:40:13
【问题描述】:

我知道有很多属于“多维数组”家族的问题。由于我找不到能解决我问题的具体问题,所以我终于要问了。

目标:存储一个二维数组,其值由用户输入。


Method-01:对数组使用任意大的初始大小。

代码

int method_arbit()
{
 int n, m;
 int i, j;
 float myarray[100][100];

 printf("Enter the number of rows: ");
 scanf("%d", &m);

 printf("Enter the number of columns: ");
 scanf("%d", &n);

 for(i=0; i<m; i++)
 {
     for(j=0; j<n; j++)
         scanf("%f", &myarray[i][j]);
 }

 for(i=0; i<m; i++)
 {
     for(j=0; j<n; j++)
         printf("[%d][%d] = %f", i, j, myarray[i][j]);
 }
}

评论:此功能按预期工作!


Method-02:使用动态内存分配。

代码

int method_dynamic()
{
 int n, m;
 int i, j;
 float **myarray; // m x n matrix

 printf("Enter the number of rows: ");
 scanf("%d", &m);

 printf("Enter the number of columns: ");
 scanf("%d", &n);

 myarray = malloc(m*sizeof(float*));
 for(i=0; i<m; i++)
     myarray[m] = malloc(n*sizeof(float));

 for(i=0; i<m; i++)
 {
     for(j=0; j<n; j++)
         scanf("%f", &myarray[i][j]);
 }

 for(i=0; i<m; i++)
 {
     for(j=0; j<n; j++)
         printf("[%d][%d] = %f", i, j, myarray[i][j]);
}
}

评论:此方法在接受输入时会给出分段错误。

Q1:我很难调试这个。此外,我发现更难理解这种行为背后的原因。 我对指针和动态内存分配有基本的了解。 我希望能详细解释我所犯的错误以及我可能忽略的概念。

Q2:如果我们在编译时不知道数组的大小,是否应该总是动态分配数组?

什么时候最好使用动态分配的数组? 我知道的一个用例是在处理函数并从它们返回数组时。

【问题讨论】:

  • 第二段代码中有一个错字:当您在循环中执行malloc 时,您将超出myarray[m] = malloc(...) 的范围。这里,m 应该是i,就是这样。
  • 请注意,第二种方法不是动态分配二维数组。
  • 当数组维度大小被限制为较小的值时,像float myarray[8][8]; 这样的最坏情况最大大小数组是可以的,否则分配。

标签: c arrays segmentation-fault malloc


【解决方案1】:

OP的问题是使用了错误的索引@forcebru

 myarray = malloc(m*sizeof(float*));
 assert(myarray); // Do some allocation check;
 for(i=0; i<m; i++) {
     //         v----- here
     // myarray[m] = malloc(n*sizeof(float));
     myarray[i] = malloc(n*sizeof(float));   
     assert(myarray[i]);
 }

【讨论】:

    【解决方案2】:

    您可以将多维数组声明为大数组。

     int n, m;
     int i, j;
     float* myarray; // m x n matrix
    
     std::cout<<"Enter the number of rows: \n";
     scanf("%d", &m);
    
     std::cout << "Enter the number of columns: \n";
     scanf("%d", &n);
    
     myarray = (float*)malloc( m * n * sizeof(float) );
    
     for(i=0; i<m; i++)
     {
         for (j = 0; j<n; j++) {
             scanf("%f", &(myarray[i * n  + j]));
         }
     }
    
     for(i=0; i<m; i++)
     {
         for(j=0; j<n; j++) {
             printf("[%d][%d] = %f", i, j, (myarray[i * n  + j]));
         }
     }
    

    【讨论】:

      【解决方案3】:

      Q1:你需要一块内存来保存一个二维浮点数组,你的初始代码

      float **myarray;
      myarray = malloc( m * sizeof(float*) );
      
      m = 3;   // for this example
      n = 5;   // for this example
      

      是个问题,因为您的两个维度是 mn。您没有在那里使用 n。此外,您正在做sizeof(float*) 并且您不希望指针的大小浮动。发生的情况是,如果 sizeof( pointer to float) = { 8 bytes on a 64-bit system, 4 bytes on 32-bit system} 那么你的 malloc 分别保留 24 或 12 个字节。但是当你真的需要 m x n 个单位时,只有 m 个内存单位{15 个单位用于 15 个唯一值,它们是浮点类型} .

      所以你想要单个 float 在内存中占用的大小。因此你应该这样做

      float *myarray;  // not a double pointer
      myarray = (float *) malloc( m * n * sizeof( float ) );
      

      例如,如果 sizeof(float) = 4 字节,那么如果 m=3 且 n=5,那么您将获得 4x15 = 60 字节的内存分配。 malloc 返回一个指向那块内存的指针,然后你将它的类型转换为你声明的指向它的指针。

      此时,您可以对 60 个字节的总块进行索引,其中*myarray 指向它的开头,仅此而已。除了指向它开头的指针myarray 并且它是一个浮点数据类型之外,编译器对如何安排或使用该内存块一无所知。当您使用 myarray[][] 约定时,malloc 或编译器无法知道您尝试使用的行或列大小 mn。 p>

      在您的系统上,如果 sizeof(float) 为 4,则该 60 字节块中的每 4 个字节可以是一个浮点值,为您提供 15 个值。

      如果你想使用约定 myarray[i][j] 你不能,因为你使用了 malloc 并且它不知道 60 字节的内存块是如何划分的。 您将不得不手动计算 [row][col] 索引到 myarray 中,例如

      m = 3;   // max rows
      n = 5;   // max columns
      
      row = 2;  // value entered by user, must be < m
      col = 3   // value entered by user, must be < n
      
      *(myarray + (row*n) + col) = 1.2345;
      

      Q2: 是的,我认为如果你不知道你需要的内存大小,最好编写好的代码来获取所有用户输入,然后弄清楚你需要的内存大小'会需要。然后使用 ma​​lloccalloc 来分配该内存,如果不成功 {system didn't have enough} 然后您处理该条件并相应地编写代码。唯一的缺点是如果你编写的代码重复调用 malloc 或 calloc,然后释放它并多次重复这个过程,这会产生很多开销。

      【讨论】:

        【解决方案4】:

        你永远不会“真正地”这样做。让用户通过标准输入输入 2D 矩阵对于学习 C 的人来说是一个教学玩具问题。该练习的缺点之一是没有真正好的答案,有一些方法可以做到,比如创建一个超大尺寸缓冲区,或重新分配内存以增长,或在输入变量之前向用户查询宽度和高度。但没有什么是完全令人满意的。

        实际上,数据很可能以 CSV 等文件格式出现。解析一个 CSV 文件是相当困难的,但并不难到任何有能力的程序员都不应该能够实现它。通常的方法是将结构读入内存结构,然后“解析解析器的输出”以将其放入平面二维数组(或结构的一维数组,在 CSV 文件中很常见)。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2017-08-27
          • 1970-01-01
          • 1970-01-01
          • 2012-11-03
          • 2021-12-07
          • 1970-01-01
          • 2020-11-25
          • 2019-04-06
          相关资源
          最近更新 更多