2014-01-27 6 views
7

Я недавно начал программировать C просто для удовольствия. Я очень опытный программист в C# .NET и Java в сфере настольных компьютеров, но для меня это становится слишком сложной задачей.Возвращение двумерного массива в C?

Я пытаюсь сделать что-то как «простое», возвращая двумерный массив из функции. Я пробовал исследовать в Интернете для этого, но мне было трудно найти что-то, что сработало.

Вот что у меня есть. Он не совсем возвращает массив, он просто заполняет его. Но даже это не скомпилируется (я уверен, что причины должны быть очевидны для вас, если вы квалифицированный программист C).

void new_array (int x[n][n]) { 
    int i,o; 

    for (i=0; i<n; i++) { 
     for (o=0; o<n; o++) { 
     x[i][o]=(rand() % n)-n/2; 
     } 
    } 

    return x; 
} 

И использование:

int x[n][n]; 
new_array(x); 

Что я делаю неправильно? Следует отметить, что n является константой, которая имеет значение 3.

Edit: Вот ошибка компиляции при попытке определить константу: http://i.imgur.com/sa4JkXs.png

+1

Я всегда хотел слушайте, как C является вызовом опытному программисту, не использующему программирование. :) – this

+0

Да. Иногда мне жаль, что я не пришел из мира программирования низкого уровня к высокоуровневому. Но, черт возьми, я пытаюсь это сделать сейчас! : D –

+1

Что вы подразумеваете под «константой»? –

ответ

5

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

исключением случаев, когда это операнд оператора sizeof или унарный &, или строковый литерал используется для инициализации другого массива в Объявление выражение типа «N-элементный массив T» будет преобразовано («распад») в выражение типа «указатель на T», а значение выражения будет адресом первого элемента массив. Этот результат не является значением lvalue; он не может быть целью присвоения, а также не может быть операндом для операторов или --.

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

Верьте или нет, есть твердая техническая причина для этого; когда он изначально разрабатывал C, Деннис Ритчи заимствовал много понятий с языка программирования B. Б был «беспричинным» языком; все было сохранено как неподписанное слово или «ячейка». Память рассматривалась как линейный массив «ячеек». Когда вы объявили массив как

auto arr[N]; 

В будет выделено N «ячеек» для содержимого массива, наряду с дополнительной ячейки, связанной с arr, чтобы сохранить смещение первого элемента (в основном указатель, но без каких-либо тип семантики).Доступ к массивам был определен как *(arr+i); вы смели i ячейки с адреса, хранящегося в a, и разыменовали результат. Это отлично поработало для C, пока Ritchie не начал добавлять типы структур к языку. Он хотел, чтобы содержимое структуры не только описывало данные в абстрактных терминах, но и физически представляло биты. Пример, который он использовал что-то вроде

struct { 
    int node; 
    char name[14]; 
}; 

Он хотел выделить 2 байта для узла, сразу за которым следует 14 байт для элемента имени. И он хотел, чтобы массив таких структур был выложен таким образом, что у вас было 2 байта, за которыми следуют 14 байт, за которыми следуют 2 байта, за которыми следуют 14 байтов и т. Д. Он не мог найти хороший способ справиться с указателем массива, поэтому он полностью избавился от него. Вместо того, чтобы выделять хранилище для указателя, C просто вычисляет его из самого выражения массива. Вот почему вы не можете назначить что-либо для выражения массива; нет ничего, чтобы назначить значение на.

Итак, как вы возвращаете 2D-массив из функции?

У вас нет. Вы можете вернуть указатель к 2D массива, например:

T (*func1(int rows))[N] 
{ 
    T (*ap)[N] = malloc(sizeof *ap * rows); 
    return ap; 
} 

Недостатком этого подхода является то, что N должны быть известны во время компиляции.

Если вы используете C99 компилятор или C2011 компилятор, который поддерживает массивы переменной длины, вы могли бы сделать что-то вроде следующего:

void func2(size_t rows, size_t cols, int (**app)[cols]) 
{ 
    *app = malloc(sizeof **app * rows); 
    (*app)[i][j] = ...;     // the parens are necessary 
    ... 
} 

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

#define COLS ... 
... 
void func3(size_t rows, int (**app)[COLS]) 
{ 
    *app = malloc(sizeof **app * rows); 
    (*app)[i][j] = ...; 
} 

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

int **func4(size_t rows, size_t cols) 
{ 
    int **p = malloc(sizeof *p * rows); 
    if (p) 
    { 
    for (size_t i = 0; i < rows; i++) 
    { 
     p[i] = malloc(sizeof *p[i] * cols); 
    } 
    } 
    return p; 
} 

p является не массив; он указывает на ряд указателей на int. Для всех практических целей, вы можете использовать это, как если бы это была 2D массив:

int **arr = foo(rows, cols); 
... 
arr[i][j] = ...; 
printf("value = %d\n", arr[k][l]); 

