【问题标题】:Mallocing a 3 dimensions array分配一个 3 维数组
【发布时间】:2015-12-13 14:57:31
【问题描述】:

我必须使用 3 维数组,因为我想将图片分成正方形并将每个正方形的 RGB 平均值存储在我的数组中。 我想要这个尺寸,tab[height][width][3], 所以我这样做了:

i = 0; j = 0; k = 0;

    float*** tab;
    tab = malloc((hauteur+1)*sizeof(float*));

    while(i <= hauteur){
        tab[i] = malloc((largeur+1)*sizeof(float**) );
        i++;
    }
    i = 0;
    while(i <= hauteur){
        j = 0;
        while (j <= largeur){
            tab[i][j] = malloc(3*sizeof(float***));
            j++;
        }
        i++;
    }

但我在 :tab[1][30][2]; 之后出现了段错误。

我的malloc 有问题吗?

这很奇怪,因为当我声明 tab 时它不会出现段错误: tab[hauteur][largeur][3].

(抱歉:“hauteur”在法语中的意思是“高度”,“largeur”的意思是“宽度”。)

(如果您认为需要检查我的整个函数:http://pastebin.com/eqQXz8Ad;它是 JPEG 文件的编写器。)

【问题讨论】:

  • 请使用for 循环来保持我们的理智,如果不是为了你的! while 循环将控制代码分散在 3 行中; for 循环将其全部压缩到一行中。使用for 循环避免麻烦要容易得多。
  • 你正在初始化一些变量两次。所以也请考虑一下。
  • 对于最里面的数组,需要分配3 * sizeof(float);对于中间分配,你应该分配(largeur + 1) * sizeof(float *);对于最外层(单个)分配,您需要分配(hauteur + 1) * sizeof(float **)。碰巧的是,您不会在 32 位或 64 位机器上遇到问题——尽管您会为最里面的数组分配两倍所需的空间。我不确定你为什么在里面有+ 1,但这意味着你可以索引tab[hauteur][longeur][2] 而不会出现下标错误。通常,您使用for (i = 0; i &lt; hauteur; i++)&lt; 而不是&lt;=)。
  • 除了@JonathanLeffler 所写的内容之外,如果您坚持始终引用分配给大小的指针的 malloc 最佳实践,则可以避免此错误。示例:tab = malloc(count * sizeof(*tab));*tab 始终是正确的类型,无论它声明了多少间接。下一个是tab[i] = malloc(count * sizeof (*tab[i])); 等。
  • 使用合适的工具来完成这项工作。 for (i = 0; i &lt; size; ++i) {...}。请注意,&lt; 不是 &lt;=。背诵这个模式,这样你就可以在凌晨 4 点 30 分在酒吧爬行后在一个陌生的地方醒来时背诵它。尝试向自己证明您确实需要&lt; 而不是&lt;=,并探索如果您使用&lt;= 会发生什么。

标签: c malloc libjpeg


【解决方案1】:

malloc 调用中的类型不正确。我建议如下:

tab = malloc( (hauteur + 1) * sizeof *tab );       // sizeof (float **)
tab[i] = malloc( (largeur + 1) * sizeof *tab[i] ); // sizeof (float *)
tab[i][j] = malloc( 3 * sizeof *tab[i][j] );       // sizeof (float)

鉴于tab的声明,以下都是正确的:

Expression                Type
----------                ----
       tab                float ***
      *tab                float **
    tab[i]                float **
   *tab[i]                float *
 tab[i][j]                float *
*tab[i][j]                float

我通常建议使用目标表达式的sizeof,而不是显式类型;这样,如果您更改了 tab 的类型(例如,从 float 更改为 double),您就不必接触 malloc 调用。

【讨论】:

  • 目前尚不清楚此修复是否会在必要时解决问题。在大多数平台上sizeof(anytype*)==sizeof(anyothertype*)sizeof(anytype*)&gt;=sizeof(float)。代码可能会分配比需要更多的内存。这并不能解释崩溃。
  • 是的,我替换了这个,它在之前的段错误行发生了段错误......
【解决方案2】:

您正在制作的基本上是一个指向浮点数的数组的数组,而不是一个浮点数的三维数组。 你可能想看看这个Ambiguity in 2d array declaration in C。它用二维数组解决了类似的问题。

也许您的问题的解决方案可能如下所示:

float (*tab)[hauteur][largeur][3];    //declare a pointer to a real array

tab = malloc(hauteur * largeur * 3 * sizeof(float));    //Allocate room for threedimensional array

for (int i=0; i<hauteur; i++)
    for (int j=0; j<largeur; j++)
        for (int k=0; k<3; k++)
        {
            (*tab)[i][j][k] = (float)((i*100)+j*1000+k);    //Fill elements with something using threedimensional subscripting
        }
