Я написал функцию, содержащую массив в качестве аргумента, и вызвал ее, передав значение массива следующим образом.
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[]
изменяется.
Не могли бы вы объяснить почему?
c
arrays
function
parameters
parameter-passing
Мохан Махаджан
источник
источник
main()
должен вернутьсяint
.Ответы:
При передаче массива в качестве параметра это
void arraytest(int a[])
означает то же самое, что и
void arraytest(int *a)
так что вы являетесь изменения значений в основном.
По историческим причинам массивы не являются гражданами первого класса и не могут передаваться по значению.
источник
struct
было изменено только после того, как был добавлен язык. И тогда было уже поздно менять правила для массивов. Пользователей уже было 10. :-)void arraytest(int a[1000])
т.д. и т.п. Расширенный ответ здесь: stackoverflow.com/a/51527502/4561887 .Если вы хотите передать одномерный массив в качестве аргумента функции , вам придется объявить формальный параметр одним из следующих трех способов, и все три метода объявления дают аналогичные результаты, потому что каждый сообщает компилятору, что целочисленный указатель быть полученным .
int func(int arr[], ...){ . . . } int func(int arr[SIZE], ...){ . . . } int func(int* arr, ...){ . . . }
Итак, вы изменяете исходные значения.
Благодарность !!!
источник
Вы не передаете массив как копию. Это всего лишь указатель, указывающий на адрес, по которому в памяти находится первый элемент массива.
источник
Вы передаете адрес первого элемента массива
источник
Массивы в C в большинстве случаев преобразуются в указатель на первый элемент самого массива. И более подробно массивы, переданные в функции, всегда конвертируются в указатели.
Вот цитата из K & R2nd :
Письмо:
void arraytest(int a[])
имеет то же значение, что и письмо:
void arraytest(int *a)
Итак, несмотря на то, что вы не пишете это явно, это так, как вы передаете указатель, и поэтому вы изменяете значения в файле main.
Для получения дополнительной информации я действительно рекомендую прочитать это .
Более того, вы можете найти другие ответы на SO здесь
источник
Вы передаете значение ячейки памяти первого члена массива.
Поэтому, когда вы начинаете изменять массив внутри функции, вы изменяете исходный массив.
Помните, что
a[1]
это так*(a+1)
.источник
В C, за исключением нескольких особых случаев, ссылка на массив всегда «распадается» на указатель на первый элемент массива. Следовательно, невозможно передать массив «по значению». Массив в вызове функции будет передан в функцию как указатель, что аналогично передаче массива по ссылке.
РЕДАКТИРОВАТЬ: есть три таких особых случая, когда массив не распадается на указатель на его первый элемент:
sizeof a
не то же самое, чтоsizeof (&a[0])
.&a
не то же самое&(&a[0])
(и не совсем то же самое&a[0]
).char b[] = "foo"
не то же самое, чтоchar b[] = &("foo")
.источник
int a[10]
и присвоил каждому элементу случайное значение. Теперь, если я передаю этот массив функции с помощьюint y[]
илиint y[10]
или.int *y
И тогда в этой функции, которую я использую,sizeof(y)
Answer будет выделен указатель байтов. Так что в этом случае он будет распадаться как указатель. Было бы полезно, если бы вы его тоже включили. См. Postimg.org/image/prhleuezdsizeof
операцию в функции в первоначально определенном нами массиве, тогда он распадется как массив, но если я передам другую функцию, тогда используетсяsizeof
оператор, он распадется как указатель.&a
это не совсем то же самое, что&a[0]
когдаa
это массив. Как так? В моей тестовой программе оба показываются одинаковыми как в функции, в которой объявлен массив, так и при передаче в другую функцию. 2. Писатель пишет, что «char b[] = "foo"
не то же самоеchar b[] = &("foo")
». Для меня последний даже не компилируется. Это только я?Передача многомерного массива в качестве аргумента функции. Передача однотонного массива в качестве аргумента более или менее тривиальна. Давайте рассмотрим более интересный случай передачи двухмерного массива. В C вы не можете использовать указатель на конструкцию указателя (
int **
) вместо двух тусклых массивов. Приведем пример: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 тусклый массив с 5 столбцами:
int arr1[1][5] int arr1[2][5] ... int arr1[20][5] ...
Вам может прийти в голову идея определить более общую функцию, которая может принимать любой 2-тусклый массив и изменять подпись функции следующим образом:
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 многомерные массивы - это не то же самое, что указатели на указатели ... на указатели. An
int(*arr)[5]
- указатель на массив из 5 элементов, anint(*arr)[6]
- указатель на массив из 6 элементов, и они являются указателями на разные типы!Хорошо, как определить аргументы функций для более высоких измерений? Все просто, мы просто следуем шаблону! Вот та же функция, настроенная для использования массива из трех измерений:
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 тусклых массива, которые имеют во втором измерении 4 элемента и в третьем измерении 5 элементов. Что-нибудь вроде этого было бы нормально:
arr[1][4][5] arr[2][4][5] ... arr[10][4][5] ...
Но мы должны указать все размеры, вплоть до первого.
источник
Использование стандартного массива в C с естественным распадом типа от массива к ptr
@Bo Persson правильно заявляет в своем замечательном ответе здесь :
Однако позвольте мне также добавить, что две вышеуказанные формы также:
означает то же самое, что и
void arraytest(int a[0])
что означает то же самое, что и
void arraytest(int a[1])
что означает то же самое, что и
void arraytest(int a[2])
что означает то же самое, что и
void arraytest(int a[1000])
и т.п.
В каждом из приведенных выше примеров массивов тип входного параметра распадается на
int *
, и его можно вызывать без предупреждений и ошибок, даже если-Wall -Wextra -Werror
включены параметры сборки (см. Мое репо здесь для получения подробной информации об этих трех вариантах сборки), например этот:int array1[2]; int * array2 = array1; // works fine because `array1` automatically decays from an array type // to `int *` arraytest(array1); // works fine because `array2` is already an `int *` arraytest(array2);
В самом деле, значение «размера» (
[0]
,[1]
,[2]
,[1000]
и т.д.) внутри параметра массива здесь, по- видимому только для целей эстетического / самодокументирования, и может быть любым положительным целым числом (size_t
типа я думаю) вы хотите!На практике, однако, вы должны использовать его для указания минимального размера массива, который вы ожидаете от функции, чтобы при написании кода вам было легко отслеживать и проверять. Стандарт MISRA-C-2012 ( купить / загрузить версию стандарта на 236 стр. 2012 в формате PDF за 15 фунтов стерлингов здесь ) заходит так далеко, что заявляет (выделено мной):
Другими словами, они рекомендуют использовать явный формат размера, даже несмотря на то, что стандарт C технически не требует его соблюдения - он, по крайней мере, помогает прояснить вам как разработчику и другим, использующим код, массив размера, ожидаемого функцией. вам пройти.
Принудительная безопасность типов для массивов в C
Как отмечает @Winger Sendon в комментарии под моим ответом, мы можем заставить C рассматривать тип массива как другой в зависимости от размера массива !
Во-первых, вы должны признать, что в моем примере, приведенном выше, используя
int array1[2];
подобное:arraytest(array1);
вызываетarray1
автоматическое преобразование в файлint *
. ОДНАКО, если вы вместо этого возьмете адресarray1
или позвонитеarraytest(&array1)
, вы получите совершенно другое поведение! Теперь он НЕ распадается наint *
! Вместо этого тип&array1
isint (*)[2]
, что означает «указатель на массив размера 2 типа int» или «указатель на массив размера 2 типа int» . Итак, вы можете FORCE C проверить безопасность типов в массиве, например:void arraytest(int (*a)[2]) { // my function here }
Этот синтаксис трудно читать, но он похож на синтаксис указателя на функцию . Онлайн-инструмент cdecl сообщает нам, что это
int (*a)[2]
означает: «объявить a как указатель на массив 2 из int» (указатель на массив 2int
с). НЕ путайте это с версией со скобками OUT:,int * a[2]
что означает: «объявить a как массив 2 указателя на int» (массив из 2 указателей наint
).Теперь эта функция ТРЕБУЕТ, чтобы вы вызывали ее с помощью адресного оператора (
&
), подобного этому, используя в качестве входного параметра УКАЗАТЕЛЬ НА МАССИВ ПРАВИЛЬНОГО РАЗМЕРА !:int array1[2]; // ok, since the type of `array1` is `int (*)[2]` (ptr to array of // 2 ints) arraytest(&array1); // you must use the & operator here to prevent // `array1` from otherwise automatically decaying // into `int *`, which is the WRONG input type here!
Однако это приведет к предупреждению:
int array1[2]; // WARNING! Wrong type since the type of `array1` decays to `int *`: // main.c:32:15: warning: passing argument 1 of ‘arraytest’ from // incompatible pointer type [-Wincompatible-pointer-types] // main.c:22:6: note: expected ‘int (*)[2]’ but argument is of type ‘int *’ arraytest(array1); // (missing & operator)
Вы можете протестировать этот код здесь .
Чтобы заставить компилятор C превратить это предупреждение в ошибку, чтобы вы всегда ДОЛЖНЫ вызывать,
arraytest(&array1);
используя только входной массив правильного размера и типа (int array1[2];
в данном случае), добавьте-Werror
в параметры сборки. Если вы запускаете приведенный выше тестовый код на сайте onlinegdb.com, сделайте это, щелкнув значок шестеренки в правом верхнем углу и щелкнув «Дополнительные флаги компилятора», чтобы ввести этот параметр. Теперь это предупреждение:превратится в эту ошибку сборки:
Обратите внимание, что вы также можете создавать «типобезопасные» указатели на массивы заданного размера, например:
int array[2]; // "type safe" ptr to array of size 2 of int: int (*array_p)[2] = &array;
... но я не обязательно рекомендую это, так как это напоминает мне многие выходки C ++, используемые для обеспечения безопасности типов повсюду, за исключительно высокую цену сложности синтаксиса языка, многословия и сложности разработки кода, и которые мне не нравятся и неоднократно высказывались ранее (например, см. «Мои мысли о C ++» здесь ).
Для дополнительных тестов и экспериментов см. Также ссылку чуть ниже.
Ссылки
См. Ссылки выше. Также:
источник
void arraytest(int (*a)[1000])
лучше, потому что тогда компилятор выдаст ошибку, если размер неправильный.Forcing type safety on arrays in C
, охватывающим ваши точка.Массивы всегда передаются по ссылке, если вы используете
a[]
или*a
:int* printSquares(int a[], int size, int e[]) { for(int i = 0; i < size; i++) { e[i] = i * i; } return e; } int* printSquares(int *a, int size, int e[]) { for(int i = 0; i < size; i++) { e[i] = i * i; } return e; }
источник