Обратите внимание, что C не имеет никакого сбора мусора; вы отвечаете за очистку своих беспорядков. В первых трех случаях, это просто:

int (*arr1)[N] = func(rows); 
// use arr[i][j]; 
... 
free(arr1); 

int (*arr2)[cols]; 
func2(rows, cols, &arr2); 
... 
free(arr2); 

int (*arr3)[N]; 
func3(rows, &arr3); 
... 
free(arr3); 

В последнем случае, так как вы сделали выделение в два этапа, что вам нужно сделать два шага Deallocation:

int **arr4 = func4(rows, cols); 
... 
for (i = 0; i < rows; i++) 
    free(arr4[i]) 
free(arr4) 
4

вашей функция возврат void, поэтому return x; линии является излишней. Кроме того, ваш код выглядит отлично. То есть, если у вас есть где-то, а не что-то вроде const int n = 3;.

+0

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

+0

Я определил его как'const int n = 3'. В чем проблема, и каковы различия между этими двумя типами констант? –

+0

Ваш тип не является постоянным. 'const' не означает« константа », это означает« вы не можете изменить эту переменную ». Я уверен, что в C FAQ есть предложения по этому вопросу. Сорт '# define' просто выполняет обычную текстовую замену, поэтому все ваши' n 'просто заменяются буквальными' 3 's. –

1

Вы, вероятно, объявляя n как постоянное целое:

const int n = 3; 

Вместо этого, вы должны определить n как определение препроцессора:

#define n 3 
4

Вы не можете вернуть массив в C, многомерное или иным образом.

Основная причина в том, что язык говорит, что вы не можете. Другая причина заключается в том, что обычно локальные массивы выделяются в стеке и, следовательно, освобождаются при возврате функции, поэтому нет смысла возвращать их.

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

2

В C есть только проход/возврат по значению (без прохождения по ссылке). Таким образом, единственный способ передачи массива (по значению) - передать его адрес функции, чтобы он мог манипулировать им с помощью указателя.

Однако, возвращаясь по значению, адрес массива невозможен, так как с помощью контроля времени до вызова вызывающей функции функция выходит за пределы области действия, и ее автоматические переменные тоже сходят с нее. Следовательно, если вам действительно нужно, вы можете динамически распределять массив, заполнять и возвращать его, но предпочтительный метод - передать массив и оставлять бремя поддержки массива вызывающему.

Что касается ошибки, единственным предупреждением, которое я получаю в GCC, является warning: 'return' with a value, in function returning void, что просто означает, что вы ничего не должны возвращать из функции void.

void new_array (int x[n][n]); то, что вы на самом деле делаете здесь, является указателем на массив из n целых чисел; разложившийся тип - int (*x)[n]. Это происходит потому, что в целом arrays decay into pointers. Если вы знаете n во время компиляции, возможно, лучший способ передать это:

#define n 3 
void new_array (int (*x)[n][n]) { 
    int i,o; 

    for (i=0; i<n; i++) { 
    for (o=0; o<n; o++) { 
     x[i][o]=(rand() % n)-n/2; 
    } 
    } 
} 

И называют его

int arr[n][n]; 
new_array(&arr); 
3

Для возврата (указатель) на вновь созданный массив размеров известных в время компиляции, вы можете сделать это:

#define n 10 // Or other size. 

int (*new_array(void))[n] 
{ 
    int (*x)[n] = malloc(n * sizeof *x); 
    if (!result) 
     HandleErrorHere; 

    for (int i = 0; i < n; ++i) 
     for (int o = 0; i < n; ++o) 
      x[i][o] = InitialValues; 

    return x; 
} 

… 
// In the calling function: 
int (*x)[n] = new_array(); 

… 
// When done with the array: 
free(x); 

Если размер не известен во время компиляции, вы не можете даже возвращать указатель на массив. C поддерживает массивы переменной длины, но не в возвращаемых типах функций. Вместо этого вы можете вернуть указатель на массив переменной длины через параметр. Для этого требуется использовать параметр, который является указателем на указатель на массив переменной длины, поэтому он становится несколько грязным.

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

+0

К сожалению, OP запросил n * n массив, а не массив * b, поэтому ответ 'n' в вашем ответе не разрешает, если он соответствует а или b. +1 в любом случае. – chux

2

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

#include <stdio.h> 

#define n 3 

struct S { 
    int a[n][n]; 
}; 


static struct S make_s(void) 
{ 
    struct S s; 

    int i, j; 
    for (i = 0; i < n; i++) { 
    for (j = 0; j < n; j++) 
     s.a[i][j] = i + j; 
    } 

    return s; 
} 

static void print_s(struct S s) 
{ 
    int i, j; 
    for (i = 0; i < n; i++) { 
    for (j = 0; j < n; j++) 
     printf(" %d", s.a[i][j]); 
    printf("\n"); 
    } 
} 

int main(void) { 
    struct S s; 

    s = make_s(); 
    print_s(s); 

    return 0; 
} 
Смежные вопросы