Матрицы как параметры функции в C89 - PullRequest
6 голосов
/ 19 января 2020

На протяжении большей части моего курса по программированию C мы изучали C99, и наш преподаватель не удосужился научить нас основным различиям между C99 и предыдущими версиями.

Недавно нам сообщили, что есть вероятность, что мы Вместо этого мне будет предложено реализовать решения с использованием C89.

Мой вопрос касается использования многомерных массивов переменной длины в отношении объявления и использования внутри функции.

В C99, у меня может быть такая функция:

void func(int cols, int mat[][cols], int rows);

В то время как в C89 VLA и подобные хитрости не разрешены. Мне сказали, что вам нужно будет вместо этого использовать указатель на указатель. Так что-то вроде:

void func(int **mat, int cols, int rows);

Однако у меня возникают проблемы с пониманием:

  1. Как вы будете обращаться к элементам в этой матрице внутри функции. Можете ли вы по-прежнему использовать обозначение mat[i][j]?
  2. Как бы вы объявили и заполнили такую ​​матрицу. Вы начинаете с чего-то вроде int **mat;? Я думаю, что вам придется использовать malloc(), но мне трудно понять точный оператор объявления.
  3. Можно ли даже использовать матрицы как указатели на такие указатели? Я читал, что они работают одинаково, но не совсем одинаково. Как вы решаете изложенную проблему в C89?

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

int rows, cols;

// get rows and cols from stdinput

int mat[rows][cols];

- не лучший способ go создать матрицу с заданными размерами из-за распределения на стек программ. Что может быть лучше?

Спасибо!

Ответы [ 4 ]

8 голосов
/ 19 января 2020

Однако у меня проблемы с пониманием:

  1. Как вы будете обращаться к элементам в этой матрице внутри функции. Вы все еще можете использовать нотацию mat [i] [j]?
  2. Как бы вы объявили и заполнили такую ​​матрицу. Вы начинаете с чего-то вроде int ** mat;? Я думаю, что вам придется использовать mallo c (), но мне трудно понять точный оператор объявления.
  3. Могут ли матрицы использоваться как указатели на указатели, подобные этому? Я читал, что они работают одинаково, но не совсем одинаково. Как решить описанную проблему в C89?

1. mat[i][j]

В C89 вы правы, у вас нет поддержки VLA, если она не предоставлена ​​нестандартным расширением компилятора (g cc делает это). Однако вы можете выполнить sh то же самое в двух разных массивах.

Если вам известно число столбцов , которое будет у вас во время компиляции, и вы сможете определить константу для этого значения, тогда Вы можете объявить указатель на массив [COLS] . Например, если вы знаете, что у вас будет 32 столбца и неизвестное количество строк, вы можете сделать:

#define COLS 32
...
    int (*array)[COLS] = malloc (rows * sizeof *array);

, который выделит блок памяти за один вызов, предоставляя хранилище для rows числа int[32] массивы, позволяющие получить доступ как array[i][j], как и раньше. Прелесть использования указатель на массив в том, что у вас есть одно выделение и одно свободное. Вы можете realloc количество строк по мере необходимости.

( примечание: как @ PaulOgilv ie указывает, есть разница в том, как вы можете передать указатель на массив функции. Вы не можете передать как int array[][cols] как с VLA, вы должны передать как int (*array)[cols] - что вы также можете использовать с VLA, но обратное не выполняется)

Другой вариант - объявить указатель на указатель type (например, int **array;). Обратите внимание, что здесь НЕ используется массив, это просто один указатель на указатель на тип. Здесь распределение является двухэтапным процессом. Сначала вы выделяете память для некоторого количества указателей (количество строк указателей). Например:

int **array = malloc (rows * sizeof *array);

Выше вы выделяете блок памяти, способный содержать rows количество указателей , для которого вы можете затем отдельно выделять и назначать блоки памяти для хранения любого числа целочисленных значений (нет необходимости, чтобы каждая строка указывала на блок с одинаковым числом целочисленных значений - возможно создание «зубчатого массива», из-за отсутствия лучших слов), чтобы затем выделить хранилище для целых значений (или любой другой тип, который вы используете), вы должны сделать:

for (int i = 0; i < rows; i++)
    array[i] = malloc (cols * sizeof *array[i]);

( примечание: вы должны проверить каждое распределение , которое для краткости был опущен. Также обратите внимание, что в обоих случаях выше разыменованный указатель использовался для установки типоразмера для выделения, например, malloc (rows * sizeof *array), который мог бы быть malloc (rows * sizeof(int*))). Если Вы всегда используете разыменованный указатель , чтобы установить типизировать - вы никогда не ошибетесь в размере шрифта)

На данный момент у вас есть указатель на блок хранение памяти rows ню Множество указателей, а затем вы назначили блок памяти, способный содержать cols количество целочисленных значений, к которым вы можете получить доступ как array[i][j]. Кроме того, здесь вы можете realloc блок памяти, предоставляющий rows указатели для добавления строк в любое время, когда вам нужно, но вы также должны выделить хранилище для целочисленных значений и назначить эти выделенные блоки новым указателям на строки, прежде чем пытаться сохраняйте значения там.

