为啥我在 C 中的多维动态分配不起作用?

Posted

技术标签:

【中文标题】为啥我在 C 中的多维动态分配不起作用?【英文标题】:Why is my multi-dimensional dynamic allocation in C not working?为什么我在 C 中的多维动态分配不起作用? 【发布时间】:2011-03-21 06:30:24 【问题描述】:

我一直试图找出我在 C 中分配和使用多维动态分配数组的问题。我非常感谢任何帮助。

我尝试了两种方法。第一个:

cdr = (double ***) malloc(NUM_REGIONS * sizeof(double **));
for(i=0; i<NUM_REGIONS; i++)
   cdr[i] = (double **) malloc(numRatings * sizeof(double *));
   for(j=0; j<numRatings; j++)
       cdr[i][j] = (double *) malloc(remQuarters * sizeof(double));
   
  

第二个:

tempPtr1 = (double *) malloc(NUM_REGIONS * numRatings * remQuarters * sizeof(double) );
tempPtr2 = (double **) malloc (NUM_REGIONS * numRatings * sizeof(double *));
cdr = (double ***) malloc(NUM_REGIONS * sizeof(double **));
for(i=0; i< NUM_REGIONS; i++)
    cdr[i] = tempPtr2 + i;
    for(j=0; j < numRatings; j++) cdr[i][j] = tempPtr1 + i * NUM_REGIONS + j;

两者都不起作用。在这两种情况下,每个 cdr[i] 最终都指向同一个地方。我第一次进入“i”循环时,所有 cdr[i](即 cdr[0]、cdr[1]、cdr[2] 等)都设置为相同的值。随后的循环不会改变它们中的任何一个。

我怀疑运算符优先级有问题,或者我解引用错误,但我无法弄清楚。

谢谢。

更新

我整理了以下简化的代码,似乎可以正常工作。但是,虽然输出完全符合预期,但当我在调试器中单步执行时,我仍然会遇到同样的奇怪行为。我开始认为我的代码的根本问题可能在其他地方,而我刚刚被调试器的问题转移(或者可能只是因为我对输出的误解)。是否有已知原因导致 Visual Studio 中“cdr[0]”、“cdr[1]”等的手表无法显示我期望的内容?

#include "stdafx.h"
#include "stdio.h"
#include "stdlib.h"

#define NUM_REGIONS 50


void printArray(double *inVec, int len)
    int i;
    for(i=0; i<len; i++) printf("%f\t",inVec[i]);
    printf("\n");


int main(array<System::String ^> ^args)

    int numRatings = 25, remQuarters = 100, i, j, k;
    double ***cdr;
    char dummy;

    cdr = (double ***) malloc(NUM_REGIONS * sizeof(double **)); 
    for(i=0; i<NUM_REGIONS; i++) 
        cdr[i] = (double **) malloc(numRatings * sizeof(double *)); 
        for(j=0; j<numRatings; j++) 
            cdr[i][j] = (double *) malloc(remQuarters * sizeof(double)); 
         
    

    for(i=0; i<NUM_REGIONS; i++)
        for(j=0; j<numRatings; j++)
            for(k=0; k<remQuarters; k++)
                cdr[i][j][k] = 100*i + 10*j +k;
            
        
    

    for(i=0; i<5; i++) printf("%f\t",cdr[1][1][i]);
    printf("\n");
    for(i=0; i<5; i++) printf("%f\t",cdr[3][1][i]);
    printf("\n");
    for(i=0; i<5; i++) printf("%f\t",cdr[1][3][i]);
    printf("\n");
    for(i=0; i<5; i++) printf("%f\t",cdr[i][i][i]);
    printf("\n");
    printArray(cdr[1][1], 5);
    printArray(cdr[3][3], 5);

    scanf("%c", &dummy);
    return 0;

再次感谢所有反馈。

【问题讨论】:

我试过你的第一个 sn-p。它和我一起工作。而且 cdr[i] 每次都是一个新位置,没有任何问题。 我的猜测是:你的 numRatings 变量等于 0 或未初始化 @stacker, cdr 被声明为 double ***cdr;声明中还有一些其他变量,但这不重要,不是吗? @Ben,numRatings 等于 27。在调试器中显示良好。 @Mark 因为它被声明为***指针,所以编译器无法找出你的第一个索引的行有多大,因此 ssegiv 答案是要走的路。 您确定这不能正常工作吗?你看到了什么地址?你确定地址完全一样吗?您的第一种方法似乎是有效的,我的测试证实了这一点。 【参考方案1】:

很久以前在大学时,我得出结论,应该用一维数组来模拟 C 中的多维数组。第一个需要分配一个足够大的缓冲区来容纳所有元素。对于将是ncolumns*nrows*sizeof(element) 的二维数组。然后通过将多维索引转换为一维索引来访问数组元素。对于二维数组,访问 A(i,j) 将转换为 bufA[i*ncolumns+j]

