2013-03-25 4 views
1

Проблема: Предположим, вы пытаетесь написать функцию в C, которая заполнит 2D-массив значениями из файла. Файл содержит значения, расположенные в строках (записях), где каждая строка содержит несколько полей. Функция должна принимать указатель на 2D-массив и адрес файла и заполнять массив. Важно отметить, что функция должна работать независимо от количества полей на каждой записи. Например, в одной программе можно вызвать функцию, чтобы считывать значения из файла, в котором Есть четыре поля на запись:Можете ли вы передать многомерные массивы в функцию C в качестве указателей, а затем отбросить их обратно в массивы внутри функции?

int array_of_values[MAX_NUMBER_OF_RECORDS][4]; 
fill_in_array(array_of_values, "spacetime.csv"); 

В другой программе, вы можете заполнить значения, когда есть одиннадцать полей в записи :

int array_of_values[MAX_NUMBER_OF_RECORDS][11]; 
fill_in_array(array_of_values, "M-theory.csv"); 

к сожалению, если вы попытаетесь сделать это, вы ссориться пути C обрабатывает многомерные массивы. Многомерные массивы не реализованы в C как массивы указателей на массивы, а вместо этого как один длинный одномерный массив. Это означает, что функция должна знать ширину массива, чтобы читать данные из него.

Таким образом, следующее определение функции будет выдавать ошибку:

void fill_in_array(int array_of_values[MAX_NUMBER_OF_RECORDS][], char *path) 

[Обратите внимание, что следующий будет в порядке:

void fill_in_array(int array_of_values[][MAX_NUMBER_OF_RECORDS], char *path) 

, так как компилятор не нужно знать индекс для но предположим, что это недопустимо (например, если функция должна обходиться с отдельными записями, такими как array_of_values[1]).]

