2013-08-16 3 views
3

В C, я хочу объявить n-мерный массив, как следующие 3-мерные.объявить n-указатель в C

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)); 
    } 
} 

Я хочу, чтобы автоматизировать этот процесс с помощью макросов и расширения п-тусклым, и я обнаружил, что мне нужно объявить указатель, как это:

type *...*(n times)typename; 

Я думаю макрос, казалось бы, возможно, способ сделать это, но после поиска ответов здесь я обнаружил, что макрос не рекурсивно расширяется.
Есть ли способы обхода этого в C?

+2

Поскольку код скомпилирован, что такое использование для TypeName во время выполнения? – Jiminion

+10

Что-то подсказывает, что проблема здесь не в объявлении указателя на n-уровни косвенности. Скорее всего, это будет * почему * вы хотите сделать это в первую очередь. – WhozCraig

+0

Извините за путаницу, я изменил вопрос. – user2690457

ответ

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 код препроцессор, который означает только признанные функции Order можно назвать (и все значения/токены предварительной обработки должны быть либо сырыми ints, либо «кавычками» с конструкцией 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((})) \ 
       ))))) 

Invoke 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))) 
) 

Обратите внимание, что имена переменных C (, i и т. Д.) Необходимо указывать при появлении в блоке ORDER_PP, чтобы Заказ обрабатывал их как текст, вместо того, чтобы их оценивать. Последний аргумент - это список переменных времени выполнения, которые будут содержать размеры для каждого измерения (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 

More Order examples


Если выше, похоже, не просто вообще .. поэтому люди говорят, что вы не должны этого делать. (Если это так, хорошо для вас, никто не сможет читать ваш код.)

3

Прекратите делать то, что вы делаете прямо сейчас! Каждый уровень направленности указателя добавляет значительный успех в вашу программу. То, что вы создаете там, не является многомерным массивом, это 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] ]; 
+0

Если использовать функцию C99 - это вариант - и в 2014 году я бы предположил, что это ... вы должны использовать 'VLA' malloc() 'ed и ... угадайте, что? Получите стандартную индексацию без какого-либо из этих ручных индексов, вычисляющих крутящий момент! – Will

+0

@Will: Это работает только до сих пор, не углубляясь в трудно читаемый синтаксический рывок. Вы не можете накладывать трансляцию в VLA; вам нужно написать что-то вроде 'void foo (size_t dim_a, size_t dim_b, данных типа element [dim_a] [dim_b])', чтобы получить область памяти с malloc-ed в форму VLA. Это не очень гибко, и «крут», как вы его называете, не очень-то трудно работать. Это идиоматично, и как только вы сделали это достаточно времени, вы просто «прочитали» его, не разбирая каждое подзадачу (точно так же, как вы не читаете эти строки по символу). – datenwolf

+0

Да, я согласен, что если вам нужно передать ссылку VLA на другие функции (или нужен гибкий доступ к нескольким «представлениям» массива), то недостатки могут перевесить преимущества подхода VLA. Тем не менее, я добавил свой подход в качестве альтернативного ответа на вопрос OP. Не стесняйтесь разорвать его (если он заслуживает разрыва, по крайней мере :). – Will

0

Вот решение с переменной длиной массивы (требуется C99 или новее). В этом случае, из-за его РоКо размера, VLA не выделяется в стеке, но вручную (м/с) 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; 
} 
Смежные вопросы