Теория
1. Основы работы с указателями
- Указатели — это способ указать местоположение переменной. Вместо хранения значения (
5
или символ ‘c
’), значением указателя является адрес другой переменной.
- Переменные-указатели имеют размер (количество байт в памяти), имя и значение. Они должны быть объявлены и инициализированы.
- «Указатель» (сам по себе) — это не тип. Это конструктор типов — языковая конструкция, которая при применении к другому типу дает нам новый тип. В частности, добавление звездочки (
*
) после любого типа задает название типу, который является указателем на этот тип.
/*
объявляет переменную с именем my_char_pointer
с указателем типа на char
(переменная, указывающая на символ)
*/
char * my_char_pointer; |
/*
объявляет переменную с именем my_char_pointer
с указателем типа на char
(переменная, указывающая на символ)
*/
char * my_char_pointer;
Объявление указателя
- Объявление указателя сообщает нам имя переменной и тип переменной, на которую будет указывать этот указатель.

Присваивание указателю
- Можно выполнять присваивание указателям, изменяя их значения. Изменение значения указателя означает изменение того, куда он указывает.
- Прежде чем использовать указатель, необходимо инициализировать его (дать ему что-то, на что можно указывать).
- Если мы не инициализируем указатель перед его использованием, то он будет указывать на какое-то случайное место в программе (это может привести к сбою программы).
- Чтобы получить указатель, указывающий на некоторый адрес, нужно назначить имя полю памяти, а затем нам нужно использовать оператор
&
.
/* код, который объявляет целое число x и указатель на целое число xPtr */
int x = 5;
int *xPtr;
xPtr = &x; // устанавливает значение переменной xPtr на адрес x |
/* код, который объявляет целое число x и указатель на целое число xPtr */
int x = 5;
int *xPtr;
xPtr = &x; // устанавливает значение переменной xPtr на адрес x
x
инициализируется значением 5 в той же строке, в которой оно объявлено;
- значение
xptr
инициализируется местоположением x
;
- после того, как он инициализирован,
xPtr
указывает на переменную x
.
- Код
&x = 5;
не будет скомпилирован. Программист может получить доступ к местоположению переменной, но изменить местоположение переменной невозможно.
Разыменовывание указателя

