Передайте массив, расположенный в куче, функции в C - PullRequest
0 голосов
/ 20 февраля 2019

Рассмотрим следующий код:

int main(void) {
    int *x[5];

    for(int i = 0; i < 5; i++) {
        x[i] = malloc(sizeof(int) * 5);
    }
    for(int i = 0; i < 5; i++) {
        for(int j = 0; j < 5; j++) {
            x[i][j] = i * j;
        }
    }
    modify(x, 5, 5);
    return 0; 
}

Какая из реализаций метода, модифицированных ниже, устанавливает все элементы матрицы x равными нулю?

  1. void modify(int **x, int m, int n) {
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                x[i][j] = 0;
            }
        }
    }
    
  2. void modify(int *x[], int m, int n) {
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                x[i][j] = 0;
            }
        }
    }
    
  3. void modify(int x[5][5], int m, int n) {
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                x[i][j] = 0;
            }
        }
    }
    

Я не совсем понимаю, почему третий вариант неверен.Есть ли разница между передачей массива в стеке и массива в куче?

Ответы [ 2 ]

0 голосов
/ 21 марта 2019

Может помочь вспомнить, что в Си нет массивов, есть только указатели и блоки памяти.Остальное - подделка компилятора.

Ваша основная программа выделяет блок памяти для 5 *int указателей (называемых x) в стеке и 5 блоков по 5 int s в куче.Он вызывает функцию с указателем на начало x и полагается на функцию для выполнения правильной арифметики указателя для правильного доступа к другим блокам.

Первые две функции в этом случае эквивалентны (это не всегдаtrue), а арифметика указателя правильно соответствует распределению.Третья функция неправильно выполняет арифметику указателя для адреса 25 дюймов, расположенных 5x5, поэтому первые 5 хранилищ перезаписывают *int на int, а шестой доступ выходит за пределы (при условии, что int и *int имеют одинаковый размер).

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

0 голосов
/ 20 февраля 2019

Компилятор дает довольно хорошую подсказку:

~$ gcc mat.c 
mat.c: In function ‘main’:
mat.c:22:12: warning: passing argument 1 of ‘modify’ from incompatible pointer type [-Wincompatible-pointer-types]
     modify(x, 5, 5);
            ^
mat.c:3:17: note: expected ‘int (*)[5]’ but argument is of type ‘int **’
 void modify(int x[5][5], int m, int n) {
             ~~~~^~~~~~~

Объявление int x[5][5] объявляет массив массивов.Это нечто совершенно отличное от int *x[5];, которое объявляет массив указателей.Интересный факт: добавьте круглые скобки вот так int (*x)[5], и вместо этого вы получите указатель на массив int размера 5. Вот хороший сайт, который переводит объявления C на английский: https://cdecl.org/

IsЕсть ли разница между передачей массива в стеке и массива в куче?

Динамически выделяемая память обычно заканчивается в куче, а остальные обычно идет в стек.Тем не менее, это всего лишь деталь реализации, и ничто в стандарте C не требует наличия кучи или стека вообще.

Когда вы передаете массив функции, он будет распадаться на указатель на его первый элемент.Функция не будет знать, где находятся данные (хммм, хорошо, поскольку указатель имеет фактический адрес, который он делает, но не знает о куче или стеке) или как они были распределены.Итак, рассмотрим следующий фрагмент кода:

void setToZero(int * arr, int size) {
    for(int i=0; i<size; i++) arr[i] = 0;
}

Функция будет иметь ТОЧНО одинаковый эффект для x и y:

int x[10];
int *y = malloc(10 * sizeof *y);
setToZero(x);
setToZero(y);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...