Когда вы закончите работу с вашим симулированным 2D-массивом, основанным на указатель-указатель , у вас также будет 2-шаговое освобождение. Вы должны освободить выделенные блоки, хранящие целые числа, прежде чем освободить блок, содержащий указатели строк, например,

for (int i = 0; i < rows; i++)
    free (array[i]);                /* free storage for integers */
free (array);                       /* free pointers */

2. Заполнение любого объекта

В любом случае, поскольку вы можете получить доступ к моделируемому 2D-массиву с помощью обозначения array[i][j], теперь вы можете заполнять и получать доступ к значениям в array, так же, как вы делали это с 2D-VLA в C99 +.

3. Можно ли использовать матрицы с указателями на указатели

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

4 голосов
/ 19 января 2020
  1. Как вы будете обращаться к элементам в этой матрице внутри функции. Вы все еще можете использовать обозначение mat[i][j]?

Да.

Как бы вы объявили и заполнили такую ​​матрицу. Вы начинаете с чего-то вроде int **mat;? Я думаю, что вам придется использовать malloc(), но мне трудно определить точный оператор объявления.

Если размер матрицы неизвестен во время компиляции, или вообще, это большой размер, тогда malloc() - это путь к go. Примерно так:

// assume nrows and ncols are dynamic
size_t nrows = /* ... */;
size_t ncols = /* ... */;
size_t i;
int **matrix;

matrix = malloc(nrows * sizeof(int*));
if (matrix == NULL) {
    perror("malloc() failed");
    exit(1);
}

for (i = 0; i < nrows; i++) {
    matrix[i] = malloc(ncols * sizeof(int));
    if (matrix[i] == NULL) {
        perror("malloc() failed");
        exit(1);
    }
}

/* fill the matrix */

/* use the matrix however you want */
func(matrix, nrows, ncols);

/* free the allocated memory once you don't need it anymore */
for (i = 0; i < nrows; i++)
    free(matrix[i]);
free(matrix);
Могут ли матрицы использоваться как указатели на подобные указатели? Я читал, что они работают одинаково, но не совсем одинаково. Как решить описанную проблему в C89?

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

Дополнительный вопрос [...] - не лучший способ go создать матрицу с заданными размерами из-за распределения на стек программ. какой способ лучше?

Да, верно, это не лучший способ. В общем случае программы имеют ограниченный размер стека, поэтому размещение больших массивов в стеке не является хорошей идеей. В некоторых случаях вы могли бы превысить доступную память, выделенную для использования стека, и ваша программа затем обработает sh. В этом случае лучше всего использовать динамическое выделение c через malloc().

1 голос
/ 19 января 2020

Как бы вы получили доступ к элементам этой матрицы внутри функции. Можете ли вы по-прежнему использовать обозначение mat [i] [j]?

Да, вы можете использовать массивоподобную нотацию для доступа к значению, указанному указателем.

Является ли имя массива указателем?

Как бы вы объявили и заполнили такую ​​матрицу. Вы начинаете с чего-то вроде int ** mat;? Я думаю, что вам придется использовать mallo c (), но мне сложно разобраться с точным утверждением объявления.

Это ваш выбор. Если вы находитесь в сценарии, где требуется размещение stati c, вы можете использовать обычное объявление массива. Если вы не знаете размеры матрицы заранее, вам следует использовать динамическое распределение c. В последнем случае вы должны объявить вашу матрицу в синтаксисе указателя, чтобы в ней можно было хранить адрес, возвращаемый mallo c.

Как работать с многомерными массивами dynamici c в C?

Могут ли матрицы использоваться как указатели на подобные указатели? Я читал, что они работают одинаково, но не совсем одинаково. Как решить описанную проблему в C89?

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

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

Этот вопрос является синонимом Stati c массив против динамического c массив в C ++

1 голос
/ 19 января 2020

В C89, если у вас есть функция типа void func(int **mat, int cols, int rows), вы обращаетесь к элементам следующим образом, однако это должен быть один указатель:

int main(void)
{
    int arr1[10][10];                    // automatic array, allocated on the stack
    int *arr2= malloc(100*sizeof(int));  // dynamic array, allocated on heap
    func(arr1, 10, 10);                  // pass to func. 
    func(arr2, 10, 10);                  // pass to func. Same for both arrays
    return 0;
}
void func(int *mat, int cols, int rows)
{
    // address element x, y, with x the column number and y the row number:
    int k= mat[cols*y + x];

Поскольку компилятор ничего не знает о строках и столбцы, вы делаете этот расчет самостоятельно. Таким образом, вы сначала пропускаете y строки cols столбцов, а затем берете x '-ый элемент.

EDIT:

Это работает для массивов, которые являются смежными блоками памяти, которые эквивалентно VLA современного C стандарта.

Другой способ - это «массив указателей на строки данных», но это не эквивалентно VLA, о котором вы и спрашивали. Другие ответы обсуждают этот тип решения.

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