передача адреса в функцию си

Указатели на функции

Указатели на функции

К ак уже обсуждалось ранее функции – это набор команд, которые расположены в соответствующей области памяти. Вызов функции – это сохранение состояния, передача аргументов и переход по адресу, где располагается функция. В си есть возможность создавать указатели на функции. Указатели на функции позволяют упростить решение многих задач. Совместно с void указателями можно создавать функции общего назначения (например, сортировки и поиска). Указатели позволяют создавать функции высших порядков (функции, принимающие в качестве аргументов функции): отображение, свёртки, фильтры и пр. Указатели на функции позволяют уменьшать цикломатическую сложность программ, создавать легко масштабируемые конструкции. Рассмотрим пример. Создадим функцию и указатель на эту функцию

Синтаксис объявления указателей на функцию
(* )( );

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

Вот где нам понадобились указатели типа void. Так как map получает указатель на функцию, то все функции должны иметь одинаковые аргументы и возвращать один и тот же тип. Но аргументы функций должны быть разного типа, поэтому мы делаем их типа void. Функция map получает указатель типа void (*)(void*), поэтому ей теперь можно передавать любую из четырёх функций.
Пример другой функции: функция filter получает указатель на массив и возвращает размер нового массива, оставляя в нём только те элементы, для которых переданный предикат возвращает логическую истину (предикат – функция, которая возвращает истину или ложь). Сначала напишем для массива типа int:

Теперь для массива типа void

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

Последний пример: функция сортировки вставками для массива типа void. Так как тип массива не известен, то необходимо передавать функцию сравнения.

Массив указателей на функции

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

Точно также можно было создать массив динамически

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

Ещё один пример: функция any возвращает 1, если в переданном массиве содержится хотя бы один элемент, удовлетворяющий условию pred и 0 в противном случае.

qsort и bsearch

Функция bsearch проводит бинарный поиск в отсортированном массиве и получает указатель на функцию сравнения, такую же, как и функция qsort. В случае, если элемент найден, то она возвращает указатель на этот элемент, если элемент не найден, то NULL.

Источник

Передача адреса в функцию си

Передача через указатель

Если передавать параметры через указатель (так называемый С-стиль), код функции выглядит так:

mul5(&var); // Здесь символ & обозначает операцию взятия адреса переменной

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

Передача через ссылку

А вызов выглядит так:

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

Читайте также:  планы ремонта однокомнатной квартиры

Поэтому, к примеру, если функция принимает:

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

void clear(unsigned char *block, int &shift)

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

unsigned char* anyBlock=»Long string»;

clear(anyBlock, anyShift); // Вызов функции

То есть, в вызове нет никаких спецсимволов указателей/адресов ни для первого ни для второго параметра. Для первого параметра нет спецсимвола взятия адреса (хоть и идет работа с указателем) из-за того, что имя массива имеет тип указателя. Для второго параметра нет спецсимвола взятия адреса из-за того, что передача идет по ссылке.

Источник

C Урок 29. Указатели в аргументах функций. Часть 1

В трёх последних уроках мы плотно занимаемся адресацией данных в языке C, изучили указатели, взятие адреса, разыменование, доступ к адресам массивов, поработали также с указателями на структуры и их поля.

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

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

Главная польза указателей в аргументах следующая.

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

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

Указатели в аргументах функций выглядят примерно вот так, как и обычное их объявление

Вот пример объявления такой функции

void print_str( const char *c_str)

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

А передаём мы указатель на массив, например, следующим образом.

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

strcpy (str1, «Hello, World. » );

Читайте также:  раздвижные двери в махачкале

А затем мы вызываем нашу функцию, передавая ей в качестве параметра имя массива, который, как мы помним, является указателем на массив

print_str(str1);

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

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

Проект мы также создадим из проекта прошлого урока с именем MYPROG28 и присвоим ему имя MYPROG29.

Откроем наш проект в Eclipse, произведём его первоначальную настройку и удалим весь наш код из функции main() за исключением возврата. Функция main() приобретёт вот такой вид

int main()

return 0; //Return an integer from a function

Давайте пока немного разомнёмся, прежде чем применять указатели в функциях. У нас в проекте (это моя промашка) существует модуль из пары файлов ariph.h и ariph.c. Здесь есть грамматическая ошибка, хотя это никак не сказывается на работоспособности модуля. Данное имя модуля произошло от слова арифметика, что на английском пишется как Arithmetic, а не Ariphmetic. Поэтому давайте изменим букву p на t в имени модуля. Дело в том, что порой нам иногда хочется изменить имя модуля и для этого мы должны знать, как это проделать более безболезненно и быстро.

Первым делом в дереве проекта вызываем контекстное меню на имени файла и выбираем в нём пункт Rename

В открывшемся диалоге меняем имя, снимаем галку (мы сами перепишем подключения, а то как-то не совсем корректно они получаются) и соглашаемся

В файлах main.c и arith.c изменим имя в подключении заголовочного файла

#include «arith.h»

Осталось открыть Makefile и там также везде изменить ariph на arith.

