Различия
Показаны различия между двумя версиями страницы.
— | pointer [2021/07/31 21:58] (текущий) – создано - внешнее изменение 127.0.0.1 | ||
---|---|---|---|
Строка 1: | Строка 1: | ||
+ | ====== Указатели в C++ ====== | ||
+ | {{htmlmetatags> | ||
+ | metatag-description=(Указатели в языке программирования C++, получение адреса переменной, | ||
+ | }} | ||
+ | |||
+ | * [[Операторы динамического распределения памяти]]: | ||
+ | **Указатель** (пойнтер, | ||
+ | <note important> | ||
+ | <note tip> | ||
+ | |||
+ | Так как указатель содержит адрес объекта, | ||
+ | * **Объявление указателя**. < | ||
+ | * **Знак &** используется для получения адреса переменной. < | ||
+ | pi = &i; | ||
+ | cout<<& | ||
+ | cout<< | ||
+ | * **Разыменование** - изменение значения переменной на который указывает указатель.< | ||
+ | |||
+ | < | ||
+ | #include < | ||
+ | using namespace std; | ||
+ | |||
+ | /* возвращает длину строки | ||
+ | int strlen(char *s) | ||
+ | { | ||
+ | int n; | ||
+ | for (n = 0; *s != ' | ||
+ | n++; | ||
+ | return(n); | ||
+ | } | ||
+ | |||
+ | int main() | ||
+ | { | ||
+ | int i; | ||
+ | int *pi = NULL; | ||
+ | pi = &i; | ||
+ | cout<<& | ||
+ | cout<< | ||
+ | *pi=48; // | ||
+ | cout<< | ||
+ | cout<< | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====== Указатели и массивы ====== | ||
+ | В языке C существует сильная взаимосвязь между указателями и массивами , настолько сильная, | ||
+ | int a[10]; | ||
+ | </ | ||
+ | |||
+ | определяет массив размера 10, т.е. набор из 10 последовательных объектов, | ||
+ | int *pa; | ||
+ | </ | ||
+ | |||
+ | |||
+ | то присваивание < | ||
+ | pa = &a[0]; | ||
+ | </ | ||
+ | |||
+ | приводит к тому, что pa указывает на нулевой элемент массива a. Это означает, | ||
+ | x = *pa</ | ||
+ | |||
+ | будет копировать содержимое a[0] в x. | ||
+ | |||
+ | Если ра указывает на некоторый определенный элемент массива a, то по определению pa+1 указывает на следующий элемент, | ||
+ | |||
+ | ссылается на содержимое a[1], pa+i - адрес a[i], а *(pa+i) - содержимое a[i]. | ||
+ | |||
+ | Эти замечания справедливы независимо от типа переменных в массиве a. Суть определения " | ||
+ | |||
+ | Очевидно существует очень тесное соответствие между индексацией и арифметикой указателей. В действительности компилятор преобразует ссылку на массив в указатель на начало массива. В результате этого имя массива является указательным выражением. Отсюда вытекает несколько весьма полезных следствий. Так как имя массива является синонимом местоположения его нулевого элемента, | ||
+ | |||
+ | можно записать как pa = a. | ||
+ | |||
+ | Еще более удивительным, | ||
+ | |||
+ | <note warning> | ||
+ | |||
+ | Когда имя массива передается функции, | ||
+ | < | ||
+ | int strlen(char *s) | ||
+ | { | ||
+ | int n; | ||
+ | for (n = 0; *s != ' | ||
+ | n++; | ||
+ | return(n); | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | |||
+ | Операция увеличения s совершенно законна, | ||
+ | |||
+ | Описания формальных параметров в определении функции в виде char s[]; | ||
+ | |||
+ | |||
+ | и char *s; | ||
+ | |||
+ | |||
+ | совершенно эквивалентны; | ||
+ | |||
+ | * Источник: | ||
+ | |||
+ | Имя массива - это константа, | ||
+ | < | ||
+ | |||
+ | массив | ||
+ | | ||
+ | array: | array[0] | ptr:| * | | ||
+ | | array[1] | | | ||
+ | | array[2] |< | ||
+ | | ||
+ | </ | ||
+ | Следствием такой интерпретации имен массивов является то, что для того чтобы поставить указатель на начало массива, | ||
+ | ptr = array; | ||
+ | но не | ||
+ | ptr = &array; | ||
+ | Операция & перед одиноким именем массива не нужна и недопустима! | ||
+ | Такое родство указателей и массивов позволяет нам применять операцию * к имени массива: | ||
+ | |||
+ | <note important> | ||
+ | ====== Как описывать ссылки (указатели) на двумерные массивы? | ||
+ | Как описывать ссылки (указатели) на двумерные массивы? | ||
+ | < | ||
+ | #include < | ||
+ | #define First 3 | ||
+ | #define Second 5 | ||
+ | char arr[First][Second] = { | ||
+ | " | ||
+ | { ' | ||
+ | { ' | ||
+ | }; | ||
+ | char (*ptr)[Second]; | ||
+ | main(){ | ||
+ | int i; | ||
+ | ptr = arr; /* arr и ptr теперь взаимозаменимы */ | ||
+ | for(i=0; i < First; i++) | ||
+ | printf(" | ||
+ | } | ||
+ | </ | ||
+ | Указателем здесь является ptr. Отметим, | ||
+ | |||
+ | Попробуйте сами объявить | ||
+ | < | ||
+ | char (*ptr)[4]; | ||
+ | char (*ptr)[6]; | ||
+ | char **ptr; | ||
+ | </ | ||
+ | и увидеть, | ||
+ | |||
+ | Обратите также внимание на инициализацию строк в нашем примере. Строка " | ||
+ | |||
+ | { ' | ||
+ | |||
+ | ===== Указатели на функции ===== | ||
+ | **Например, | ||
+ | |||
+ | Прежде чем вводить указатель на функцию, | ||
+ | |||
+ | А теперь путём последовательности утверждений придем к обсуждению темы данного раздела урока. | ||
+ | |||
+ | 1. При использовании имени функции без последующих скобок и параметров имя функции выступает в качестве указателя на эту функцию, | ||
+ | |||
+ | 2. Это значение адреса может быть присвоено некоторому указателю, | ||
+ | |||
+ | 3. В определении нового указателя должен быть тот же тип, что и возвращаемое функцией значение, | ||
+ | |||
+ | 4. Указатель на функцию определяется следующим образом: | ||
+ | |||
+ | |||
+ | тип_функции | ||
+ | |||
+ | |||
+ | |||
+ | Например: | ||
+ | |||
+ | Примечание: | ||
+ | Второй пример: | ||
+ | |||
+ | Иллюстрируем на практике. | ||
+ | В определении указателя на функцию тип возвращаемого значения и сигнатура (типы, количество и последовательность параметров) должны совпадать с соответствующими типами и сигнатурами тех функций, | ||
+ | |||
+ | < | ||
+ | #include < | ||
+ | using namespace std; | ||
+ | void f1(void) | ||
+ | { | ||
+ | cout << "Load f1()\n"; | ||
+ | } | ||
+ | void f2(void) | ||
+ | { | ||
+ | cout << "Load f1()\n"; | ||
+ | } | ||
+ | void main() | ||
+ | { | ||
+ | void (*ptr)(void); | ||
+ | ptr = f2; // Присваивается адрес f2(). | ||
+ | (*ptr)(); | ||
+ | ptr = f1; // Присваивается адрес f1(). | ||
+ | (*ptr)(); | ||
+ | ptr(); | ||
+ | } | ||
+ | </ | ||
+ | Результат выполнения программы: | ||
+ | |||
+ | Load f2() | ||
+ | Load f1() | ||
+ | Load f1() | ||
+ | Press any key to continue | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | Здесь значением имени_указателя служит адрес функции, | ||
+ | |||
+ | При определении указатель на функцию может быть инициализирован. В качестве инициализирующего значения должен использоваться адрес функции, | ||
+ | |||
+ | При присваивании указателей на функции также необходимо соблюдать соответствие типов возвращаемых значений функций и сигнатур для указателей правой и левой частей оператора присваивания. То же справедливо и при последующем вызове функций с помощью указателей, | ||
+ | |||
+ | < | ||
+ | char f1(char) {...} // Определение функции. | ||
+ | char f2(int) {...} // Определение функции. | ||
+ | void f3(float) {...} // Определение функции. | ||
+ | int* f4(char *){...} | ||
+ | char (*pt1)(int); | ||
+ | char (*pt2)(int); | ||
+ | void (*ptr3)(float) = f3; // Инициализированный указатель. | ||
+ | void main() | ||
+ | { | ||
+ | pt1 = f1; // Ошибка - несоответствие сигнатур. | ||
+ | pt2 = f3; // Ошибка - несоответствие типов (значений и сигнатур). | ||
+ | pt1 = f4; // Ошибка - несоответствие типов. | ||
+ | pt1 = f2; // Правильно. | ||
+ | pt2 = pt1; // Правильно. | ||
+ | char с = (*pt1)(44); // Правильно. | ||
+ | с = (*pt2)(' | ||
+ | } | ||
+ | </ | ||
+ | Следующая программа отражает гибкость механизма вызовов функций с помощью указателей. | ||
+ | < | ||
+ | #include < | ||
+ | using namespace std; | ||
+ | // Функции одного типа с одинаковыми сигнатурами: | ||
+ | int add(int n, int m) { return n + m; } | ||
+ | int division(int n, int m) { return n/m; } | ||
+ | int mult(int n, int m) { return n * m; } | ||
+ | int subt(int n, int m) { return n - m; } | ||
+ | void main() | ||
+ | { | ||
+ | int (*par)(int, int); // Указатель на функцию. | ||
+ | int a = 6, b = 2; | ||
+ | char c = ' | ||
+ | while | ||
+ | { | ||
+ | cout << "\n Arguments: a = " << a <<", | ||
+ | cout << ". Result for c = \'" | ||
+ | |||
+ | switch (c) | ||
+ | { | ||
+ | case ' | ||
+ | par = add; | ||
+ | c = '/'; | ||
+ | break; | ||
+ | case ' | ||
+ | par = subt; | ||
+ | c = ' '; | ||
+ | break; | ||
+ | case ' | ||
+ | par = mult; | ||
+ | c = ' | ||
+ | break; | ||
+ | case '/': | ||
+ | par = division; | ||
+ | c = ' | ||
+ | break; | ||
+ | } | ||
+ | cout << (a = (*par)(a, | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | Результаты выполнения программы: | ||
+ | < | ||
+ | Arguments: a = 6, b = 2. Result for c = ' | ||
+ | |||
+ | Arguments: a = 8, b = 2. Result for c = '/': | ||
+ | |||
+ | | ||
+ | |||
+ | | ||
+ | Press any key to continue | ||
+ | </ | ||
+ | |||
+ | Цикл продолжается, | ||
+ | |||
+ | Массивы указателей на функции. | ||
+ | Указатели на функции могут быть объединены в массивы. Например, | ||
+ | < | ||
+ | float а = (*ptrArray[2])(' | ||
+ | </ | ||
+ | Как обычно, | ||
+ | |||
+ | Массивы указателей на функции удобно использовать при разработке всевозможных меню, точнее программ, | ||
+ | |||
+ | < | ||
+ | #include < | ||
+ | using namespace std; | ||
+ | |||
+ | /* Определение функций для обработки меню | ||
+ | (функции фиктивны т. е. реальных действий не выполняют): | ||
+ | |||
+ | void act1 (char* name) | ||
+ | { | ||
+ | cout <<" | ||
+ | } | ||
+ | void act2 (char* name) | ||
+ | { | ||
+ | cout << " | ||
+ | } | ||
+ | void act3 (char* name) | ||
+ | { | ||
+ | cout << "Read file... " << name; | ||
+ | } | ||
+ | void act4 (char* name) | ||
+ | { | ||
+ | cout << "Mode file... " << name; | ||
+ | } | ||
+ | void act5 (char* name) | ||
+ | { | ||
+ | cout << "Close file... " << name; | ||
+ | } | ||
+ | |||
+ | void main() | ||
+ | { | ||
+ | // Создание и инициализация массива указателей | ||
+ | void (*MenuAct[5])(char*) | ||
+ | |||
+ | int number; | ||
+ | char FileName[30]; | ||
+ | |||
+ | // Реализация меню | ||
+ | cout << "\n 1 - Create"; | ||
+ | cout << "\n 2 - Delete"; | ||
+ | cout << "\n 3 - Read"; | ||
+ | cout << "\n 4 - Mode"; | ||
+ | cout << "\n 5 - Close"; | ||
+ | |||
+ | while (1) // Бесконечный цикл. | ||
+ | { | ||
+ | while (1) | ||
+ | { // Цикл продолжается до ввода правильного номера. | ||
+ | cout << " | ||
+ | cin >> number; | ||
+ | if (number>> | ||
+ | |||
+ | cout << " | ||
+ | } | ||
+ | if (number != 5) | ||
+ | { | ||
+ | cout << "Enter file name: "; | ||
+ | cin >> FileName; // Читать имя файла. | ||
+ | } | ||
+ | else break; | ||
+ | // Вызов функции по указателю на нее: | ||
+ | (*MenuAct[number-1])(FileName); | ||
+ | } // Конец бесконечного цикла. | ||
+ | } | ||
+ | </ | ||
+ | Пункты меню повторяются, | ||
+ | |||
📌 Удобный подбор VPS по параметрам доступен на DIEGfinder.com - официальном инструменте проекта DIEG. Это часть единой экосистемы, созданной для того, чтобы помочь быстро найти подходящий VPS/VDS сервер для любых задач хостинга.
📌 Для тестирования скриптов, установщиков VPN и Python-ботов рекомендуем использовать надежные VPS на короткий срок. Подробнее о быстрой аренде VPS для экспериментов - читайте здесь.
💥 Подпишись в Телеграм 💥 и задай вопрос по сайтам и хостингам бесплатно!7 Самых Популярных Статей
- Как запустить скрипты и веб-приложения на Python
- Что такое страны TIER 1,2,3
- 7 способов сравнения файлов по содержимому в Windows или Linux
- Установка и тестирование веб-панели HestiaCP
- Nginx простые примеры конфигурации
- top, htop, atop определение загрузки ОС (Load average, LA)
- Использование rsync в примерах
7 Самых Популярных Обзоров
- Хостинг для Python-скриптов и приложений
- ТОП 4 лучших антидетект браузеров (Бесплатные & Платные)
- Подборка купонов (промокоды) на хостинг, антидетект браузеры
- Обзор THE.Hosting (PQ Hosting): надежный хостинг с профессиональной поддержкой
- Хостинг в России
- Хостинг в Европе
- Обзор браузера Dolphin {anty} для мультиаккаунтинга