В уроке «1.3 – Знакомство с переменными в C++», мы отметили, что переменная – это имя части памяти, которая содержит значение. Когда наша программа создает экземпляр переменной, ей автоматически присваивается адрес свободной памяти, и любое значение, которое мы присваиваем переменной, сохраняется в памяти с этим адресом.
int x<>;
Когда эта инструкция выполняется процессором, будет выделена часть памяти из ОЗУ. В качестве примера предположим, что переменной x присвоена ячейка памяти 140. Всякий раз, когда программа видит переменную x в выражении или инструкции, она знает, что она должна искать значение в ячейке памяти 140.
Что хорошо в переменных, так это то, что нам не нужно беспокоиться о том, какой конкретный адрес памяти назначен. Мы просто ссылаемся на переменную по ее заданному идентификатору, и компилятор переводит это имя в соответствующий адрес памяти.
Однако у этого подхода есть некоторые ограничения, которые мы обсудим в этом и будущих уроках.
Оператор адреса ( ) позволяет нам увидеть, какой адрес памяти назначен переменной. Это довольно просто:
Указатели c++ что это. Для чего нужны. Указатели c++ разыменование. C++ для начинающих. Урок #46
#include int main() < int x< 5 >; std::cout
На машине автора показанная выше программа напечатала:
5 0027FEA0
Примечание. Хотя оператор адреса выглядит так же, как оператор побитового И, их можно различить, поскольку оператор адреса является унарным, тогда как оператор побитового И является бинарным.
Оператор косвенного обращения ( * )
Получение адреса переменной само по себе не очень полезно.
Оператор косвенного обращения ( * ) (также называемый оператором разыменования) позволяет нам получить доступ к значению по определенному адресу:
#include int main() < int x< 5 >; std::cout
На машине автора показанная выше программа напечатала:
5 0027FEA0 5
Примечание. Хотя оператор косвенного обращения выглядит так же, как оператор умножения, вы можете различить их, поскольку оператор косвенного обращения является унарным, а оператор умножения – бинарным.
Указатели
Теперь, когда в наши инструменты добавлены оператор адреса и оператор косвенного обращения, мы можем поговорить об указателях. Указатель – это переменная, которая в качестве значения хранит адрес памяти.
Указатели обычно считаются одной из самых запутанных частей языка C++, но при правильном объяснении они удивительно просты.
Объявление указателя
Переменные-указатели объявляются так же, как обычные переменные, только со звездочкой между типом данных и именем переменной. Обратите внимание, что эта звездочка не является косвенным обращением. Это часть синтаксиса объявления указателя.
int *iPtr<>; // указатель на значение типа int double *dPtr<>; // указатель на значение типа double int* iPtr2<>; // тоже допустимый синтаксис int * iPtr3<>; // тоже допустимый синтаксис (но не делайте так, это похоже на умножение) // При объявлении нескольких переменных в одной строке звездочка должна // быть у каждой переменной. int *iPtr4<>, *iPtr5<>; // объявляем два указателя на переменные типа int (не рекомендуется)
Синтаксически C++ принимает звездочку рядом с типом данных, рядом с именем переменной или даже в середине.
Передача адреса переменной в функцию в Си
Лучшая практика
При объявлении переменной-указателя ставьте звездочку рядом с типом, чтобы его было легче отличить от косвенного обращения.
Как и обычные переменные, указатели не инициализируются при объявлении. Если они не инициализированы значением, они будут содержать мусор.
Одно замечание по номенклатуре указателей: «указатель X» (где X – какой-либо тип) – это обычно используемое сокращение для «указателя на X». Поэтому, когда мы говорим «указатель int », мы на самом деле имеем в виду «указатель на значение типа int ».
Присвоение значения указателю
Поскольку указатели содержат только адреса, когда мы присваиваем значение указателю, это значение должно быть адресом. Одна из самых распространенных вещей, которые делают с указателями, – это хранение в них адреса другой переменной.
Чтобы получить адрес переменной, мы используем оператор адреса:
int v< 5 >; int* ptr< ; // инициализируем ptr адресом переменной v
Концептуально вы можете представить приведенный выше фрагмент так:
Отсюда указатели и получили свое название – ptr (от англ. «pointer», «указатель») содержит адрес значения переменной, поэтому мы говорим, что ptr «указывает на» v .
Это также легко увидеть с помощью кода:
#include int main() < int v< 5 >; int* ptr< ; // инициализируем ptr адресом переменной v std::cout
На машине автора эта программа напечатала:
0012FF7C 0012FF7C
Тип указателя должен соответствовать типу переменной, на которую он указывает:
int iValue< 5 >; double dValue< 7.0 >; int* iPtr< ; // ok double* dPtr< ; // ok iPtr = &dValue; // неправильно — указатель типа int не может указывать на адрес переменной типа double dPtr = &iValue; // неправильно — указатель типа double не может указывать на адрес переменной типа int
Обратите внимание, что следующее также некорректно:
int* ptr< 5 >;
Это связано с тем, что указатели могут содержать только адреса, а целочисленный литерал 5 не имеет адреса памяти. Если вы попробуете это сделать, компилятор сообщит вам, что он не может преобразовать int в указатель int .
C++ также не позволит вам напрямую преобразовать литеральные адреса памяти в указатель:
double* dPtr< 0x0012FF7C >; // не допустимо
Оператор адреса возвращает указатель
Стоит отметить, что оператор адреса ( int x< 4 >; std::cout
В Visual Studio 2013 этот код напечатал:
gcc вместо этого выводит «pi» («pointer to int », указатель на int ).
Затем этот указатель по желанию можно распечатать в консоль или присвоить.
Косвенное обращение через указатели
Когда у нас есть переменная-указатель, указывающая на что-то, другая распространенная вещь, которую мы делаем с ней, – это косвенное обращение через указатель для получения значения того, на что он указывает. Косвенное обращение через указатель вычисляет содержимое адреса, на который он указывает.
int value< 5 >; std::cout ; // ptr указывает на value std::cout
На машине автора этот код напечатал:
0012FF7C 5 0012FF7C 5
Вот почему указатели должны иметь тип. Без типа, при косвенном обращении через указатель, указатель не знал бы, как интерпретировать содержимое, на которое он указывает. По этой же причине тип указателя и тип переменной, адрес которой ему присваивается, должны совпадать. Если бы это было не так, косвенное обращение через указатель неверно интерпретировало бы биты как другой тип.
После присваивания значению указателя можно присвоить другое значение:
int value1< 5 >; int value2< 7 >; int* ptr<>; ptr = &value1; // ptr указывает на value1 std::cout
Когда адрес переменной value присваивается указателю ptr , верно следующее:
- ptr равен 5 >; int* ptr< ; // ptr указывает на value *ptr = 7; // *ptr — это то же, что и value, которому присвоено 7 std::cout
Предупреждение о косвенном обращении через недействительные указатели
Указатели в C++ по своей сути небезопасны, и неправильное использование указателей – один из лучших способов вывести ваше приложение из строя.
Во время косвенного обращения через указатель приложение пытается перейти в ячейку памяти, которая хранится в указателе, и получить содержимое памяти. В целях безопасности современные операционные системы используют приложения-песочницы, чтобы предотвратить их неправильное взаимодействие с другими приложениями и защитить стабильность самой операционной системы. Если приложение пытается получить доступ к области памяти, не выделенной ему операционной системой, операционная система может завершить работу приложения.
Следующая программа иллюстрирует это и, вероятно, выйдет со сбоем, когда вы запустите ее (попробуйте, вы не навредите своей машине):
#include // Мы рассмотрим p) < // p — ссылка на указатель.
Мы рассмотрим ссылки (и ссылки на указатели) позже в этой главе. // Мы используем ее, чтобы заставить компилятор думать, что p мог быть изменен, // поэтому он не будет жаловаться на то, что p неинициализирован. // Это не то, что вам когда-либо захочется делать намеренно. >int main() < int* p; // Создаем неинициализированный указатель (указывающий на мусор) foo(p); // Обманываем компилятор, заставляя его думать, что мы собираемся // присвоить указателю допустимое значение std::cout
Размер указателей
Размер указателя зависит от архитектуры, для которой скомпилирован исполняемый файл – 32-битный исполняемый файл использует 32-битные адреса памяти – следовательно, указатель на 32-битной машине занимает 32 бита (4 байта). С 64-битным исполняемым файлом указатель будет 64-битным (8 байтов). Обратите внимание, что это верно независимо от размера объекта, на который он указывает:
char* chPtr<>; // char равен 1 байту int* iPtr<>; // int обычно равен 4 байтам struct Something < int x<>; int y<>; int z<>; >; Something* somethingPtr<>; // Something, вероятно, занимает 12 байт std::cout
Как видите, размер указателя всегда один и тот же. Это связано с тем, что указатель – это просто адрес памяти, а количество битов, необходимых для доступа к адресу памяти на данной машине, всегда постоянно.
Что хорошего в указателях?
На этом этапе указатели могут показаться немного глупыми, чисто теоретическими или бесполезными. Зачем использовать указатель, если мы можем просто использовать исходную переменную?
Оказывается, указатели полезны во многих разных случаях:
- Массивы реализованы с помощью указателей. Указатели могут использоваться для итерации по массиву (как альтернатива индексам массива) (рассматривается в уроке «10.24 – Введение в итераторы»).
- В C++ это единственный способ динамического выделения памяти (рассматривается в уроке «10.13 – Динамическое выделение памяти с помощью new и delete »). Это, безусловно, наиболее распространенный вариант использования указателей.
- Их можно использовать для передачи функции в качестве параметра другой функции (рассматривается в уроке «11.9 – Указатели на функции»).
- Их можно использовать для достижения полиморфизма при работе с наследованием (рассматривается в уроке «18.1 – Указатели и ссылки базового класса на объекты производных классов»).
- Их можно использовать, чтобы иметь указатель на одну структуру/класс в другой структуре/классе, чтобы сформировать цепочку. Это полезно в некоторых более сложных структурах данных, таких как связанные списки и деревья.
Так что на самом деле существует удивительное количество применений указателей. Но не волнуйтесь, если вы еще не понимаете что-то из этого. Теперь, когда вы на базовом уровне понимаете, что такое указатели, мы можем начать подробно рассматривать различные случаи, в которых они могут быть полезны; что мы и сделаем в следующих уроках.
Заключение
Указатели – это переменные, которые содержат адреса памяти. К значениям, на которые они указывают, можно получить доступ с помощью оператора косвенного обращения ( * ). Косвенное обращение через мусорный указатель вызывает неопределенное поведение.
Небольшой тест
Вопрос 1
Какие значения выводит эта программа? Предположим, что short занимает 2 байта, а машина 32-битная.
short value< 7 >; // 3 >; // ; std::cout
0012FF60 7 0012FF60 7 0012FF60 9 0012FF60 9 0012FF54 3 0012FF54 3 4 2Краткое объяснение про 4 и 2. 32-битная машина означает, что указатели будут иметь длину 32 бита, но sizeof() всегда выводит размер в байтах. 32 бита – это 4 байта. Таким образом, sizeof(ptr) равен 4. Поскольку ptr является указателем на short , *ptr – это short . Размер short в этом примере составляет 2 байта. Таким образом, sizeof(*ptr) равен 2.
Вопрос 2
Что не так с этим фрагментом кода?
int value< 45 >; int* ptr< ; // объявляем указатель и инициализируем адресом value *ptr = &value; // присвоить ptr адрес value
Последняя строка приведенного выше фрагмента не компилируется.
Разберем эту программу подробнее.
Первая строка содержит определение обычной переменной вместе со значением инициализации. Здесь ничего особенного.
Во второй строке мы определяем новый указатель с именем ptr и сохраняем в нем адрес value . Помните, что в этом контексте звездочка является частью синтаксиса объявления указателя, а не косвенным обращением через указатель. Так что с этой строкой всё в порядке.
В третьей строке звездочка представляет собой косвенное обращение, которое используется для получения значения, на которое указывает указатель. Итак, эта строка говорит: «Получить значение, на которое указывает ptr (целочисленное значение типа int ), и перезаписать его адресом value . В этом нет никакого смысла – вы не можете присвоить адрес значению типа int !
Третья строка должна быть:
Это правильно присваивает указателю адрес переменной value .
Источник: radioprog.ru
Используя вместо самой переменной указатель на нее написать программу в соответствии с заданием
Язык Си.
Используя вместо самой переменной указатель на нее написать программу в соответствии с заданием:
Вычислить y = |x-cos(x)|.
Подскажите, как с указателями это записать.Последний раз редактировалось Alexandr-; 16.03.2013 в 20:30 .
Регистрация: 12.11.2010
Сообщений: 8,570y^:=abs(x^ — cos(x^))
Пользователь
Регистрация: 04.03.2013
Сообщений: 79
А можно в Си если не сложно.
Регистрация: 19.08.2009
Сообщений: 2,120
*y=abs(*x — cos(*x))А вы почему со мной не соглашаетесь, у вас что, импотенция? (c) ACE Valery
Пользователь
Регистрация: 04.03.2013
Сообщений: 79
#include #include #include main ()
А что не правильно, тогда в данном коде?
Регистрация: 12.11.2010Сообщений: 8,570
А какие проблемы возникают?
Ну, я бы посоветовал написать так:
int main () Пользователь
Регистрация: 04.03.2013
Сообщений: 79
Когда ввожу число программа просто с ошибкой вылетает.
Белик Виталий 🙂
Регистрация: 23.07.2007Сообщений: 57,792
ИМХО вместо scanf («%f», нужно scanf («%f»,x);
Плюс ко всему для *x,*y ты не запросил память.
Так примерно:float *x=(float*)malloc(sizeof(float)),*y=(float*)malloc(sizeof(float));
I’m learning to live.
Форумчанин
Регистрация: 20.05.2012
Сообщений: 315Alexandr-, у вас x и y не переменный типа float, а указатель на переменную типа float. Т.е памяти там нет. Поэтому и ошибка. Решение:
1) использовать вместо указателей переменные;
2) захватить память динамически (читайте про функцию malloc).Источник: www.programmersforum.ru
Помогите исправить ошибки в программе [закрыт]
Используя вместо самой переменной указатель на нее, написать программу в соответствии с заданием:
Вычислить y = cos|x3-x2|.Отслеживать
3,007 20 20 серебряных знаков 33 33 бронзовых знака
задан 8 дек 2016 в 21:58
1 1 1 бронзовый знак
А где ошибки-то, Катя?
8 дек 2016 в 22:128 дек 2016 в 22:15
нужен ли здесь тэг visual-studio ? про саму IDE не слова, но может специалисту сразу ясно особенности компилятора? но тогда, наверное и версию надо указывать?
8 дек 2016 в 22:19
8 дек 2016 в 22:53
8 дек 2016 в 23:092 ответа 2
Сортировка: Сброс на вариант по умолчанию
Вот это — double * x = new double(0.5) — лишнее, Вы ведь не под массив память выделяете. Метод precision нужно выполнить до того, как Вы что-то отправите в std::cout, иначе какой от него прок.
#include #include #define PI 3.14159265 using namespace std; int main() < double x = 0.5; double* px = double res = pow( *px, 3 ) — pow( *px, 2 ); cout )» data-controller=»se-share-sheet» data-se-share-sheet-title=»Поделиться ссылкой на ответ» data-se-share-sheet-subtitle=»» data-se-share-sheet-post-type=»answer» data-se-share-sheet-social=»facebook twitter » data-se-share-sheet-location=»2″ data-se-share-sheet-license-url=»https%3a%2f%2fcreativecommons.org%2flicenses%2fby-sa%2f3.0%2f» data-se-share-sheet-license-name=»CC BY-SA 3.0″ data-s-popover-placement=»bottom-start»>Поделиться )» title=»»>Улучшить ответ Отслеживать ответ дан 8 дек 2016 в 23:04 isnullxbhisnullxbh 5,128 22 золотых знака 1616 серебряных знаков 3838 бронзовых знаков Добавить комментарий | 0
в строке
cos = (pow(**px, 3) — pow(**px, 2));
double res = cos(abs(pow(*px,3)-pow(*px,2)));и в начале просто объявить
double *px = new double(0.5);
и еще в cpp нужно писать
Источник: ru.stackoverflow.com