Передача массива в качестве аргумента функции в C - PullRequest
62 голосов
/ 04 июля 2011

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

void arraytest(int a[])
{
    // changed the array a
    a[0]=a[0]+a[1];
    a[1]=a[0]-a[1];
    a[0]=a[0]-a[1];
}

void main()
{
    int arr[]={1,2};
    printf("%d \t %d",arr[0],arr[1]);
    arraytest(arr);
    printf("\n After calling fun arr contains: %d\t %d",arr[0],arr[1]);
}

Я обнаружил, что хотя я вызываю функцию arraytest(), передавая значения, оригинальная копия int arr[] изменяется.

Не могли бы вы объяснить, почему?

Ответы [ 9 ]

98 голосов
/ 04 июля 2011

При передаче массива в качестве параметра это

void arraytest(int a[])

означает точно так же, как

void arraytest(int *a)

чтобы вы изменяли значения в main.

По историческим причинам массивы не являются гражданами первого класса и не могут быть переданы по значению.

6 голосов
/ 04 июля 2011

В C, за исключением нескольких особых случаев, ссылка на массив всегда «затухает» на указатель на первый элемент массива.Следовательно, невозможно передать массив «по значению».Массив в вызове функции будет передан функции в виде указателя, который аналогичен передаче массива по ссылке.

РЕДАКТИРОВАТЬ: Есть три таких особых случая, когда массив не распадается на указатель наэто первый элемент:

  1. sizeof a - это не то же самое, что sizeof (&a[0]).
  2. &a - это не то же самое, что &(&a[0]) (и не совсем то же самое, что &a[0]).
  3. char b[] = "foo" - это не то же самое, что char b[] = &("foo").
6 голосов
/ 04 июля 2011

Вы передаете адрес первого элемента массива

6 голосов
/ 04 июля 2011

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

4 голосов
/ 05 мая 2016

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

int func(int arr[], ...){
    .
    .
    .
}

int func(int arr[SIZE], ...){
    .
    .
    .
}

int func(int* arr, ...){
    .
    .
    .
}

Итак, вы изменяете исходные значения.

Спасибо!!!

4 голосов
/ 04 июля 2011

Вы передаете значение ячейки памяти первого члена массива.

Поэтому, когда вы начинаете изменять массив внутри функции, вы модифицируете исходный массив.

Помните, что a[1] - это *(a+1).

3 голосов
/ 22 февраля 2018

Передача многомерного массива в качестве аргумента функции. Передача одного тусклого массива в качестве аргумента более или менее тривиальна.Давайте рассмотрим более интересный случай передачи массива 2 dim.В C вы не можете использовать указатель на конструкцию указателя (int **) вместо 2 dim array.Давайте сделаем пример:

void assignZeros(int(*arr)[5], const int rows) {
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < 5; j++) {
            *(*(arr + i) + j) = 0;
            // or equivalent assignment
            arr[i][j] = 0;
        }
    }

Здесь я указал функцию, которая принимает в качестве первого аргумента указатель на массив из 5-ти целых чисел.Я могу передать в качестве аргумента любой массив 2 dim, который имеет 5 столбцов:

int arr1[1][5]
int arr1[2][5]
...
int arr1[20][5]
...

Вы можете прийти к мысли определить более общую функцию, которая может принимать любой массив 2 dim и изменять сигнатуру функции следующим образом:

void assignZeros(int ** arr, const int rows, const int cols) {
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            *(*(arr + i) + j) = 0;
        }
    }
}

Этот код скомпилируется, но вы получите ошибку времени выполнения при попытке присвоить значения таким же образом, как в первой функции.Таким образом, в C многомерные массивы - это не то же самое, что указатели на указатели ... на указатели.Int (* arr) [5] - это указатель на массив из 5 элементов, int (* arr) [6] - это указатель на массив из 6 элементов, и они являются указателями на разные типы!

Как определить аргументы функций для более высоких измерений?Просто, мы просто следуем шаблону!Это та же функция, настроенная на массив из 3 измерений:

void assignZeros2(int(*arr)[4][5], const int dim1, const int dim2, const int dim3) {
    for (int i = 0; i < dim1; i++) {
        for (int j = 0; j < dim2; j++) {
            for (int k = 0; k < dim3; k++) {
                *(*(*(arr + i) + j) + k) = 0;
                // or equivalent assignment
                arr[i][j][k] = 0;
            }
        }
    }
}

Как и следовало ожидать, она может принимать в качестве аргумента любые 3 dim-массива, которые имеют во втором измерении 4 элемента и в третьем измерении5 элементов.Что-нибудь вроде этого было бы хорошо:

arr[1][4][5]
arr[2][4][5]
...
arr[10][4][5]
...

Но мы должны указать все размеры до первого размера.

3 голосов
/ 30 апреля 2017

Массивы в C в большинстве случаев преобразуются в указатель на первый элемент самого массива. А более подробно массивы, передаваемые в функции, всегда преобразуются в указатели.

Здесь цитата из K & R2nd :

Когда имя массива передается функции, передается расположение начального элемента. В вызываемой функции это Аргумент является локальной переменной, и поэтому параметр имени массива является указатель, то есть переменная, содержащая адрес.

Запись:

void arraytest(int a[])

имеет то же значение, что и письмо:

void arraytest(int *a)

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

Для большего я действительно предлагаю прочитать это .

Более того, вы можете найти другие ответы на SO здесь

0 голосов
/ 25 июля 2018

@ Бо Перссон правильно заявляет в своем великолепном ответе здесь :

================================================

При передаче массива в качестве параметра это

void arraytest(int a[])

означаетточно так же, как

void arraytest(int *a)

================================================

Однако, позвольте мне также добавить, что это:

означает то же самое, что и

void arraytest(int a[0])

, что означает точнотакой же, как

void arraytest(int a[1])

, что означает точно такой же, как

void arraytest(int a[2])

, что означает точно такой же, как

void arraytest(int a[1000])

и т. д.

Фактически, значение "size" внутри параметра массива здесь, по-видимому, просто для эстетических целей / целей самодокументирования и может быть любым положительным целым числом (я думаю, типа * 1031), которое вы хотите!

На практике, однако, вы должны использовать его, чтобы указать минимальный размер массива, который вы ожидаете получить от функции, чтобы при написании кода было легко отслеживать и проверять. Стандарт MISRA-C-2012 ( купить / скачать PDF-версию стандарта 236-pg 2012 года за 15 фунтов стерлингов здесь ) доходит до того, что заявляет:

Правило 17.5 Аргумент функции, соответствующий параметру, объявленному как имеющий тип массива, должен иметь соответствующее количество элементов.

...

Еслипараметр объявляется как массив с указанным размером, соответствующий аргумент в каждом вызове функции должен указывать на объект, который имеет как минимум столько же элементов, сколько массив.

...

Использование декларатора массива для параметра функции определяет интерфейс функции более четко, чем использование указателя.Минимальное количество элементов, ожидаемое функцией, указано явно, тогда как это невозможно с указателем. [выделение добавлено]

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

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