Как передать адрес переменной в функцию c
Перейти к содержимому

Как передать адрес переменной в функцию c

Как передать адрес переменной в функцию c

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

Например, пусть у нас будет простейшая функция, которая увеличивает число на единицу:

#include void increment(int x) < x = x + 1; printf("increment function: %d \n", x); >int main(void)

Здесь переменная n передается в качестве аргумента для параметра x. Передача происходит по значению, поэтому любое изменение параметра x в функции increment никак не скажется на значении переменной n. Что мы можем увидеть, запустим программу:

increment function: 11 main function: 10

Теперь изменим функцию increment, использовав в качестве параметра указатель:

#include void increment(int *x) < *x = *x + 1; printf("increment function: %d \n", *x); >int main(void)

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

*x = *x + 1;

Это изменяет значение, которое находится по адресу, хранимому в указателе x.

Поскольку теперь функция в качестве параметра принимает указатель, то при ее вызове необходимо передать адрес переменной: increment(&n); .

В итоге изменение параметра x также повлияет на переменную n:

increment function: 11 main function: 11

Еще один показательный пример применения указателей в параметрах — функция обмена значений:

#include void swap(int *a, int *b) < int temp = *a; *a = *b; *b=temp; >int main(void)

Функция swap() в качестве параметров принимает два указателя. Посредством переменной temp происходит обмен значениями.

При вызове функции swap в нее передаются адреса переменных x и y, и в итоге их значения будут изменены.

Константые параметры

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

#include // константный параметр int twice(const int *x) < //*x = *x * *x; // так нельзя, так как x - константный параметр int y = *x + *x; return y; >int main(void) < int n = 10; int m = twice(&n); printf("n = %d \n", n); // n = 10 printf("m = %d \n", m); // m = 20 return 0; >

Фактически такой константный параметр будет работать как указатель на константу — мы не сможем изменить его значение внутри функции.

Массивы в параметрах

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

Например, определим функцию для увеличения элементов массива в два раза:

#include void twice(int n, int p[]) < for(int i = 0; i < n; i++) < p[i]= p[i] * 2; >> int main(void) < int nums[] = ; // получаем количество элементов массива int length = sizeof(nums)/sizeof(nums[0]); twice(length, nums); for(int i=0; i return 0; >

Функция twice в качестве параметров принимает массив и число его элементов и в цикле увеличивает их в два раза.

В функции main передаем массив в функцию twice и затем выводим его на консоль. В результате мы увидим, что массив nums был изменен:

2 4 6 8 10

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

#include void twice(int n, int *p) < for(int i=0; i> int main(void) < int nums[] = ; int length = sizeof(nums)/sizeof(nums[0]); twice(length, nums); for(int i=0; i return 0; >

В итоге в данном случае не будет большой разницы, какой тип имеет параметр — массив или указатель.

Как передать адрес переменной в функцию c

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

Например, пусть у нас будет простейшая функция, которая увеличивает число на единицу:

#include void increment(int); int main() < int n ; increment(n); std::cout void increment(int x)

Здесь переменная n передается в качестве аргумента для параметра x. Передача происходит по значению, поэтому любое изменение параметра x в функции increment никак не скажется на значении переменной n. Что мы можем увидеть, запустим программу:

increment function: 11 main function: 10

Теперь изменим функцию increment, использовав в качестве параметра указатель:

#include void increment(int*); int main() < int n ; increment(&n); std::cout void increment(int *x) < (*x)++; // получаем значение по адресу в x и увеличиваем его на 1 std::cout

Для изменения значения параметра применяется операция разыменования с последующим инкрементом: (*x)++ . Это изменяет значение, которое находится по адресу, хранимому в указателе x.

Поскольку теперь функция в качестве параметра принимает указатель, то при ее вызове необходимо передать адрес переменной: increment(&n); .

В итоге изменение параметра x также повлияет на переменную n, потому что оба они хранят адрес на один и тот же участок памяти:

increment function: 11 main function: 11

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

#include void increment(int*); int main() < int n ; int *ptr ; increment(ptr); std::cout void increment(int *x) < int z ; x = &z; // переустанавливаем адрес указателя x std::cout

В функцию increment передается указатель ptr, который хранит адрес переменной n. При вызове функция increment получает копию этого указателя через параметр x. В функции изменяется адрес указателя x на адрес переменной z. Но это никак не затронет указатель ptr, так как он предствляет другую копию. В итоге поле переустановки адреса указатели x и ptr будут хранить разные адреса.

Результат работы программы:

increment function: 6 main function: 10

Константные параметры-указатели

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

#include void print(const int*); // константный параметр int main() < int n ; print(&n); > void print(const int *x)

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

#include void print(const int*); // константный параметр int main() < const int n ; print(&n); // передаем адрес константы > void print(const int *x)

При этом константность параметра не означает, что мы не можем изменить адрес, хранимый в указателе, например, следующим образом:

void print(const int *x) < int z; x = &z; // меняем адрес в указателе на адрес переменной z std::cout 

Чтобы гарантировать, что не только значение по указателю не будет меняться, но и само значение указателя (хранимый в нем адрес) не будет меняться, надо определить указатель как константный:

#include void print(const int*); // константный параметр int main() < const int n ; print(&n); > void print(const int* const x) // константный указатель на константу < int z; //x = &z; // значение указателя нельзя изменить std::cout

Передача параметров по указателю

Некоторые разработчики предпочитают наличие амперсанда в f(&obj) как подсказки о том, что значение obj может измениться внутри вызова. Правда, если obj уже и так указатель, то амперсанд не будет нужен.

Плюсы передачи по указателю

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

Это экономия одновременно и по памяти, и по скорости!

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

Можно использовать константный указатель: const T*

Минусы передачи по указателю

В Си существует неявное преобразование типа целого числа к типу указателя, о котором компилятор выдает лишь предупреждение, но не ошибку:

warning: passing argument 1 of 'foo' makes pointer from integer without a cast

Представьте себе, что foo довольно длинна, и везде, где употребляется x , нужна звёздочка, а вызывается foo 48 раз в разных местах программы — при этом иногда нужен амперсанд, иногда нет.

Итак, легко забыть звёздочку в теле функции foo или амперсанд — в её вызове, значит переданное число может случайно истолковаться как адрес числа. Обращение по непредсказуемому адресу приводит к ошибке Segmentation fault и завершению программы.

Поэтому передача аргументов по адресу опасна!

Передача указателей в функцию C++

Вопрос по C++. При передаче обычной переменной в функцию создается, как я понимаю, её копия. А что происходит, когда мы передаем указатель? Создается ли копия указателя или нет?

Отслеживать
44.8k 3 3 золотых знака 38 38 серебряных знаков 89 89 бронзовых знаков
задан 24 дек 2016 в 23:45
113 1 1 серебряный знак 5 5 бронзовых знаков

2 ответа 2

Сортировка: Сброс на вариант по умолчанию

Если у вас объявлена функция с параметром, как, например,

void f( T t ); 

где T - это некоторый тип, и эта функция вызывается с некоторым выражением, переданным ей в качестве аргумента, как

f( exp ); 

то инициализацию параметра можно представить следующим образом

void f( /* T t */ ) < T t = exp; //. 

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

Сравните две функции

void f( int *p ) < p = new int( 10 ); >void g( int * &p )
int main()

В этом примере при вызове функции f имеет место утечка памяти, так как память, распределенная в функции, не освобождается. Параметр функции - локальная переменная p - при выходе из функции удаляется, и тем самым адрес выделенной динамически памяти будет утерян.

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

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

void h( int **p )

Вызов функции будет выглядеть как

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *