#arrays #c #function
#c
Вопрос:
Как я могу создать функцию, которая возвращает массив? Я попробовал это
const int WIDTH=11;
const int HEIGHT=11;
int main() {
char A[WIDTH][HEIGHT];
A=rand_grid(WIDTH,HEIGHT);
return 0;
}
// Initializes a random board.
char[][] rand_grid(int i, int k) {
char* A[i][k];
for(j=0;j<i; j) {
for(l=0;l<k; l) {
A[j][l]=ran(10);
}
}
return A;
}
// Returns a random number from the set {0,...,9}.
int ran(int i) {
srand((unsigned int) time(0));
return(rand()%10);
}
Комментарии:
1. Не вызывайте srand() более одного раза в своей программе: это излишне замедлит работу вашей программы и, что наиболее важно, снизит случайность функции rand().
Ответ №1:
Следует отметить несколько моментов.
Прежде всего, вы не можете назначить объект массива, как вы делаете здесь:
char A[WIDTH][HEIGHT];
A=rand_grid(WIDTH,HEIGHT);
Объекты типа array не могут быть изменены.
Во-вторых, функции в C не могут возвращать типы массивов. Они могут возвращать указатели на массивы, хотя:
char (*foo(int width))[HEIGHT]
{
/**
* dynamically allocate memory for a widthxHEIGHT array of char
*/
char (*newArr)[HEIGHT] = malloc(sizeof *newArr * width);
/**
* initialize array contents here
*/
return newArr;
}
Синтаксис немного сбивает с толку; он читается как
foo -- foo
foo(int width) -- is a function
-- taking an int parameter
*foo(int width) -- returning a pointer
(*foo(int width))[HEIGHT] -- to a HEIGHT-element array
char (*foo(int width))[HEIGHT] -- of char
Для C89 ВЫСОТА в приведенном выше фрагменте должна быть постоянным целочисленным выражением времени компиляции (либо макросом, числовым литералом, либо арифметическим выражением, состоящим из макросов и / или числовых литералов). Я не уверен, верно ли это и для C99.
Основываясь на опубликованном вами фрагменте, вам нужно взять уже выделенный массив и инициализировать его содержимое. Помните, что в большинстве контекстов выражение типа массива будет неявно преобразовано в указатель на базовый тип. Итак, если вы передаете N-элементный массив T функции, то фактически функция получает указатель на T:
void foo (T *p) {...}
...
T arr[N];
foo(arr);
Для 2-d массивов это немного уродливее:
void foo (T (*p)[M]) {...}
...
T arr[N][M];
foo(arr);
Это также зависит от того, что M известно во время компиляции, что ограничивает полезность функции. Вам нужна функция, которая может обрабатывать двумерный массив произвольного размера. Лучший известный мне способ добиться этого — вместо передачи указателя на массив передать адрес первого элемента в массиве [1] и передать количество строк и столбцов в качестве отдельных параметров:
void foo(T *base, size_t rows, size_t cols) {...}
...
T arr[N][M];
foo (amp;arr[0][0], N, M);
Итак, ваша функция rand_grid будет выглядеть примерно так:
void rand_grid(char *base, size_t rows, size_t cols)
{
size_t i, j;
for (i = 0; i < rows; i )
{
for (j = 0; j < cols; j )
{
/**
* Since base is a simple char *, we must index it
* as though it points to a 1-d array. This works if
* base points to the first element of a 2-d array,
* since multi-dimensional arrays are contiguous.
*/
base[i*cols j] = initial_value();
}
}
}
int main(void)
{
char A[WIDTH][HEIGHT];
rand_grid(amp;A[0][0], WIDTH, HEIGHT);
...
}
- Несмотря на то, что выражения
amp;A[0][0]
иA
выдают одно и то же значение (базовый адрес A), типы двух выражений различны. Первое выражение вычисляется как простой указатель на char (char *
), в то время как второе вычисляется как указатель на 2-d массив char (char (*)[HEIGHT]
) .
Комментарии:
1. Этот ответ удивительно познавателен. Спасибо!
2. Должна ли функция C , возвращающая массив, использовать аналогичный подход? (немного выходит за рамки вопроса, который я знаю, но недостаточно отличается, чтобы задать новый)
Ответ №2:
Вы не можете. Вы можете либо передать указатель на массив в качестве параметра и заставить функцию изменить его, либо сама функция может выделять данные и возвращать указатель.
в вашем случае
void rand_grid(char A[WIDTH][HEIGHT]) {
A[0][0] = 'A'; // or whatever you intend to do
}
main() {
char A[WIDTH][HEIGHT];
rand_grid(A);
}
Редактировать: как указал caf, на самом деле можно вернуть struct
с массивом в нем, но, конечно, ни один программист на c в здравом уме этого не сделает.
Комментарии:
1. На самом деле вы можете вернуть массив по значению, заключив его в a
struct
, но это очень плохая идея.2. Спасибо. Мне было интересно, как решить следующую проблему: мне нужна сетка, заполненная случайными целыми числами. Я хочу, чтобы инициализация массива не выполнялась в основной функции, поскольку это было бы полезно в остальной части программы. Как я могу использовать указатели для решения проблемы?
3. Jaska, ты должен сделать этот подвопрос отдельным, то есть новым вопросом о stackoverflow.
4. При условии, что размеры массива заданы во время компиляции, вы можете использовать фрагмент кода из моего ответа. Фактически это передача указателя.
5. Почему возвращать массив внутри структуры — плохая идея? Упаковка небольших массивов в структуры не является чем-то необычным. Вероятно, это быстрее и, безусловно, проще, чем выделение памяти и возврат указателя на нее.
Ответ №3:
Вы никогда не сможете вернуть выделенную стеком (« auto
«) переменную чего-либо другого, кроме примитивного (value) типа, и struct
s такого. Для других типов вам необходимо выделить память из кучи, используя malloc()
или обернуть массив (фиксированного размера) в a struct
.
Если вы используете массив фиксированного размера, вы можете смоделировать его как a struct
и использовать struct-return:
#define WIDTH 11
#define HEIGHT 11
typedef struct {
unsigned char cell[WIDTH * HEIGHT];
} Board;
Board board_new(void)
{
Board b;
size_t i;
for(i = 0; i < sizeof b.cell / sizeof *b.cell; i )
b.cell[i] = rand() amp; 255;
return b;
}
Это нормально и не должно быть более дорогостоящим, чем альтернатива использования явного указателя:
void board_init(Board *b);
Поскольку первый случай struct-return может быть переписан (компилятором) во второй. Это называется оптимизацией возвращаемого значения.
Комментарии:
1. Это просто неверно — вы можете возвращать
struct
типы, которые явно не являются примитивными. Однако это не очень хорошая идея.2. Но типы структур будут скопированы из стека в (скрытую) локальную переменную рядом с вызывающим объектом. Это могло бы сработать, если бы массив был определен как фиксированный размер, чтобы компилятор знал размер возвращаемой структуры / массива. Но это не так. Это динамический массив…
3. malloc() — не единственный способ выделения памяти. Внутри функции может быть статическая переменная
4. Алекс, нет требования к динамическому размеру массива.
5. размотайте, я голосую против вас за использование слова «best» там, где это более чем неуместно 😉 «лучшим, вероятно, было бы смоделировать его как структуру». Я верну свой понижающий голос, если вы измените свою формулировку;-)
Ответ №4:
Если вы действительно хотите это сделать, вы можете попробовать сделать массив статическим, таким образом, хранилище для A не определяется областью действия функции, и вы действительно можете вернуть массив (в виде указателя, конечно).
Но это не очень хороший способ добиться того, чего вы пытаетесь достичь, вместо этого передайте массив функции rand_grid
. Для этого и предназначена передача по адресу.
Комментарии:
1. В C. нет такого понятия, как «передача по адресу». Я уверен, что вы имеете в виду
That's what passing the (value of the) array address is meant for.
Ответ №5:
Все известные мне методы возврата массива из функции имеют слабые и сильные стороны.
Обертывание в struct позволяет избежать накладных расходов на выделение и освобождение памяти, а также избежать запоминания освобождения. Эти проблемы возникают при любом решении, использующем malloc, calloc и realloc. С другой стороны, обертывание в структуре требует знания максимально возможного размера массива и явно расходует память и время выполнения для больших массивов (например, загрузка файла в память и передача содержимого файла из функции в функцию путем копирования).