копирование указателей и путаница в распределении памяти - PullRequest
1 голос
/ 06 февраля 2020

Что если я захочу создать функцию, которая будет принимать массив и изменять его.

void mod_Arr(int* arr) {
    int*arr = int[3][3]; //arr is now an array
    int*arrCpy = arr;
    //some modification to arrCpy
}

``

1) будет также изменяться arr? т.е. я должен включить строку: arr = arrCpy; или я должен вместо этого использовать: arr = mod_Arr (arr) // и получить mod_Arr для возврата массива? 2) Должен ли я освободить (arr), чтобы старые значения arr не занимали место? В случае, когда указатель теперь указывает на новую память вместо изменения значений по старому адресу, я должен очистить ненужные вещи, все еще находящиеся в старом адресе?

Ответы [ 2 ]

0 голосов
/ 06 февраля 2020

Если у вас есть массив в main(), то есть int a[3][3];, и вы хотите передать этот массив в вашу функцию, чтобы его можно было изменить внутри функции и увидеть изменения в вызывающей функции (main() здесь ), вы должны передать адрес массива в качестве параметра. Если вы просто передадите сам массив по значению , то функция получит массив copy-of , и любые изменения, внесенные в массив в функции, будут потеряны при возврате функции - - так же, как любой другой локально объявленный массив будет.

Когда вы берете address-of массив, у вас есть указатель на массив. В вашем случае указатель на 2D массив. Синтаксис для типа выше будет int (*a)[3][3]. (например, указатель на массив из int[3][3]) Таким образом, чтобы передать address-of массив (он все еще передает адрес по значению, C не имеет ссылок), объявление вашей функции будет выглядеть следующим образом:

void mod_arr (int (*a)[ROWS][COLS])
{
    for (int i = 0; i < ROWS; i++)
        for (int j = 0; j < COLS; j++)
            (*a)[i][j] = (i + 1) * (j + 1);
}

Чтобы работать с исходным массивом в функции и назначать / изменять значения элементов, необходимо разыменовать указатель на массив . Если вы посмотрите выше, это будет:

            (*a)[i][j] = (i + 1) * (j + 1);

Если скобки вокруг (*a) требуются из-за C приоритета оператора .

Объявление короткого prn_arr Функция для вывода результатов вы можете сделать что-то вроде следующего:

#include <stdio.h>

#define ROWS 3
#define COLS ROWS

void mod_arr (int (*a)[ROWS][COLS])
{
    for (int i = 0; i < ROWS; i++)
        for (int j = 0; j < COLS; j++)
            (*a)[i][j] = (i + 1) * (j + 1);
}

void prn_arr (int a[ROWS][COLS])
{
    for (int i = 0; i < ROWS; i++) {
        for (int j = 0; j < COLS; j++)
            printf (" %2d", a[i][j]);
        putchar ('\n');
    }
}

int main (void) {

    int a[ROWS][COLS] = {{0}};

    mod_arr (&a);
    prn_arr (a);
}

Где в main() выше, вы просто объявляете массив и инициализируете все значения нулями, а затем передаете адрес в mod_arr для манипулирования массивом, а затем вызовите prn_arr для вывода результатов - нужно только передать сам массив (он является указателем на первый ряд значений).

Однако следует помнить, что для больших массивов более эффективно передавать указатель на массив в качестве параметра, а не передавать полную копию массива (например, указатель на x86_64 равен 8 байтам независимо от того, размера массива, в то время как, например, массив 1000x1000, для копирования которого потребуется 1 000 000 раз больше байтов элемента)

Пример использования / Вывод

$ ./bin/modarr
  1  2  3
  2  4  6
  3  6  9

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

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

0 голосов
/ 06 февраля 2020

Если вы передадите переменную по значению в функцию, функция не изменит эту переменную, если вы передадите переменную по ссылке (как указатель на arr в вашем случае) функция изменит переменную:

void f(int a)
 {
   a = a+2; // or equivalently a+=2
 }

не изменит a ( передача по значению )

, в то время как

void f(int* a)
 {
   a* = (*a)+2;  // or equivalently (*a)+=2
 }

изменит a ( передача по ссылке )

Фон состоит в том, что функции имеют свою собственную локальную область , что в случае передача по значению может быть оставлена ​​return, однако вышеприведенная функция возвращает void, поэтому она ничего не изменяет вне своей локальной области видимости (https://www.geeksforgeeks.org/scope-rules-in-c/)

В C массив может быть передан по значению, как в https://www.geeksforgeeks.org/pass-array-value-c/

Если вы передаете массив по ссылке (используя указатели), вы можете изменить массив непосредственно в функция без необходимости что-то копировать

Вы можете проверить это с помощью следующего:

#include <stdio.h>

void f1(int a)   // pass-by-value
  {
     a+=2;
  }

void f2(int* a)  // pass-by-reference (pointer)
  {
     (*a)+=2;
  }

int main(int argc, char argv[])
{
    int a=0;
    f1(a);
    printf("a=%i \n",a);
    a=0;
    f2(&a);
    printf("a=%i \n",a);#
    return 0;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...