- Как только у нас появятся указатель на адреса переменных, мы хотим использовать их, «следуя за стрелкой» и изменить ячейку, на которую ссылается указатель.
- Символ звездочка
*
— унарный оператор, который разыменовывает указатель.
- Обратите внимание, что зеленая стрелка указывает на то, что эта строка еще не была извлечена, следовательно, x все еще имеет значение 5 в концептуальном представлении. Однако, как только строка 7 будет выполнена, значение будет равно 6.
6
7
| // Пример использования оператора разыменования:
*xPtr = 6; // изменяет значение, которое хptr указывает на 6 |
// Пример использования оператора разыменования:
*xPtr = 6; // изменяет значение, которое хptr указывает на 6
- Обратите внимание, что зеленая стрелка указывает на то, что эта строка еще не была выполнена, следовательно,
x
все еще имеет значение 5 в концептуальном представлении. Однако, как только строка 7 будет выполнена, значение будет равно 6.
Два контекста в которых используется (*
), не путайте их:
1. В объявлении переменной, такой как int *p;
, звездочка является частью имени типа и сообщает нам, что нам нужен указатель на какой-либо другой тип (в нашем примере int *
— это тип p
).
2. Когда звездочка является оператором разыменования. Например, код r = *p;
присваивает переменной r
новое значение, а именно значение внутри ячейки памяти, на которую указывает p
. Код *p = r;
изменяет значение внутри ячейки памяти, на которую указывает p
, на новое значение, а именно на значение переменной r
.
- При работе с указателями, вы будете использовать звездочку сначала для объявления переменной, а затем для ее разыменования. Только переменные, имеющие тип указателя, могут быть разыменованы.
int * q = &y; // совпадает с двумя утверждениями:
int *q;
q = &y; |
int * q = &y; // совпадает с двумя утверждениями:
int *q;
q = &y;
Выводы
- Существует только три основных действия с указателем: объявление, присваивание (включая инициализацию) и разыменование.
обмен значениями
Выполнение: Необходимо передавать указатели a
и b
через аргументы функции, как показано ниже:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| #include <iostream>
using namespace std;
void swap(int *x, int *y) {
int temp = *x; // temp временно сохраняет значение, на которое указывает x, =3
*x = *y; // берем значение, на которое указывает y, которое является значением b (=4)
//и сохраняем это в значении, на которое указывает x, а именно в переменной a
*y = temp; // берем значение temp = 3 и сохраняем его в виде целого числа,
//на которое указывает y, т.е. в переменной b
}
int main() {
int a = 3;
int b = 4;
swap(&a, &b);
cout<<"a = "<< a<< ", b = "<< b << endl;
system("pause");
return 0;
} |
#include <iostream>
using namespace std;
void swap(int *x, int *y) {
int temp = *x; // temp временно сохраняет значение, на которое указывает x, =3
*x = *y; // берем значение, на которое указывает y, которое является значением b (=4)
//и сохраняем это в значении, на которое указывает x, а именно в переменной a
*y = temp; // берем значение temp = 3 и сохраняем его в виде целого числа,
//на которое указывает y, т.е. в переменной b
}
int main() {
int a = 3;
int b = 4;
swap(&a, &b);
cout<<"a = "<< a<< ", b = "<< b << endl;
system("pause");
return 0;
}
— функция swap
принимает два аргумента, x
и y
, которые являются указателями на целые числа;
— поскольку мы получаем доступ к значениям целых чисел, на которые указывают x
и y
в коде внутри swap, нам придется отменить ссылки на x и y. Итак, вот почему мы будем видеть эти звездочки (*
) перед x и y по всему коду;
— внутри main
, вместо передачи в a
и b
, мы теперь должны передать в адрес a
и адрес b
.
Работа с памятью
- На 32-разрядной машине, где размер адресов составляет 32 бита, все пространство памяти начинается с адреса
0x000000000
(в шестнадцатеричном формате каждый 0 представляет 4 бита 0) и заканчивается на 0xFFFFFFFF
(напомним, что 0x обозначает шестнадцатеричный, и что каждое F
представляет четыре двоичных 1). Каждая программа имеет в своем распоряжении все адресное пространство, и существует соглашение о том, как программа использует этот диапазон адресов.
Давайте посмотрим на рисунок, чтобы понять, где в памяти хранятся различные компоненты программы.