Вот и всё. Попробуем собрать наш проект, если он собрался, то мы всё сделали правильно.

Ну и теперь после такой небольшой разминки начнём работать с нашим проектом.

Сначала поработаем со строковым массивом.

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

Источник

Передача в функцию указателя или ссылки [дубликат]

В чем будет разница, если в функцию передать указатель и ссылку? например

Программы выведут одно и то же? Ведь и так, и так мы обращаемся к существующему элементу, а не создаем новый.

3 ответа 3

В принципе, технически разница между указателем и ссылкой лишь в том, что саму ссылку нельзя изменить (а указатель можно). [Вот тут бóльший список отличий.] Кроме этого, разница ещё синтаксическая: с ссылкой вы обращаетесь как будто это переменная, а с указателем нужно его правильно получать/разыменовывать:

Других технических отличий нет, и результат будет одинаковый.

Но разница на самом деле не в синтаксисе, а в смысле, в семантике.

Указатель может означать всё, что угодно. Он в C и C++ означает строку, ассив, адрес переменной, передачу переменной по ссылке и ещё кучу всяких вещей. А смысл ссылки ровно один — это как бы альтернативное имя (alias) существующей переменной.

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

И ещё: в чистом C ссылок нет, так что у вас нет другого варианта кроме указателей.

Золотое правило с++ программиста: используй ссылки где можешь, а указатели где должен.

Если вы исправите опечатки и включите заголовок

Читайте также:  пол ягоды как пишется

То вывод будет идентичен.

Когда используются указатели, то вы можете передать null-указатель. Например,

И функция может быть вызвана как

Когда же используются ссылки, то ссылка должна указывать на существующий объект.

Всё ещё ищете ответ? Посмотрите другие вопросы с метками c++ указатели ссылки или задайте свой вопрос.

Связанные

Похожие

дизайн сайта / логотип © 2021 Stack Exchange Inc; материалы пользователей предоставляются на условиях лицензии cc by-sa. rev 2021.9.9.40167

Нажимая «Принять все файлы cookie» вы соглашаетесь, что Stack Exchange может хранить файлы cookie на вашем устройстве и раскрывать информацию в соответствии с нашей Политикой в отношении файлов cookie.

Источник

Передача параметров в функцию

Об аргументах функции

П рактика показывает, что тема передачи аргументов в функции, особенно указателей, вызывает массу проблем. Давайте ещё раз обобщим наши знания и соберём информацию вместе.

Пусть у нас имеется какая-то структура и функция, которая работает с этой структурой.

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

В этом коде мы передаём в функцию foo копию переменной p. Значение кладётся на стек при вызове функции, и внутри функции мы можем с ним работать. Но изменяем мы только локальную копию. В main никакие изменения не видны. Проиллюстрируем это

Пусть переменная p имеет адрес AB00. По этому адресу в памяти компьютера располагается экземпляр структуры со значением x = 10. Мы передаём это значение в функцию foo, и локальная копия имеет адрес CD00. Потом мы изменяем значение внутри foo, и оно становится 100, но по тому же адресу CD00, а исходное значение не изменилось.

Мы уже знаем, что для изменения значения нужно передавать указатель. Вот пример, который работает, как мы и ожидаем

Пусть переменная p имеет адрес AB00. Передача по указателю – это тоже передача значения, просто в данном случае мы передаём адрес переменной. Внутри функции переменная point хранится по адресу CD00. Хранит она адрес переменной p, равный AB00. Далее мы изменяем значение содержимого, на которое ссылается переменная point. Поэтому изменение видно внутри main.

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

Более сложный пример, когда мы хотим изменить не содержимое объекта, а непосредственно сам объект. Типичная ошибка делать вот так

В этом случае изменения объекта не произойдёт, плюс, мы получим утечку памяти. Давайте разберёмся.

Пусть переменная p хранится по адресу AB00, а память под структуру была выделена по адресу AB04. p хранит этот адрес. В foo передаётся AB04 (содержимое p). Внутри foo мы локальной переменной point, которая хранится по адресу CD00, присваиваем новое значение СВ04 – адрес, который вернула нам функция malloc. Как видно, внутри main ничего не поменялось. Кроме того, после выхода из функции foo будет удалена переменная point, которая хранит адрес структуры на куче, а сама память не будет очищена.

Как обычно, p имеет адрес CD00 и хранит адрес CD04, по которому хранится структура. Мы передаём адрес переменной p – это AB00. Внутри foo теперь мы меняем содержимое по адресу AB00, и оно становится равным CD04. Иначе говоря, мы имеем две переменные, которые хранят один и тот же адрес на куче – это p внутри main, и point внутри foo. После выхода из функции локальная переменная point будет удалена, а значение, выделенное на куче, по адресу CD04 не пострадает.

Но заметьте, теперь у нас осталась «висячая» структура на куче по адресу AB04, на неё никто не ссылается и никто её не удаляет!

Внимательно следите за такими вещами.

Краткая памятка по использованию разных типов аргументов, на примере структуры point_t

Источник

Образовательный портал