Передавать массив по значению

Обычно, когда мы передаем массив по его имени, его вызов по адресу. Это означает, что если мы изменим любое значение массива вне main(), оно будет отражено в main().

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

Например:

void Foo(int arr[])   //takes an integer array `arr` as argument
{
    // do something with `arr
}

int main()
{
    int abc[]={5,1,2,9};
    //do something to pass `abc` inside `Foo` so that changes inside `Foo` doesn't change the value of `abc` array.
}

Теперь я хочу передать массив abc в Foo по значению.

Ответ 1

Это можно сделать, обернув массив в struct. Вы можете включить поле для размера массива, чтобы вам не нужно явно передавать этот параметр. Этот подход имеет преимущество в том, чтобы избежать дополнительных распределений памяти, которые впоследствии должны быть освобождены.

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

Также обратите внимание, что a struct не может содержать элемент с неполным типом и поэтому не может содержать VLA. Здесь я определил глобальную константу MAX_ARR равным 100, чтобы предоставить некоторое пространство для обработки массивов разного размера с тем же типом struct.

Вы также можете вернуть struct из функции. Я включил пример, который изменяет Array struct, который передается в функцию, и возвращает измененную struct, которая должна быть назначена другому Array struct в вызывающей функции. Это приводит к тому, что вызывающий абонент имеет доступ как к оригиналу, так и к преобразованным массивам.

#include <stdio.h>

#define MAX_ARR  100

struct Array {
    size_t size;
    int array[MAX_ARR];
};

void print_array(struct Array local_arr);
void func(struct Array local_arr);
struct Array triple(struct Array local_arr);

int main(void)
{
    struct Array data = {
        .size = 10,
        .array = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }
    };
    struct Array transformed_data;

    func(data);
    transformed_data = triple(data);

    printf("Original\n");
    print_array(data);

    printf("Transformed\n");
    print_array(transformed_data);

    return 0;
}

void print_array(struct Array local_arr)
{
    for (size_t i = 0; i < local_arr.size; i++) {
        printf("%5d", local_arr.array[i]);
    }
    putchar('\n');
}

void func(struct Array local_arr)
{
    for (size_t i = 0; i < local_arr.size; i++) {
        local_arr.array[i] *= 2;
    }
    printf("Modified\n");
    print_array(local_arr);
}

struct Array triple(struct Array local_arr)
{
    for (size_t i = 0; i < local_arr.size; i++) {
        local_arr.array[i] *= 3;
    }
    return local_arr;
}

Выход программы:

Modified
    2    4    6    8   10   12   14   16   18   20
Original
    1    2    3    4    5    6    7    8    9   10
Transformed
    3    6    9   12   15   18   21   24   27   30

Ответ 2

В общем, вы не можете.

Вызывающий может сделать что-то вроде этого:

int main()
{
    int abc[]={5,1,2,9};

    {
         int temp[sizeof (abc)/sizeof (*abc)];
         memcpy(temp, abc, sizeof(abc));
         Foo(temp);
    }
}

Имейте в виду, что Foo() не получает никакой информации о количестве элементов в переданном массиве.

Если вы хотите, чтобы Foo() выполнял аналогичную вещь, поэтому вызывающему не нужно, необходимо передать количество элементов в качестве отдельного аргумента.

void Foo(int arr[], size_t size)    /*  C99 or later */
{
       int temp[size];   //   VLA
       memcpy(temp, arr, size*sizeof(int));
         /* whatever */
}

или (до C99).

void Foo(int arr[], size_t size)    /*  Before C99 */
{
       int *temp = malloc(size * sizeof (int));
       memcpy(temp, arr, size*sizeof(int));
         /* whatever */
       free(temp);
}

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

В обеих версиях Foo() выше может потребоваться дополнительная проверка ошибок (например, для обнаружения нулевого указателя или нулевых размеров, что malloc() завершается успешно и т.д.).

Ответ 3

(Я не уверен, почему Райан не добровольно дал свой собственный ответ, но я согласен, что следующее должно работать:)

#include <stdlib.h> // In header
#include <string.h>

int Foo(size_t size, int arr[])
{
   // guard against bad arguments
   if (arr == NULL || size <= 0)
   {
       return -1;
   }

   // create local array, since size is variable, allocate dynamically
   int* arr_2 = NULL;
   if ((arr_2 = malloc(n * sizeof(*arr_2)) == NULL)
   {
       return -1; // malloc allocation error
   }
   // otherwise if the size is constant:
   // int arr_2[SOME_CONSTANT_SIZE];

   // copy from arr to arr_2
   memcpy(arr_2, arr, n * sizeof(*arr_2));

   // some computation

   free(arr_2);

   return 0;
}

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