Null
- Если используется значение NULL — указатель ни на что не указывает.
- Нулевой указатель имеет особый тип — void *. Указатель void указывает на на любой тип — мы можем присвоить ему значение int *, double* или любой другой тип указателя, который мы хотим.
2. Указатели и массив
- Массив представляет собой константный указатель на свой первый элемент.
- Доступ к элементу массива с помощью арифметики указателей работает нормально и иногда является естественным способом доступа к элементам. Но в случае, когда нам нужен просто n-й элемент массива, было бы громоздко объявлять указатель, добавлять к нему, а затем разыменовывать его.
- Пример:
// * array : имя массива является указателем на его первый элемент, то есть на 3
void printArr(int *array, int n) {
int *ptr = array; // *ptr - это указатель, который указывает на то, куда указывает *array
for (int i = 0; i < n; i++) {
cout << *ptr; // вывод 1-й итерации - 3, вывод 2-й итерации - 1,
// вывод 3-й итерации - 7,
//чтобы указатель указывал на ячейку памяти следующего элемента в массиве:
// пример адреса ptr = 0x00b5f9a0
ptr++; // 1st 0x00b5f9a4, 2nd 0x00b5f9a8, 0x00b5f9ac,
// в массиве нет элемента, но ошибки быть не может
// это вызвало бы проблему, если бы мы разыменовали указатель
}
}
int main(){
int arr[4] = { 3,1,7,2 };
printArr(arr, 4);
} |
// * array : имя массива является указателем на его первый элемент, то есть на 3
void printArr(int *array, int n) {
int *ptr = array; // *ptr - это указатель, который указывает на то, куда указывает *array
for (int i = 0; i < n; i++) {
cout << *ptr; // вывод 1-й итерации - 3, вывод 2-й итерации - 1,
// вывод 3-й итерации - 7,
//чтобы указатель указывал на ячейку памяти следующего элемента в массиве:
// пример адреса ptr = 0x00b5f9a0
ptr++; // 1st 0x00b5f9a4, 2nd 0x00b5f9a8, 0x00b5f9ac,
// в массиве нет элемента, но ошибки быть не может
// это вызвало бы проблему, если бы мы разыменовали указатель
}
}
int main(){
int arr[4] = { 3,1,7,2 };
printArr(arr, 4);
}
- Можно достичь этой цели, проиндексировав массив. Когда мы индексируем массив, мы записываем имя массива, за которым следуют квадратные скобки, содержащие номер элемента, на который мы хотим сослаться. Важно отметить, что в C++ индексы массивов начинаются с нуля — первым элементом массива является
myArray[0]
.
// * array : имя массива является указателем на его первый элемент, то есть на 3
void printArr(int *array, int n) {
for (int i = 0; i < n; i++) {
cout << array[i]; // 1-я итерация выводит 3, 2-я итерация - 1, 3-я итерация - 7, а затем 2
}
}
int main(){
int arr[4] = { 3,1,7,2 };
printArr(arr, 4);
} |
// * array : имя массива является указателем на его первый элемент, то есть на 3
void printArr(int *array, int n) {
for (int i = 0; i < n; i++) {
cout << array[i]; // 1-я итерация выводит 3, 2-я итерация - 1, 3-я итерация - 7, а затем 2
}
}
int main(){
int arr[4] = { 3,1,7,2 };
printArr(arr, 4);
}
Поставим указатель p на начало массива:
int a[] {1,3,5,7,9};
int* p = a;
// переместите указатель на второй элемент:
p++;
p++; |
int a[] {1,3,5,7,9};
int* p = a;
// переместите указатель на второй элемент:
p++;
p++;
Доступ по индексу
int a[] {1,3,5,7,9};
int* p = a;
cout << *(p + 1) << endl; // = a[1]=3
cout << *(p + 2) << endl; // = a[2]=5 |
int a[] {1,3,5,7,9};
int* p = a;
cout << *(p + 1) << endl; // = a[1]=3
cout << *(p + 2) << endl; // = a[2]=5
Символьная строка и указатели
Символьная строка -это строка (массив символов) с нулевым символом в конце (\0).
char s[6] = "Hello"; // в конце автоматически добавляется \0
char s[] = "Hello"; // тот же самый результат
char* p = s; // H |
char s[6] = "Hello"; // в конце автоматически добавляется \0
char s[] = "Hello"; // тот же самый результат
char* p = s; // H
Для перебора символьной строки:
char* p = s;
while (*p != 0)
cout << *p++; |
char* p = s;
while (*p != 0)
cout << *p++;
или
char* p = s;
while (*p)
cout << *p++; |
char* p = s;
while (*p)
cout << *p++;
Лабораторные работы
Все задания, которые вы не успели выполнить на уроке, автоматически становятся вашим домашним заданием. Домашнее задание должно быть выполнено до следующего урока.
1. Основы работы с указателями
Лабораторная работа 1:
Выполнить: Создайте функцию для вычисления суммы значения указателя и переменной целочисленного типа. То есть функция должна возвращать сумму значения, на которое указывает указатель, и значения переменной.
Указание: Объявление функции должно быть таким:
int addP(int var1, int var2); |
int addP(int var1, int var2);
Примерный вывод:
введите первое число (указатель):
>>>2
введите второе число (которое прибавить):
>>>500
результат: указатель + 500 = 502
✍ Алгоритм:
- Создайте проект
Lesson7
. Добавьте в соответствующие папки проекта файлы: main7lab1.cpp
, L7lab1.cpp
, L7lab1.h
.
- Откройте
main7.cpp
и подключите все необходимые директивы и файлы. Выведите сообщение пользователю для ввода двух значений. Первое значение — указатель, второе — для добавления:
cout << "Lab 1:" << endl;
cout << "пожалуйста, введите первое число (для указателя): "<< endl;
int var1;
cin >> var1;
cout << "пожалуйста, введите второе число (для добавления): " << endl;
int var2;
cin >> var2;
- Переключитесь на код файла
string7.h
и подключите необходимые файлы и директивы:
#pragma once
#ifndef STRING7_H
#define STRING7_H
#include
// здесь ваш код
#endif
- Добавим объявление функции, как это было рекомендовано в тексте задания:
//lab 0: Создайте функцию для вычисления суммы указателя и переменной целочисленного типа
int addP(int var1, int var2);
- Теперь создадим реализацию функции. Откройте файл
string7.cpp
, подключите все необходимые директивы и добавьте код:
//lab 1: для вычисления суммы значения указателя и переменной целочисленного типа
int addP(int var1, int var2) {
int* p_var = &var1;
return *p_var + var2;
}
Здесь p_var
инициализируется адресом переменной var1
. Функция возвращает *p_var + var2
— что означает, что мы берем значение, на которое указывает p_var
(то есть значение var1
), и добавляем к нему значение var2
.
- Теперь можно вызвать созданную нами функцию и вывести результаты. Это необходимо сделать в файле
main7.cpp
:
cout << "Результат:" << endl;
cout << "указатель + " << var2 << " = " << addP(var1,var2) << endl;
- Запустите программу и проверьте выходные данные.
Задание 1:
Выполнить: Создайте приложение с функцией, которая принимает два аргумента - целые числа (var1
и var2
), и выполняет следующие вычисления со своими аргументами, используя при этом указатели на переменные (p1
и p2
):
1) произведение указателя и переменной: p1 * var1
2) сумма указателей: p1 + p2
3) изменение значения var1 = 5
, используя при этом указатель p1
4) изменение адреса указателя: p1
(address) = p2
(address)
Указание 1: Чтобы изменить значение переменной:
int* p1 = &var1; // объявляем и инициализируем указатель p1
*p1 = 1; // 1 - это новое значение переменной |
int* p1 = &var1; // объявляем и инициализируем указатель p1
*p1 = 1; // 1 - это новое значение переменной
Указание 2: Объявление функции должно быть следующим:
void task1(int var1, int var2); |
void task1(int var1, int var2);
Примерный вывод:
Задание 1:
Введите первое значение:
>>5
Введите второе значение:
>>10
произведение указателя и переменной: 25
сумма указателей: 15
результат смены значения var1, var1 = 5
результат ДО смены адреса, p1 = 0098F7E8
результат смены адреса, p1 = 0116FC90
[Solution and Project name: Lesson_7task1
, file name L7Task1main.cpp
, L7Task1imp.cpp
, L7Task1.h
]
2. Указатель на массив
Лабораторная работа 2:
Выполнить: Создайте функцию для вычисления суммы элементов массива. Задача должна быть выполнена с использованием двух функций. Первая функция не использует указатель, вторая функция должна использовать указатель, обращающийся к элементу массива по индексу.
Указание 1: Объявление функции:
int sumArray1(int * array, int n);
int sumArray2(int * array, int n); |
int sumArray1(int * array, int n);
int sumArray2(int * array, int n);
Примерный вывод:
array:
1 2 3 4 5
результат выполнения первой функции: sum = 15
результат выполнения второй функции: sum = 15
✍ Алгоритм:
- Сначала давайте создадим функцию, которая выполняет итерацию по элементам массива без использования указателя:
int sumArray1(int * array, int n) // array указывает на значение 1
{
// начинаем с суммы, равной 0
int AnswerSum = 0;
// count from 0 to n
for (int i = 0; i < n; i++) {
// добавляем в сумму array[i]
AnswerSum += array[i];
}
// возвращаем сумму в качестве результата
return AnswerSum;
}
int main() {
int data[] = { 1,2,3,4,5 };
cout << "array: " << endl;
for (auto x : data) {
cout << x << " ";
}
cout << endl;
int sum= sumArray1(data, 5);
cout << "результат с первой функцией: sum = " << sum<< endl;
- Рассмотрим работу с памятью:
1. Первая итерация цикла:
|
2. Вторая итерация цикла:
|
3.Третья итерация цикла:
|
4.Последняя итерация цикла:
|
Возвращаемся к функции main и область sumArray1 уничтожается:
|
- Теперь давайте создадим функцию, которая выполняет итерацию по элементам массива, используя указатель, обращающийся к элементу массива по индексу:
int sumArray2(int * array, int n) // массив указывает на значение 1
{
// начинаем с суммы, равной 0
int AnswerSum = 0;
// счетчик цикла - это указатель, указывающий на адрес 1-го элемента массива
for (int *p = array; p < array + n; p++) {
// добавим в сумму ссылку на p
AnswerSum += *p;
}
// возвращаем сумму
return AnswerSum;
}
- Вызовите функцию в функции
main
и запустите программу.
Лабораторная работа 3, Массивы:
Выполнить: Дан массив целых чисел (
1, 2, 3, 4, 5, 6, 7, 8, 9
). Необходимо каждый второй элемент массива установить в
0 (то есть четные по порядку элементы должны быть = 0). Необходимо использовать индексацию массива.
Указание 1: Объявление функции должно быть следующим:
void DelEveryEven(int* array, int n); |
void DelEveryEven(int* array, int n);
Примерный вывод:
Лабораторная работа 3, Массивы:
Исходный массив:
1 2 3 4 5 6 7 8 9
Результат:
1 0 3 0 5 0 7 0 9
✍ Алгоритм:
- Откройте приложение, которое вы создали для лабораторной 1, или, если вы ее не сделали, сделайте следующее: создайте пустое консольное приложение с именем Lesson7. Добавьте файлы:
main7.cpp
, string7.cpp
, string7.h
.
- Подключите все необходимые библиотеки и файлы (если вы не делали этого раньше).
- Откройте файл
main7.cpp
. Выведите сообщение для пользователя, объявите массив и инициализируйте его значениями:
cout << "Лабораторная работа 3, Массивы:\n";
int my_arr[] = { 1,2,3,4,5,6,7,8,9 };
- Распечатайте элементы исходного массива:
cout << "Исходный массив: " << endl;
for (auto x : my_arr) {
cout << x << " ";
}
- После этого откройте файл
string7.h
и подключим все необходимые директивы (если это не было сделано ранее):
#pragma once
#ifndef STRING7_H
#define STRING7_H
#include <iostream>
#endif
- Объявление функции должно быть таким, как рекомендовано в задании:
// Лабораторная работа 3, Массивы: Дан массив (1, 2, 3, 4, 5, 6, 7, 8, 9).
// каждый второй элемент установите в 0
void DelEveryEven(int* , int);
- Теперь необходимо написать саму функцию, т.е. ее реализацию. Откройте файл
string7.cpp
, подключите все необходимые директивы и добавьте код:
//Лабораторная работа 3, массивы
void DelEveryEven(int* array, int n) {
for (int* p = array + 1; p < array + n; p = p + 2) {
*p = 0;
}
}
Параметр *array
инициализируется адресом массива.
Здесь, в качестве счетчика цикла используется указатель на первый элемент массива+1.
Рассмотрим строку int* p = array + 1;
.
array
возвращает ячейку памяти первого элемента. Но нам необходимо установить в 0 второй элемент. Поэтому имеем array + 1
.
- В строке
*p = 0
мы разыменовываем указатель, и второй элемент устанавливаем = 0.
- Чтобы проделать те же манипуляции с четвертым элементом (четным), мы можем увеличить указатель на 2 (
p = p + 2
).
array + n
- ячейка памяти первого элемента + n
, это 9. Затем происходит выход их цикла.
- Теперь можно вызвать функцию и вывести результаты. Это необходимо сделать в
main7.cpp
:
DelEveryEven(my_arr, 9);
cout << "Результат: " << endl;
for (auto x : my_arr) {
cout << x << " ";
}
cout << endl;
- Запустите программу и проверьте выходные данные.
Задание 2:
Выполнить: Задан массив целых чисел. Создайте две функции, которые возвращают значение максимального элемента массива. Первая функция не использует указатель, вторая функция должна использовать указатель, обращающийся к элементу массива по индексу.
Указание: Объявления функций должны быть следующими:
int maxArray1(int * array, int n);
int maxArray2(int * array, int n); |
int maxArray1(int * array, int n);
int maxArray2(int * array, int n);
Примерный вывод:
array:
1 2 3 4 5
результат для первой функции: max = 5
результат для второй функции: max = 5
[Solution and Project name: Lesson_7task2
, file name L7Task2main.cpp
, L7Task2imp.cpp
, L7Task2.h
]
Задание 3:
Выполнить: Задан массив положительных и отрицательных целых чисел. Создайте приложение с функцией, которая отрицательные элементы массива трансформирует в положительные значения.
Указание: Объявление функции должно быть следующим:
void SetNegToPos(int* array, int n); |
void SetNegToPos(int* array, int n);
Примерный вывод:
Task 2:
исходный массив:
1 -2 3 4 5 -6 7 8 -9
результат:
1 2 3 4 5 6 7 8 9
[Solution and Project name: Lesson_7task3
, file name L7Task3main.cpp
, L7Task3imp.cpp
, L7Task3.h
]
Задание 4:
Выполнить: Задан массив целых чисел. Создайте функцию, которая возвращает индекс максимального элемента массива.
Указание 1: Объявление функции должно быть следующим:
int findLargestInd(int * array, int n); |
int findLargestInd(int * array, int n);
Указание 2: Внутри функции должно быть сравнение:
if (array[i] > array[largestIndex])
{
...
} |
if (array[i] > array[largestIndex])
{
...
}
Примерный вывод:
Задание 4:
массив:
1 5 3 2 1
индекс максимального элемента = 1
[Solution and Project name: Lesson_7task4
, file name L7Task4main.cpp
, L7Task4imp.cpp
, L7Task4.h
]
3. Доступ к с-строкам указателем
Лабораторная работа 4:
Выполнить: Даны две строки. Создайте функцию, которая возвращает
1
, если строки равны, и возвращает
0
в противном случае.
Указание 1: Чтобы получить доступ к символам с-строки указателем, необходимо использовать тип char
для этой строки:
Указание 2: Объявление функции должно быть следующим:
int stringEqual(const char * str1, const char * str2); |
int stringEqual(const char * str1, const char * str2);
Примерный вывод:
// lab 4:
результат для 'apple' и 'apple' : 1
++++
// lab 4:
результат для 'apple' и 'apples': 0
✍ Алгоритм:
- Объявим функцию с двумя аргументами - константными с-строками:
int stringEqual(const char * str1, const char * str2){
...
}
- Затем объявим указатели, указывающие на первые символы строк
str1
и str2
. Добавьте код в функцию:
const char* p1 = str1;
const char* p2 = str2;
- После этого необходимо создать цикл, который будет повторяться до тех пор, пока совпадают символы, на которые указывают указатели
p1
и p2
. Следует использовать оператор разыменования для получения значений:
- Кроме того, в цикле следует проверять, не достиг ли указатель
p1
конца строки: \0
(означает конец строки). В этом случае функция должна возвращать 1
(то есть ответ "true"):
if (*p1 == '\0') {
return 1;
}
- После этого, мы передвигаем
p1
и p2
к следующей букве:
- После цикла необходимо вернуть значение "нет", что в нашей функции означает
0
:
- Теперь откройте функцию
main
и создайте две символьных строки. Затем вызовите функцию:
char str1[] = "apple";
char str2[] = "apples";
cout << "результат для \'"<< str1 <<"\' и \'"<< str2 <<"\' = " << stringEqual (str1,str2) << endl;
- Запустите проект и проверьте результат.
Задание 5:
Выполнить: Создайте функцию проверки, содержит ли строка сочетание букв
'mo'
. Функция должна возвращать
1
(если сочетание найдено) или
0
(не найдено). Запрещено использовать стандартные функции. Необходимо использовать указатель.
Указание 1: Заголовок функции должен быть следующим:
bool findMo(const char * str); |
bool findMo(const char * str);
Указание 2: Чтобы проверить, достигнут ли конец строки, необходимо использовать условие цикла:
Примерный вывод:
Задание 5:
результат для строки 'the movie was nice' =: 1
[Solution and Project name: Lesson_7task5
, file name L7Task5main.cpp
, L7Task5imp.cpp
, L7Task5.h
]
Задание 6: Символьные строки
Выполнить: Создайте функцию для проверки, содержит ли строковая переменная слово "dog
". Функция возвращает 1 или 0. Необходимо использовать символьную строку (с-строку) и указатель. Не допускается использование каких-либо стандартных функций.
Указание 1: Сигнатура функции должна быть следующей:
Примерный вывод:
задание 6
строка: Hello world
результат:
0
---
задание 6
строка: my dog is nice
результат:
1
Лабораторная работа 5:
Выполнить: Измените порядок символов в строке на обратный. Используйте указатели на символы в начале и конце строки, и, перемещая оба указателя к середине, меняйте местами соответствующие символы.
Указание 1: Чтобы использовать указатель на символы строки, необходимо, чтобы строка была массивом символьного типа (c-строкой):
char str[] = "Hello world"; |
char str[] = "Hello world";
Указание 2: Заголовок функции должен быть следующим:
void reverseString (char * str); |
void reverseString (char * str);
Примерный вывод:
// lab 4:
Исходная строка:
Hello world
Результат:
dlrow olleH
✍ Алгоритм:
- Создайте консольное многофайловое приложение и добавьте в все необходимые библиотеки в (директивы) в файлы проекта.
- Откройте код главного файла (с функцией
main()
). Выведите сообщение пользователю и инициализируйте массив типа char
(строка будет обрабатываться как массив символов):
cout << "Lab 4:" << endl;
char str[] = "Hello world";
cout << "Исходная строка: " << str << endl;
- Затем откройте заголовочный файл и подключите необходимые директивы (если они требуются):
#pragma once
#ifndef STRING7_H
#define STRING7_H
#include <iostream>
#endif
- Объявление функции было указано в тексте задачи, давайте добавим этот код:
// Lab 5: Измените порядок символов в строке на обратный.
void reverseString(char* str);
str
получает адрес на str[0]
.
- Создадим реализацию функции. Откройте файл
....cpp
, добавьте необходимые директивы и вставьте следующий код:
// Lab 5: Измените порядок символов в строке на обратный.
void reverseString(char* str) {
char* str1 = str;
char* res = nullptr; // пустой указатель
while (*str1) {
res = str1;
*(str1++);
} // конец while
// ...
} // конец функции
Объявляем указатель на первый символ строки. Строка (char* res = nullptr;
) присваивает значение "null", что означает адрес 0x00000000
. Затем цикл while
перебирает адреса элементов массива. После цикла переменная res
присваивает адрес последнего символа строки, например, адрес 0x00cffae6
переменной, хранящей символ "d
" ).
- После этого будем присваивать значения переменной
*str
, сдвигая указатель с правого конца к середине. Для этого нам пригодится временная переменная t
. В то же самое время мы заменяем значения элементов массива: крайнему элемент справа присваиваем значение первого элемента. Здесь нам необходимо использовать разыменовывание указателя:
auto l = res;
while ((l) > (str)) {
auto t = *l; // временная переменная для хранения значений, начиная с последнего, 'd'
*l = *str; // разыменовывая, мы берем значение, на которое указывает *str
// и сохраняем по адресу, на который указывает *l
*str = t; // изменяем значение, на которое указывает указатель *str
l--; // передвигаемся по адресам с конца к середине
str++; // передвигаемся по адресам с начала к середине
}
- Вызовите созданную функцию и распечатайте результаты:
reverseString(str);
cout << "Исходная строка: " << str << endl;
- Запустите программу и проверьте результат.
Задание 7:
Выполнить: Дана строка, содержащая, помимо слов, положительное целое число (
"С наступающим 2021 годом вас!"
). Создайте функцию для отображения подстроки, содержащей это число.
Указание 1: Используйте следующий заголовок функции (необходимо будет сделать число из полученных цифр):
void numbFromStr(char* str); |
void numbFromStr(char* str);
Чтобы понять, когда "наступит" конец строки, можно использовать следующий цикл:
while (*str1)
{
pLast = str1; // адрес последнего символа
*(str1++);
} |
while (*str1)
{
pLast = str1; // адрес последнего символа
*(str1++);
}
Указание 2: Результат должен иметь строковый тип, поэтому следует подключить библиотеку:
Указание 3: для проверки того, является ли символ цифрой, следует создать логическую функцию:
bool isdigit(char c) {
// проверка символа: например, можно использовать c<='9' и т.п. |
bool isdigit(char c) {
// проверка символа: например, можно использовать c<='9' и т.п.
Примерный вывод:
Задание 7:
Строка: Happy 2021 year to u!
подстрока с числом: 2021
[Solution and Project name: Lesson_7task7
, file name L7Task7main.cpp
, L7Task7imp.cpp
, L7Task7.h
]
Задание 8:
Выполнить: Дана строка символов. Преобразуйте в нем все заглавные латинские буквы в строчные. Функция должна возвращать количество выполненных замен.
Указание 1: Необходимо использовать тип символьной строки:
Указание 2: Используйте следующий заголовок функции. Функция возвращает целое число (количество замен):
int to_lower(char * str); |
int to_lower(char * str);
Указание 3: Для проверки, не достигнут ли конец строки, можно использовать следующее условие цикла:
Указание 3: Чтобы проверить, является ли буква прописной, нужно использовать следующую функцию:
bool isCappital(char ch)
{
return (ch >= 'A' && ch <= 'Z');
} |
bool isCappital(char ch)
{
return (ch >= 'A' && ch <= 'Z');
}
Указание 4: Для конвертации букв в нижний регистр (в маленькие буквы) нужно использовать стандартную функцию tolower(ch)
.
Примерный вывод:
Задание 8:
Исходная строка: HelLo wOrLD
Результат: hello world
[Solution and Project name: Lesson_7task8
, file name L7Task8main.cpp
, L7Task8imp.cpp
, L7Task8.h
]
Задание 9:
Выполнить: Дана символьная строка (с-строка), которая представляет арифметическое выражение в форме: "<digit>±<digit>±...±<digit>"
, где арифметические операции "+
" или "−
" могут располагаться на месте символа "±
" (например, "4+7-2-8
"). Посчитайте результат выражения. Для решения использовать указатель.
Указание 1: Используйте следующий заголовок функции:
Указание 2: Чтобы выделить первый символ из строки, можно использовать следующий код:
int res = *expr++ - '0'; // здесь *expr++ это первый символ в строке |
int res = *expr++ - '0'; // здесь *expr++ это первый символ в строке
или так:
char r = *expr; //*expr - код в таблице ascii
int res = atoi(&r); // конвертирует код в число |
char r = *expr; //*expr - код в таблице ascii
int res = atoi(&r); // конвертирует код в число
Указание 3: Для хранения числа или знака операции можно использовать следующую концепцию:
char ch1 = *expr++; // может быть как знаком операции (символом), так и числом |
char ch1 = *expr++; // может быть как знаком операции (символом), так и числом
// чтобы использовать переменную, как число:
ch1-'0'; |
// чтобы использовать переменную, как число:
ch1-'0';
Примерный вывод:
Задание 9:
Строка: 9+4-2-0
Результат: 11
[Solution and Project name: Lesson_7task9
, file name L7Task9main.cpp
, L7Task9imp.cpp
, L7Task9.h
]