Передача массива по ссылке в C
Я новичок в C, и у меня есть сомнения.
Так как функции C создают локальные копии его аргументов, мне интересно, почему следующий код работает так, как ожидалось:
void function(int array[]){
array[0] = 4;
array[1] = 5;
array[2] = 6;
}
int main(){
int array[] = {1,2,3};
function(array);
printf("%d %d %d",array[0],array[1],array[2]);
return 0;
}
При линейном выходе 4 5 6.
Почему это работает, а следующее не работает?
void function(int integer){
integer = 2;
}
int main(){
int integer = 1;
function(integer);
printf("%d",integer);
return 0;
}
В этом случае вывод будет всего 1.
Краткая версия: почему функции могут изменять значения своих родительских переменных, если они переданы как массив?
Спасибо всем!
Ответы
Ответ 1
Это связано с тем, что массивы имеют тенденцию распадаться на указатели.
int a[] = { 1, 2, 3 };
int* p = a; // valid: p is now the address of a[0]
a = p; // NOT valid.
printf("a = %p\n", a);
printf("p = %p\n", p); // prints same address as a
a
и p
напечатает то же значение.
В отличие от других, a
не является указателем, он может просто распадаться на один. http://c-faq.com/aryptr/aryptrequiv.html
В вашем первом function()
то, что передается, - это адрес первого элемента массива, а также разделение функций тела, которое. Infact, компилятор обрабатывает прототип функции следующим образом:
void function(int* array /*you wrote int array[]*/){
array[0] = 4;
array[1] = 5;
array[2] = 6;
}
function(&array[0]);
Это должно произойти, потому что вы сказали "массив неизвестного размера" (int array []). Компилятор не смог гарантировать вывод стека, необходимого для передачи по значению, поэтому он распадается на указатель.
---- Редактировать ----
Позволяет комбинировать оба ваших примера и использовать более отличительные имена, чтобы сделать вещи более ясными.
#include <stdio.h>
void func1(int dynArray[]) {
printf("func1: dynArray = %p, &dynArray[0] = %p, dynArray[0] = %d\n",
dynArray, &dynArray[0], dynArray[0]);
}
void func2(int* intPtr) {
printf("func2: intPtr = %p, &intPtr[0] = %p, intPtr[0] = %d\n",
intPtr, &intPtr[0], intPtr[0]);
}
void func3(int intVal) {
printf("func3: intVal = %d, &intValue = %p\n",
intVal, &intVal);
}
int main() {
int mainArray[3] = { 1, 2, 3 };
int mainInt = 10;
printf("mainArray = %p, &mainArray[0] = %p, mainArray[0] = %d\n",
mainArray, &mainArray, mainArray[0]);
func1(mainArray);
func2(mainArray);
printf("mainInt = %d, &mainInt = %p\n",
mainInt, &mainInt);
func3(mainInt);
return 0;
}
Живая демонстрация на идеоне: http://ideone.com/P8C1f4
mainArray = 0xbf806ad4, &mainArray[0] = 0xbf806ad4, mainArray[0] = 1
func1: dynArray = 0xbf806ad4, &dynArray[0] = 0xbf806ad4, dynArray[0] = 1
func2: intPtr = 0xbf806ad4, &intPtr[0] = 0xbf806ad4, intPtr[0] = 1
mainInt = 10, &mainInt = 0xbf806acc
func3: intVal = 10, &intValue = 0xbf806ad0
В func1
и func2
"dynArray" и "intPtr" являются локальными переменными, но они являются указательными переменными, в которые они получают адрес mainArray из main.
Это поведение специфично для массивов. Если вы должны были разместить массив внутри структуры, вы могли бы передать его по значению.
Ответ 2
Массив, переданный функции, преобразуется в указатель. Когда вы передаете указатель в качестве аргумента функции, вы просто указываете адрес переменной в памяти. Поэтому, когда вы изменяете значение ячейки массива, вы редактируете значение по адресу, указанному в функции.
Когда вы передаете простое целое число в функцию, целое копируется в стек, когда вы изменяете целое число внутри функции, вы изменяете копию целого, а не оригинала.
Напоминание о различных типах памяти в C
В C мы можем использовать три типа памяти:
- стек, используемый для локальных переменных и вызовов функций: при создании переменной в main() мы используем стек для хранения переменной, а при вызове функции заданные параметры к методу регистрируются в стеке. Когда мы выходим из функции, мы "выкладываем" эти параметры, чтобы вернуться в исходное состояние, используя используемую переменную перед вызовом функции. (anecdote: stackoverflow - это когда мы взламываем стек, чтобы использовать предыдущие переменные в функции, не передавая его в качестве параметров)
- куча, которая соответствует динамически распределенной памяти: когда нам нужен большой объем данных, мы используем эту кучу, потому что стек ограничен несколькими мегабайтами.
- код, в котором хранятся инструкции программы
В случае этого массива, переданного функцией, которая является указателем (адрес другой переменной), она хранится в стеке, когда мы вызываем функцию, мы копируем указатель в стек.
В случае целого числа он также сохраняется в стеке, когда мы вызываем функцию, мы копируем целое число.
Если мы хотим изменить целое число, мы можем передать адрес целого, чтобы изменить значение под указателем, например:
void function(int *integer)
{
*integer = 2;
}
int main()
{
int integer = 1;
function(&integer);
printf("%d", integer);
return 0;
}
Ответ 3
Существует разница между 'pass by reference' и 'pass by value'
Передача по ссылке приводит к местоположению в памяти, где pass by value передает значение напрямую, переменная массива всегда является аргументом refference, поэтому указывает на местоположение в памяти. По умолчанию значения целых чисел будут передаваться по значению
Ответ 4
В первом коде вы передаете адрес массива, указывающий на верхний элемент в массиве. Поэтому, когда вы изменяете значение в функции и возвращаетесь к основной функции, вы все равно обращаетесь к тому же массиву, который находится в одном адресе. Это называется pass by reference.
Однако во втором случае значение целого числа копируется из основной функции в вызываемую функцию. Другими словами, два целых числа находятся в другом адресе в памяти. Так что модификация не изменяет другого.
Ответ 5
Имя массива - это указатель на первый элемент массива. В первом примере кода вы передали указатель на ячейку памяти, содержащую первый элемент массива. Во втором примере кода вы передали целое число по значению, поэтому оно не имеет ничего общего с локальной переменной с именем "integer"
проверьте, что ссылка
Передать по ссылке и пройти по значению
Передача по ссылке/значению в С++