Почему функция работает с копией фактических параметров? - PullRequest
0 голосов
/ 09 ноября 2019

Я хочу понять, что происходит, когда вы передаете параметр по значению в функцию. Как эта «функция копирует значения»?

Я бы хотел, чтобы это было параллелью между передачей массива и передачей двух переменных.

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

Вот два примера:

Пример 1:

void function(int arr[])
{
    cout << arr << endl;                // The address of the first elm
    cout << sizeof(arr);                // 4 (bytes size of address on 32 bit)
}

int main()
{
    int vector[] = {1,2,3,4,5,6,7};
    function(vector);
    return 0;
}

Пример 2:

void interChange(int a, int b)
{
    int tmp;
    tmp = a;
    a = b;
    b = tmp;
}

int main()
{
    int a = 5, b = 3;
    interChange(a, b);
    return 0;
}

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

Во втором примере параметры передаются по значению, но на этот раз они не изменяют значенияпеременные a и b. Как происходит этот процесс? Почему они копируются вместо использования их адресов тоже? Компилятор учитывает что-то об их адресах? Если я печатаю &a в main(), а затем внутри InterChange, я получаю два адреса очень близко друг к другу, например: 0x69fed8, 0x69fe80.

Ответы [ 3 ]

2 голосов
/ 09 ноября 2019

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

Случай 1: когда вы передали компилятор массива, он преобразовал его в указатель на первый элемент этого массива. Теперь массив типа int, так что указатель Ofc будет иметь тип int, а размер указателя int равен 4 байтам, что вы можете видеть.

Случай 2: когда вы передали два целых числа во второй функции. Передаваемый аргумент копируется в параметр функции. Поэтому помните, пишете ли вы то же имя в списке аргументов и параметров, что и вы. Оба являются разными переменными. Что бы вы ни делали в функции, это не повлияет на переменные в main. Таким образом, ваша функция обмена бесполезна, так как она работает с А и В своей собственной функции, а не с конца, который находится в основном.

Надеюсь, теперь все понятно. Комментарий, если вы не получили какую-либо часть

1 голос
/ 09 ноября 2019

Копирование по значению фактически означает следующий шаблон.

int a = 10;
int b = a;

В этом простом примере значение a копируется в переменную b.

Это объявление функциииз вашего первого примера

void function(int arr[]);

эквивалентно объявлению

void function(int *arr);

, поскольку компилятор неявно настраивает параметр, имеющий тип массива, на указатель на тип элемента массива.

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

Это определение функции

void function(int arr[])
{
    cout << arr << endl;                // The address of the first elm
    cout << sizeof(arr);                // 4 (bytes size of address on 32 bit)
}

и ее вызов

function(vector);

Вы можете представить себе следующий способ

function(vector);
//...

void function( /*int arr[] */)
{
    int *arr = vector; 
    cout << arr << endl;                // The address of the first elm
    cout << sizeof(arr);                // 4 (bytes size of address on 32 bit)
}

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

Но учтите, что элементы массива фактически передаются по ссылке через указатель на первый элемент массива.

Это выглядит так же, как и яЕсли у вас есть следующая функция

void interChange(int *a, int *b)
{
    int tmp;
    tmp = *a;
    *a = *b;
    *b = tmp;
}

и

interChange( &a, &b);

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

void interChange(int *a, int *b)
{
    int tmp;
    tmp = a[0];
    a[0] = b[0];
    b[0] = tmp;
}

То есть это похоже на передачу в массивы функций, каждый из которых содержит только один элемент.

1 голос
/ 09 ноября 2019

Хорошо, это просто соглашение, которое выбрал язык C-программирования. C ++ унаследовал его от C.

Вы привели два разных, но несколько связанных примера. Я собираюсь обратиться к ним отдельно.

Для второго примера : Например, когда вы объявляете переменную int a - машине нужно где-то хранить значение. То есть имеется определенный объем ОЗУ, выделенный для хранения значения, которое можно интерпретировать как int. на 32-битной машине x86 - это должно быть 32 бита / 4 байта памяти.

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


// Copy two integers
void interChange(int a, int b) {
    int tmp;
    tmp = a;
    a = b;
    b = tmp;
}

void interChangePtr(int* a, int* b) {
    int tmp;
    tmp = *a;
    a* = *b;
    b* = tmp;
}

int main() {
    int a = 5, b = 3;
    interChange(a, b);
    // a=5, b=3.

    interChangePtr(&a, &b);
    // a=3, b=5

    return 0;
}

Что касается вашего второго примера - это еще одно соглашение, выбранное C. Когда вы набираете:

int main() {
 int arr[25]; // Allocates memory on the stack for 25 integers
 ...

Объявление массива (в стиле C) сообщает компилятору, что вы хотите, чтобы он выделил память для вашего массива в стеке. Массив - это просто непрерывный кусок памяти. Таким образом, вы можете взять указатель на него и изменить значения, используя этот указатель. А в C, если вы наберете arr - это указатель на память, выделенную вам в стеке. Поэтому, когда вы вызываете функцию void function(int arr[]) как function(arr) - это фактически передает указатель на ваш массив, а не на реальный блок памяти. Причина этих условностей - это производительность. Проще передать единственный указатель на массив, чем выделить новый массив и скопировать данные.

Надеюсь, это даст вам некоторый указатель для дальнейшего изучения темы.

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