在C中,我想声明一个n维数组,如下面的3-dim。
printf("please insert n1, n2, n3\n");
scanf("%d %d %d", &n1, &n2, &n3);
int ***nda;
nda = (int ***)malloc(n1*sizeof(int **));
for (i = 0; i < n1; i++) {
nda[i] = malloc(n2*sizeof(int *));
for (j = 0; j < n2; j++) {
nda[i][j] = malloc(n3*sizeof(int));
}
}
我想使用宏自动化这个过程并扩展到n-dim,我发现我需要声明一个这样的指针:
type *...*(n times)typename;
我认为宏是一种看似可能的方法,但是在这里搜索答案后,我发现宏不会递归扩展。
在C中是否有任何变通方法?
答案 0 :(得分:3)
停止做你现在做的事!指针间接的每个级别都会为您的程序带来显着的性能提升。你在那里创建的不是一个多维数组,它是一个n-ary树,其中每个数组元素都是一个指向多个分支的指针,它们再次展开。
如果要为n维数据创建数组,请执行以下操作:
size_t dim[...]
int *nda;
nda = malloc(sizeof(int) * dim[0] * dim[1] * … * dim[n]);
要解决数组中的元素,请使用此
nda[ dim[0]*(i[1] + dim[1]*(i[2] + dim[…]*(i[…+1])) + i[0] ];
答案 1 :(得分:1)
尽管这样做是个坏主意,可以完成,所以这是一种方式。
如果使用强大的元编程库(如Order(如上所述,Boost是另一种可能的候选者),您实际上可以在预处理器中循环并定义递归宏。订单允许您以熟悉Scheme或ML的任何人熟悉的功能方式进行编程。
要循环,请使用for_each
构造。要简单地创建一定数量的内容,您可以for_each_in_range
使用1, N+1
:
ORDER_PP( // within this block Order code runs
8for_each_in_range(8fn(8_, 8print( (*) ) ),
1, 8)
)
以上将打印7颗星。您可以将元编程块包装在常规宏中,这遵循正常的预处理器规则:
// print COUNT stars
#define STARS(COUNT) ORDER_PP( \
8for_each_in_range(8fn(8_, 8print((*)) ), 1, 8plus(COUNT, 1)) \
)
在ORDER_PP
块中,假设所有内容都是订单代码而不是C预处理器代码,这意味着只能调用已识别的订单函数(并且所有值/预处理令牌必须是原始整数或“引用” “使用8(val)
构造)。要将stars
定义为Order函数而不是CPP宏,以便可以在ORDER_PP
内作为嵌套表达式的一部分进行调用,我们需要像这样编写它:
#define ORDER_PP_DEF_8stars ORDER_PP_FN( \
8fn(8C, 8for_each_in_range(8fn(8_, 8print((*)) ), 1, 8plus(8C, 1)) ))
ORDER_PP( 8stars(7) ) // prints 7 stars
Order提供完全透明的递归,因此编写嵌套的初始化器循环相对简单:
#define ORDER_PP_DEF_8ndim_init ORDER_PP_FN( \
8fn(8N, 8T, 8C, 8I, 8D, \
8do( \
8print( 8N (=malloc) 8lparen 8seq_head(8D) (*sizeof) 8lparen 8T 8stars(8minus(8C, 1)) 8rparen 8rparen (;) ), \
8if(8equal(8C, 1), \
8print(((void)0;)), \
8do( \
8print( (for) 8lparen (int) 8I (=0;) 8I (<) 8seq_head(8D) (;) 8I (++) 8rparen ({) ), \
8ndim_init(8adjoin(8N, 8([), 8I, 8(])), 8T, 8minus(8C, 1), 8cat(8I, 8(_K)), 8seq_tail(8D)), \
8print( (}) ) \
)))))
像这样调用ndim_init
:
// print the nested initializer from the question
ORDER_PP(
8ndim_init(8(nda), 8(int), 3, 8(i), 8seq(8(n1), 8(n2), 8(n3)))
)
请注意,当nda
块中出现时,需要引用C变量名称(i
,ORDER_PP
等),以便Order将它们视为文本,而不是尝试评估他们。最后一个参数是包含每个维度大小的运行时变量列表(8seq
构建列表,8
再次引用C变量名称。)
您可以将ndim_init
的调用打包到常规预处理器宏中以便于访问,就像第一个带有STARS
的示例一样;您可以通过这种方式轻松地将它与声明器宏结合起来,在一次调用中发出声明和初始化:
#define NDIM(NAME, TYPE, ...) ORDER_PP ( \
8lets( (8D, 8((__VA_ARGS__))) \
(8C, 8tuple_size(8D)), \
8do( \
8print( (TYPE) 8stars(8C) (NAME; {) ), \
8ndim_init(8(NAME), 8(TYPE), 8C, 8(_ITER), 8tuple_to_seq(8D)), \
8print( (}) ) \
)) \
)
NDIM(nda, int, n1, n2, n3) // emits declaration and init block for int ***nda
如果以上看起来并不简单......那就是人们说你不应该这样做的原因。 (如果它确实对您有好处,那么其他任何人都无法阅读您的代码。)
答案 2 :(得分:0)
这是一个可变长度数组的解决方案(需要C99或更新版本)。在这种情况下,由于其潜在的大小,VLA不会在堆栈上分配,而是手动使用(m / c)alloc():
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_DIM_C 10
#define MAX_DIM_SIZE 100
// Works for versions of C from C99 onward.
int main(void) {
int dim_c;
do {
printf("Please input the number of dimensions. "
"The minimum value is 1. The maximum value is %d.\n",
MAX_DIM_C);
} while (scanf("%d", &dim_c) != 1 || dim_c < 1 || dim_c > 100);
int dim[MAX_DIM_C];
// Give all dimensions a default size of 1.
memset(dim, 1, MAX_DIM_C);
for (int i = 0; i < dim_c; i++) {
do {
printf("Please input the size of dimension %d. "
"The minimum value is 1. The maximum value is %d.\n",
i, MAX_DIM_SIZE);
} while (scanf("%d", dim + i) != 1 || dim[i] < 1 || dim[i] > 100);
}
// Always allocate a MAX_DIM_C-dimensional array. When the user specifies
// a number of dimensions fewer than MAX_DIM_C, the MAX_DIM_C-dim_c
// dimensions are basically just dummy dimensions of size 1
int (*vla)[dim[1]]
[dim[2]]
[dim[3]]
[dim[4]]
[dim[5]]
[dim[6]]
[dim[7]]
[dim[8]]
[dim[9]] =
calloc(dim[0] *
dim[1] *
dim[2] *
dim[3] *
dim[4] *
dim[5] *
dim[6] *
dim[7] *
dim[8] *
dim[9], sizeof(int));
//
// Do something useful here
//
printf("%d\n", vla[dim[0] - 1]
[dim[1] - 1]
[dim[2] - 1]
[dim[3] - 1]
[dim[4] - 1]
[dim[5] - 1]
[dim[6] - 1]
[dim[7] - 1]
[dim[8] - 1]
[dim[9] - 1]);
// To pass the VLA to another function cast it to void (or another simple
// type) and also pass the dim array along with it to generate a new
// VLA pointer in the called function with matching dimensions
return 0;
}