C 中的 malloc,但使用多维数组语法
Posted
技术标签:
【中文标题】C 中的 malloc,但使用多维数组语法【英文标题】:malloc in C, but use multi-dimensional array syntax 【发布时间】:2011-03-09 19:38:01 【问题描述】:有没有办法 malloc 一个大数组,但用 2D 语法引用它?我想要类似的东西:
int *memory = (int *)malloc(sizeof(int)*400*200);
int MAGICVAR = ...;
MAGICVAR[20][10] = 3; //sets the (200*20 + 10)th element
更新:这很重要:我只想拥有一个连续的内存块。我只是不想写一个像这样的宏:
#define INDX(a,b) (a*200+b);
然后像这样引用我的 blob:
memory[INDX(a,b)];
我更喜欢:
memory[a][b];
更新:我知道编译器无法按原样知道。我愿意提供额外的信息,例如:
int *MAGICVAR[][200] = memory;
不存在这样的语法吗?请注意,我不只使用固定宽度数组的原因是它太大而无法放在堆栈上。
更新:好的,伙计们,我可以这样做:
void toldyou(char MAGICVAR[][286][5])
//use MAGICVAR
//from another function:
char *memory = (char *)malloc(sizeof(char)*1820*286*5);
fool(memory);
我收到警告,passing arg 1 of toldyou from incompatible pointer type
,但代码有效,并且我已验证可以访问相同的位置。有什么方法可以在不使用其他功能的情况下做到这一点?
【问题讨论】:
是的,这已经在 SO 上多次介绍过了,例如C Programming: malloc() for a 2D array (using pointer-to-pointer) er 抱歉,我应该声明我不想嵌套指针。我只想要一个连续的内存块。 发布我的答案后,我脑海中闪过“告诉你”的想法。我只是无法想象这一点语法糖是如何值得你跳过所有的箍来获得它;) @Cogwheel:呵呵,我猜不是,但我第一次发布这个问题时并不知道! 【参考方案1】:是的,你可以这样做,不,你不需要另一个指针数组,就像大多数其他答案告诉你的那样。您想要的调用只是:
int (*MAGICVAR)[200] = malloc(400 * sizeof *MAGICVAR);
MAGICVAR[20][10] = 3; // sets the (200*20 + 10)th element
如果你想声明一个返回这样一个指针的函数,你可以这样做:
int (*func(void))[200]
int (*MAGICVAR)[200] = malloc(400 * sizeof *MAGICVAR);
MAGICVAR[20][10] = 3;
return MAGICVAR;
或者使用typedef,这样会更清楚一点:
typedef int (*arrayptr)[200];
arrayptr function(void)
/* ... */
【讨论】:
啊,我知道这是可能的!拿那个,反对者...... @Tim:对不起,但我没有意识到你的解决方案达到了我想要的效果,caf 让它非常明显。 @Tim:是的,当我看到它时,我也赞成你的意见 - 但我想我还是留下我的答案,因为它似乎只有我们两个人反对这个世界;)跨度> @Claudiu:可能值得指出的是,函数参数声明中的foo[]
只是 (*foo)
的语法糖 - 只是 []
在实际变量声明中意味着不同的东西(它意味着一个数组,其大小由初始化器决定)。
你可以通过使用C99的VLA功能做更多的魔法! int (*MOREMAGICVAR)[b] = (int (*)[b]) malloc(a * b * sizeof(int));
@VaderB:是的,VLA 功能实际上只在这样的变量声明中有用。顺便说一句,您仍然可以使用a * sizeof MOREMAGICVAR[0]
公式来确定大小(即不要重复b
)。【参考方案2】:
使用指向数组的指针:
#include <stdio.h>
#include <stdlib.h>
int main()
int (*arr)[10];
arr = malloc(10*10*sizeof(int));
for (int i = 0; i < 10; i++)
for(int j = 0; j < 10; j++)
arr[i][j] = i*j;
for (int i = 0; i < 10; i++)
for(int j = 0; j < 10; j++)
printf("%d\n", arr[i][j]);
free(arr);
return 0;
【讨论】:
我如何在这里释放()内存?【参考方案3】:如果不需要额外的间接性,您可以使用指针数组。
编辑
这是@Platinum Azure 的答案的一个变体,它不会对 malloc 进行太多调用。除了更快的分配,所有元素都保证是连续的:
#define ROWS 400
#define COLS 200
int **memory = malloc(ROWS * sizeof(*memory));
int *arr = malloc(ROWS * COLS * sizeof(int));
int i;
for (i = 0; i < ROWS; ++i)
memory[i] = &arr[i * COLS];
memory[20][10] = 3;
【讨论】:
hmm 有趣...比我想要的设置多一点(这是 1 行特殊语法),但这可能是最接近的...指针数组。 哦,更少的mallocs?我喜欢。我应该注意这一点并自己使用它。 (+1) 这可行,但指向数组的指针更简单(不需要设置循环),并且正是 OP 所追求的。【参考方案4】:与 Cogwheel 的回答一样,这是一个(有点肮脏的)技巧,只调用一次 malloc()
:
#define ROWS 400
#define COLS 200
int** array = malloc(ROWS * sizeof(int*) + ROWS * COLS * sizeof(int));
int i;
for (i = 0; i < ROWS; ++i)
array[i] = (int*)(array + ROWS) + (i * COLS);
这会用指向紧随其后的连续数组数据中每一行的指针填充缓冲区的第一部分。
【讨论】:
即使在编译时两个维度的大小都不知道的情况下,这也具有工作的优势。另见:c-faq.com/aryptr/dynmuldimary.html @jamesdlin 从 C99 开始,当在编译时维度未知时,其他解决方案也可以工作;这样做的缺点是数组中的第一个条目(在指针表之后)可能没有正确对齐。【参考方案5】:#define ROWS 400
#define index_array_2d(a,i,j) (a)[(i)*ROWS + (j)]
...
index_array_2d( memory, 20, 10 ) = -1;
int x = index_array_2d( memory, 20, 10 );
编辑:
数组和指针看起来非常相似,但是编译器对它们的处理却大不相同。让我们看看数组索引和取消引用带有偏移量的指针需要做什么:
假设我们声明了一个静态数组(堆栈上的数组只是稍微复杂一点,与寄存器的固定偏移量,但本质上是一样的):
static int array[10];
还有一个指针:
static int* pointer;
然后我们按如下方式取消尊重:
x = array[i];
x = pointer[i];
需要注意的是array
的开头地址以及pointer
的地址(不是其内容)在链接/加载时固定。编译器然后执行以下操作:
-
对于
array
取消引用:
加载i
的值,
将其与array
的值相加,即其固定地址,形成目标内存地址,
从计算的地址加载值
对于pointer
取消引用:
加载i
的值,
加载pointer
的值,即其地址的内容,
将两个值相加形成有效地址
从计算的地址加载值。
2D 数组也是如此,需要加载第二个索引并将其乘以行大小(这是一个常数)。这一切都是在编译时决定的,在运行时没有办法用一个代替另一个。
编辑:
@caf 这里有正确的解决方案。毕竟语言中有一种合法的方法可以将指针索引为二维数组。
【讨论】:
是的,但是当我声明一个二维数组时,编译器足够聪明,可以在语法上做到这一点。有没有办法告诉它像这样对待单个指针? 正如铂金天青所说,编译器无法知道(阅读:没有办法告诉它)。 当你声明二维数组时,你必须告诉编译器外部维度——这就是它“足够聪明”的方式。如果没有这些信息,就无法弄清楚。 我知道,但为了方便起见,我愿意提供此信息。例如,我可以想象像“int *arrayptr[][200] = memory”这样的语法,然后它知道外部维度是什么。但我认为没有办法做到这一点? (也需要内部维度,而不是外部维度) @Nikolai: 没有办法为指针做这件事,除了我更新中的函数技巧=)【参考方案6】:编译器和运行时无法仅通过 malloc 调用中的乘法来了解您的预期维度容量。
您需要使用双指针来实现两个索引的功能。应该这样做:
#define ROWS 400
#define COLS 200
int **memory = malloc(ROWS * sizeof(*memory));
int i;
for (i = 0; i < ROWS; ++i)
memory[i] = malloc(COLS * sizeof(*memory[i]);
memory[20][10] = 3;
确保检查所有 malloc 返回值是否返回 NULL,表示内存分配失败。
【讨论】:
没错,但我能以某种方式告诉吗? C 中有很多事情可以告诉编译器去做,即使它可能知道你错了 =P。 在这种情况下,您可以只使用固定宽度的数组。 :-P @Platinum:错了,太大了!见***.com/questions/3144135/…。这就是为什么我必须首先使用 malloc 嗯,这就是我的意思:有时你就是不能拥有一切。尤其是低级语言。除非您使用编译时常量,否则您不能只将数组声明为固定宽度。如果你想要一些堆的好处,或者全部,你需要在堆和语言的约束下工作。我知道,这很糟糕,但你想要的在便携式 ANSI C 中是不可能的。 @Platinum:查看最新更新,当将内容传递给函数时,我可以让它工作。真的没有什么可以限制编译器这样做。似乎堆上的内存本质上与堆栈上的内存没有任何不同。但我可以看到可能不支持此特定功能。【参考方案7】:根据 Tim 和 caf 的答案,我将把它留在这里以供后代使用:
#include <stdio.h>
#include <stdlib.h>
void Test0()
int c, i, j, n, r;
int (*m)[ 3 ];
r = 2;
c = 3;
m = malloc( r * c * sizeof(int) );
for ( i = n = 0; i < r; ++i )
for ( j = 0; j < c; ++j )
m[ i ][ j ] = n++;
printf( "m[ %d ][ %d ] == %d\n", i, j, m[ i ][ j ] );
free( m );
void Test1( int r, int c )
int i, j, n;
int (*m)[ c ];
m = malloc( r * c * sizeof(int) );
for ( i = n = 0; i < r; ++i )
for ( j = 0; j < c; ++j )
m[ i ][ j ] = n++;
printf( "m[ %d ][ %d ] == %d\n", i, j, m[ i ][ j ] );
free( m );
void Test2( int r, int c )
int i, j, n;
typedef struct _M
int rows;
int cols;
int (*matrix)[ 0 ];
M;
M * m;
m = malloc( sizeof(M) + r * c * sizeof(int) );
m->rows = r;
m->cols = c;
int (*mp)[ m->cols ] = (int (*)[ m->cols ]) &m->matrix;
for ( i = n = 0; i < r; ++i )
for ( j = 0; j < c; ++j )
mp[ i ][ j ] = n++;
printf( "m->matrix[ %d ][ %d ] == %d\n", i, j, mp[ i ][ j ] );
free( m );
int main( int argc, const char * argv[] )
int cols, rows;
rows = 2;
cols = 3;
Test0();
Test1( rows, cols );
Test2( rows, cols );
return 0;
【讨论】:
【参考方案8】:int** memory = malloc(sizeof(*memory)*400);
for (int i=0 ; i < 400 ; i++)
memory[i] = malloc(sizeof(int)*200);
【讨论】:
两件事......(1)我认为这是倒退;memory
的第一个维度中的每个索引增量将跳跃 400,而 OP 指定为 200。 (2) 您应该将代码语句放在单独的行中,并在每行的开头使用四个空格缩进以创建 固定宽度的环境。
以上是关于C 中的 malloc,但使用多维数组语法的主要内容,如果未能解决你的问题,请参考以下文章