【讨论】:

这确实发生在我身上,可能就是我最终要走的路。我只是认为数组表示法更容易阅读和理解。但我想只有当它有效时才重要!【参考方案2】:

问题在于您的取消引用。 cdr[i][j] 不会做你想做的事。当您分配cdr[i] 时,您将在该索引处放置一个指针。但是,cdr[i][j] 不会取消引用 cdr[i]。它假定您有一个矩形内存块,并将适当的值添加到 cdr 以确定cdr[i][j] 的位置。您可能必须使用 *s 手动解除引用。

【讨论】:

?!?我一直使用这种技术来制作参差不齐的数组。 它的行为完全符合预期。在这种情况下,cdr[i][j] != cdr[i*rowlen+j]。在问题中,cdr 的类型为 double ***。这使得cdr[i] 的类型为double **,而cdr[i][j] 的类型为double *。语法正确。【参考方案3】:

我很难弄清楚你到底遇到了什么问题。

我为一个多维双精度数组尝试了以下方法并且没有问题:

int i, j;
int rows = 5;
int columns = 10;
int z_axis = 5;
double ***cdr = malloc(rows * sizeof(double **));
for (i = 0; i < rows; i++)

  cdr[i] = malloc(columns * sizeof(double *));
  for (j = 0; j < columns; j++)
  
    cdr[i][j] = malloc(z_axis * sizeof(double));
  

有关此问题的详细信息,请参阅 c-faq:http://c-faq.com/aryptr/dynmuldimary.html

【讨论】:

【参考方案4】:

cdr 应该是什么?一个双精度的 3D 数组(这是我假设的,基于你的 sn-p)?指向双精度指针的二维数组?还是指向二维双精度数组的指针?

假设第一个:

#include <stdlib.h>

int main(void)

  double ***cdr = malloc(sizeof *cdr * NUM_REGIONS);
  if (cdr)
  
    size_t i;
    for (i = 0; i < NUM_REGIONS; i++)
    
      cdr[i] = malloc(sizeof *cdr[i] * numRatings);
      if (cdr[i])
      
        cdr[i][j] = malloc(sizeof *cdr[i][j] * remQuarters);
      
    
  
  return 0;

需要注意的几点:

    不要转换malloc() 的返回值。从 C89 开始,您不需要(void 指针被隐式转换为目标指针类型),如果您忘记包含 stdlib.h 或在范围内没有 malloc() 的原型,则强制转换将抑制有用的“分配的不兼容类型”诊断。 对正在分配的对象使用sizeof 运算符,而不是类型表达式。如果目标对象的类型发生变化,这可以保护您;我认为它也有助于提高可读性。

这应该有效;你应该有一个相当于

的双精度 3D 数组
double cdr[NUM_REGIONS][numRatings][remQuarters];

【讨论】:

我可以看到更多的上下文会有所帮助。我不清楚 double 的 3D 数组与指向 double 的 2D 指针数组有何不同。我试图获得两者的特征。最终,我试图发送cdr[region][rating] 作为指向函数的双倍指针。该函数将double *cdr1 作为参数,然后将所有remquarters 值作为cdr1[i] 循环。那有意义吗?另外,感谢您提供的一般“良好的内务管理”说明。 是的,这是有道理的。通过简单地将 cdr 声明为 3D VLA(如果您有可用的 C99 编译器;如果没有,您将被分段分配卡住),您可能会省去一些麻烦。请记住,在大多数情况下,数组表达式的类型从“T 的 M 元素数组”“衰减”到“指向 T 的指针”。如果你将cdr声明为一个3D数组,比如double cdr[X][Y][Z];,那么表达式cdr[i][j]的类型就是“Z-element array of double”,但是当你把它传递给一个函数时,它会“衰减”输入“双倍指针”。【参考方案5】:

事实证明,所编写的代码是有效的(尽管正如一些人指出的那样,它确实有改进风格的空间)。我将代码中的真正问题跟踪到一些对于缓冲区来说太大的文件输入(这对我来说是错误检查的一个教训)。

调试器给出了一些奇怪的输出,让我专注于这部分代码。我仍然不明白为什么我的调试器会在其他人没有的情况下这样做,但我会改天处理。

【讨论】:

以上是关于为啥我在 C 中的多维动态分配不起作用?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我在 vue.js 中的自定义搜索过滤器不起作用?

为啥动态组件创建中的属性绑定不起作用?

为啥是 Ctrl+.当我将它绑定到 Emacs 中的命令时不起作用?

为啥我在 codeigniter 中的分页不起作用? (404 未找到)

为啥从函数调用分配给属性 innerHTML 不起作用?

为啥我在 paintComponent 中的 gif 动画不起作用?