Это t он указывает, что я достиг в своей программе. Существуют два решения:

  1. Заставить функцию работать с фиксированным количеством полей. Я бы предпочел не делать этого, но я мог бы, например, объявить константу MAX_NUMBER_OF_FIELDS и оставить неиспользуемые поля пустыми.
  2. Выполнение функции fill_in_array выполняется в указателе, а не в массиве, и динамически выделяет вектор Iliffe, содержащий эти поля. Это привлекательная идея (так как это помешает нам объявить максимальное количество записей/полей, но это также означало бы, что нам нужно было бы создать (и не забудьте использовать!) Функцию для освобождения массива полей.

у меня есть одна другая идея, что это изменить объявление функции на следующее:.

void fill_in_array(int **array_of_values, int number_of_fields, char *path) 

(Здесь number_of_fields относится к числу полей в записи, так что мы могли бы назвать его как fill_in_array(array_of_values, 4, "spacetime.csv");.

Обратите внимание, что параметр array_of_values больше не является явным массивом, а является указателем. Обычно, если вы назначаете двунаправленный указатель на 2D-массив, результат не имеет смысла. Моя идея состоит в том, что можно было бы использовать параметр number_of_fields, чтобы функция умела справляться с выражениями типа array_of_values[i][j].

В принципе, это должно быть довольно легко: в самом деле, если a является 2D массив, то a[i][j] определяется как

*(a + (i * n) + j) 

где n длина массива, так что мы могли бы заменить все вхождения array_of_values[i][j] с *(array_of_values + (i * number_of_fields) + j), и каждый случай array_of_values[i] с array_of_values + (i * number_of_fields). Однако этот код очень трудно прочитать. Есть ли способ сообщить компилятору, что ширина массива равна number_of_fields, чтобы я мог использовать нотацию индекса для доступа к элементам массива?

+0

'Многомерные массивы не реализованы в C как массивы массивов, а как один длинный одномерный массив »(a) Нет, они не являются, и (б) я не понимаю различия. –

+0

Извините, что не ясны. Я имел в виду: «Массивы массивов» = вектор Iliffe (т. Е. Одномерный массив, элементами которого являются адреса одномерных массивов, доступ к элементам осуществляется по формуле «a [i] [j] = * (* (a + i) + j) ') - это НЕ, как они реализованы в C; 'Один длинный одномерный массив' = одномерный массив из m * n элементов, где m, n - размеры массива; Элементы доступны как 'a [i] [j] = * (a + (i * n) + j)' - это то, как они реализованы в C, что означает, что компилятор должен знать значение 'n' , –

+0

Итак, вы хотели сказать, что это «массивы указателей на массивы», что является самой другой (и более низкой) вещью. –

ответ

1

Существует несколько решений.

Используйте-структуру:

typedef struct { 
    // whatever appears in a record 
} record_t 

void fill_in_array(record_t records[MAX_NUMBER_OF_RECORDS], const char* path); 

Обратите внимание, что это имеет смысл только в случае, если размер записи известен во время компиляции, что данный ваш пример, это не может быть.

Используйте шаг:

void fill_in_array(int *array_of_values, int stride, const char *path) 
{ 
    #define IDX(x, y) (x + (y * stride)) 

    // get the val at i,j 
    int val = array_of_values[IDX(i,j)]; 

    #undef IDX 
} 

Вы предложили этот подход в своей функции с number_of_fields, которая является шагом, однако шаг это термин, который другие разработчики, глядя на ваш код, скорее всего, признают.

Один несовершеннолетний не связан пункт, если вы не меняете содержимое path, вы должны сделать это const :)

+0

ОК спасибо - как вы можете видеть из моего вопроса, я размышлял о добавлении шага (хотя я не знал, что это было то, что он назывался). Это то, что я собираюсь делать, так как я предпочел бы не думать о том, как создаются записи. –

+0

Хорошая практика кодирования для использования '# define's внутри функций, хотя? Я был воспитан только для использования в заголовочных файлах. –

+0

Вы можете назвать свой макрос чем-то немного отличающимся, чтобы убедиться, что вы не получили столкновение имен (IDX немного короток, но это SO-ответ, а не производственный код: P). Вы должны включить соответствующий undef, я обновляю свой код сейчас :) –

3

Нет, такого пути нет.

Как только вам нужно будет иметь общий адресный расчет, вам нужно его реализовать самостоятельно.

Поздравляем вас с решением о добавлении явного параметра, который описывает количество полей на запись, это, безусловно, как это сделать.

Вы можете использовать макрос внутри функции, чтобы упростить управление адресами.

+0

OK - спасибо за помощь. –

0

Что вы ищете существует в C++, но не в C, я считаю. В C++ вы можете определить функции шаблона для работы с массивами размера, известных во время компиляции, а компилятор позаботится об остальном. В C, существует два подхода:

  • Определить размер явно
    Это касается функций, как memcpy, где вы указываете количество элементов

    void process_array(int *data[], size_t max_x, size_t max_y) 
    .... 
    
  • Определите размер, используя неправильный номер
    Это функции таких как strlen, где данные заканчиваются определенным значением ('\0' здесь)
    Итак, если вы хотите иметь функции с матрицей, но с переменным числом элементов, вы должны определить способ указания этого в данных.

    #define ARRAY_TERM -1 
    
    void process_array(int *data[]) 
    { 
        size_t i, j; 
        for (i = 0; data[i]; i++) 
        { 
         for (j = 0; data[i][j] != ARRAY_TERM; j++) 
         { 
          ... 
         } 
        } 
    } 
    ... 
    

Надеюсь у вас есть идея. Не очень удобно использовать.

Существует другой подход: определите свой собственный тип. Да, это является жизнеспособным вариантом во многих случаях:

typedef struct array *array_t; 
struct array 
{ 
    size_t max_x, max_y; 
    int *data; 
}; 

Базовый набор функций для работы с ним:

int array_init(array_t *a; size_t max_x, size_t max_y) 
{ 
    array_t res; 
    res = malloc(sizeof(*res)); 
    res->max_x = max_x; 
    res->max_y = max_y; 
    res->data = calloc(max_x * max_y, sizeof(int)); 
    *a = res; 
    return 0; 
} 

void array_destroy(array_t *a) 
{ 
    free((*a)->data); 
    free(*a); 
} 

И тогда вы можете определить дополнительные функции для работы.

0

Если вы не ограничены C89 (т.е. компилятор MSVC), вы можете передавать многомерные массивы вокруг, как это:

#include <stdio.h> 

void fill_in_array(size_t m, size_t n, int array_of_values[m][n]) 
{ 
    for (size_t i = 0; i < m; ++i) { 
    for (size_t j = 0; j < n; ++j) { 
     array_of_values[i][j] = ((i == j) ? 1 : 0); 
    } 
    } 
} 

void print_array(size_t m, size_t n, int array_of_values[m][n]) 
{ 
    for (size_t i = 0; i < m; ++i) { 
    for (size_t j = 0; j < n; ++j) { 
     printf(" %d", array_of_values[i][j]); 
    } 
    printf("\n"); 
    } 
} 

int main() 
{ 
    { 
    int array_of_values[2][4]; 
    fill_in_array(2, 4, array_of_values); 
    print_array(2, 4, array_of_values); 
    } 
    { 
    size_t h = 6, w = 5; 
    int array_of_values[h][w]; 
    fill_in_array(h, w, array_of_values); 
    print_array(h, w, array_of_values); 
    } 
} 
+0

Это очень здорово, что вы можете сделать это на C99, но я бы предпочел сохранить свой код переносимым. –

+0

@ Donkey_2009: Ну, это переносится ... везде, кроме MSVC. – han

+0

К сожалению, MSVC - это именно тот компилятор, который я использую. –