for (int i=0; i<hauteur; i++)
    for (int j=0; j<largeur; j++)
        for (int k=0; k<3; k++)
        {
            printf("[%d][%d][%d]=%f\n", i, j, k, (*tab)[i][j][k]);    //Check back data...
        }

已编辑 查看 cmets 我发现使用指向数组符号 (*array)[a][b]...[n] 的指针访问数组在某种程度上是“不自然的”,即使此符号在声明中明确报告了整个维度。为了使使用更友好,您可以使用以下允许众所周知的格式的表格:

#include <stdio.h>
#include <stdlib.h>

int largeur = 10;
int hauteur = 10;

int main(int argc, char *argv[])
{
    float (*tab)[largeur][3];    //declare a bidimensional array of pointers to our variable
                               //this fools the compiler acting as a one more dimension array of variable

    tab = malloc(hauteur * largeur * 3 * sizeof(float));    //Allocate room for threedimensional array

    for (int i=0; i<hauteur; i++)
        for (int j=0; j<largeur; j++)
            for (int k=0; k<3; k++)
            {
                tab[i][j][k] = (float)((i*100)+j*1000+k);    //Fill elements with something using threedimensional subscripting
                //This use the natural addressing...
            }
    for (int i=0; i<hauteur; i++)
        for (int j=0; j<largeur; j++)
            for (int k=0; k<3; k++)
            {
                printf("[%d][%d][%d]=%f\n", i, j, k, tab[i][j][k]);    //Check back data...
            }
}

这种技巧之所以奏效,是因为C语言中缺少多维数组的概念。
C 只知道数组,或者更好的是它应该是 string 的东西,所以二维数组只是一些东西的数组的数组。如果我们再增加一个维度,它就是一个数组的数组,它是一个数组的数组……等等,我们每增加一个维度。
这定义了 C 中数组的内存布局以及编译器用来访问数据的寻址方法。因为数据在内存中流式传输以访问特定下标处的值,所以编译器需要计算除第一个维度之外的所有维度所使用的空间。

+++ 我修复了一个错误,现在该示例可以在任何符合 C99-C11 的编译器下编译并且可以正常工作。

【讨论】:

  • 当我这样做时会出现奇怪的编译错误image.noelshack.com/fichiers/2015/50/1450028563-screen-3.png 它是否与 tab[][][] 兼容?
  • 不,tab 是一个指向三维数组的指针。您必须像在我的示例中一样使用它作为(*tab)[][][]。您可以使用宏来简化#define TabArray (*tab),然后以TabArray[][][] 访问数据。但是您也可以将tab 定义为float *tab[largeur][3];,并按照我的回答中所述进行分配。通过这种方式,您可以更自然地以tab[][][] 访问数据。
  • 哦,我认为我做错了什么,所以我只是将我的 while 代码替换为您的(带有 for 的),它说:“ecrire.c:114:15:错误:无效类型参数一元''(有'float')”但它通过在第一个malloc上用“tab*”替换“tab”来工作
  • 当我使用定义或 (*tab)[][][] 形式时,情况更糟(我认为起初我忘记了代码中的某些内容,所以它给了我这些错误屏幕截图)如果我使用您的解决方案,我是否必须更改代码中的某些内容?
  • 很难说没有看到整个代码,这是一项艰巨的工作。我认为问题在于在函数调用中使用变量的声明。请使用我的第二个示例,这样一切都应该更加熟悉和简单。
【解决方案3】:

如果您非常在意效率,您应该考虑使用维数组(如Frankie_C's answer):

 int height = something_to_compute_height();
 int width = something_to_compute_width();
 double *arr = malloc(height*width*3*sizeof(double));
 if (!arr) { perror("malloc"); exit(EXIT_FAILURE); }

(顺便说一句,即使我们都是以法语为母语的人,让我们在这里尝试在问题和代码中使用英语)

那么你可以定义一个宏来简化arr中的某些元素

#define ELEM_ARR(a,i,j,k) a[i*width*height+j*width+k]
#define ARR(i,j,k) ELEM_ARR(arr)

(你有想法,细节可以不同)

这可能比指向指针数组的指针数组更有效,因为 cache 局部性并且您只需要一次分配。

仔细选择row-major 或列主要访问权限以适应最频繁的访问模式。

如果heightwidth 特定于每个数组,您可以在struct 中使用一些flexible array member 作为最后一个。

根据经验,只有当所有维度(可能最后一个维度除外)都是编译时常量时,才在 C 中使用多维数组很有用,例如double arr3d[3][4][5];;否则,最好有一个单维数组,自己做索引计算。

顺便说一句,如果您非常关心性能,您可能会担心OpenCLOpenMP(并且两者通常都更喜欢一维数组)。

【讨论】:

  • 哦,是的,我在缓冲区中使用了它,但它更喜欢为此使用 3 维数组
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-11-18
  • 1970-01-01
  • 2021-02-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-03
相关资源
最近更新 更多