Закрепить практические навыки работы с динамической памятью, указателями, ссылками и функциями в C++.
- Изучить различия между передачей параметров в функцию по значению, по указателю и по ссылке.
- Освоить применение модификатора
constс указателями и ссылками для защиты данных. - Исследовать представление статических и динамических массивов в памяти, понять принципы адресной арифметики.
- Научиться корректно работать с динамически выделяемой памятью: создавать массивы, передавать их в функции, изменять и освобождать.
- Попрактиковаться в организации модульного проекта с использованием системы сборки CMake.
lab_pointers/
├── CMakeLists.txt # корневой файл сборки
├── README.md # этот файл
├── 01_task/ # передача параметров в функцию
├── 02_task/ # const с указателями и ссылками
├── 03_task/ # массивы и адресная арифметика
├── 04_task/ # передача динамического массива в функцию
└── 05_task/ # зубчатые двумерные массивы
В каждой папке задания:
NN_task/
├── CMakeLists.txt # сборка задания
├── main.cpp # точка входа
├── <utils>.h # объявления
├── <utils>.cpp # реализации
└── README.md # ответы на контрольные вопросы
- Объявить три версии функции
modifyвmodifiers.h:modifyByValue(int),modifyByPointer(int*),modifyByReference(int&). - В
modifiers.cppреализовать их так, чтобы каждая выводила адрес параметра и значение до и после присваиванияx = 999. - В
main.cppреализоватьdemonstrateBasicDifferences(): объявитьint a = 10, последовательно вызвать все три функции, после каждого вызова вывести текущее значениеa. - Ответить на вопросы в
01_task/README.md: почему по значению оригинал не меняется, разница между указателем и ссылкой, когда что безопаснее, что предпочтительнее для больших неизменяемых объектов, что копируется при каждом способе передачи.
- Объявить в
const_utils.hфункцииprintValue(const int*),printValueRef(const int&),tryModify(const int*),setValue(int*, int). - В
main.cpp: объявитьint x = 42, вызватьprintValue(&x)иprintValueRef(x), изменитьxчерезsetValue(&x, 100). - Создать
const int* p1 = &x, вызватьtryModify(p1), показать закомментированной строку*p1 = 50;с пояснением ошибки компиляции. - Создать
int y = 200; int* const p2 = &y;, показать закомментированной строкуp2 = &x;(ошибка), выполнить*p2 = 300;и вывести новоеy. - Ответить на вопросы в
02_task/README.md: различиеconst int*иint* const, почему нельзя менять данные черезconst int*, почемуconst T&принимает неконстантные объекты, что происходит при попытке переприсвоить константный указатель, зачемconstв параметрах, что лучше для больших неизменяемых объектов.
- Объявить в
array_utils.hфункциюfillArray(int*, int), в.cppреализовать заполнение по правилуarr[i] = (i + 1) * 7. - В
main.cppсоздать статическийint staticArr[5]и динамическийint* dynamicArr = new int[5], заполнить оба массива черезfillArray. - Для каждого массива вывести таблицу:
i,arr[i],&arr[i],arr + i, разница в байтах с предыдущим элементом (—дляi = 0). - Вывести
sizeof(staticArr)иsizeof(dynamicArr), освободить динамический массив черезdelete[]. - Ответить на вопросы в
03_task/README.md: почему имя массива работает как указатель, отличие статического от динамического по времени жизни и размещению, почему различаетсяsizeof, почему&arr[1] - &arr[0]равно 1, а не 4, можно ли переприсвоитьstaticArr/dynamicArr, что будет безdelete[], почемуint arr[]работает с обоими массивами.
- Объявить в
array_utils.hтри функции:modifyElementsOnly(int*, int),reallocateArray(int*&, int&),fakeByValue(int*, int). - В
main.cpp: создатьint* data = new int[3]{1, 2, 3}, вывести адрес и содержимое. - Вызвать
modifyElementsOnly(data, 3)— элементы изменились, адрес тот же. - Вызвать
fakeByValue(data, 3)— внутриarr = nullptr, внешнийdataне изменился. - Вызвать
reallocateArray(data, size)— освободить старый массив, выделить новый размеромsize * 2, заполнить, обновитьdataиsize. - Вывести новый адрес и содержимое (должны отличаться), освободить память.
- Ответить на вопросы в
04_task/README.md: почему нельзя изменить указатель при передачеint*, что означаетint*&, можно ли передать массив «по значению», что будет при забытомsize, зачем освобождать старый массив, какие риски при передаче указателя без размера.
- Объявить в
matrix_utils.hфункцииcreateMatrix(int, int),fillMatrix(int**, int, int),printMatrix(int**, int, int),freeMatrix(int**, int),resizeMatrix(int**&, int&, int&, int, int). - В
matrix_utils.cppреализовать: созданиеint**с построчнымnew int[cols]и инициализацией нулями, заполнениеmatrix[i][j] = i * j, вывод матрицы, освобождение всех строк и массива указателей, перевыделение с сохранением старых данных в левом верхнем углу и инициализацией новых элементов нулями. - В
main.cppсоздать матрицу 3×4, заполнить, вывести, расширить до 5×6 черезresizeMatrix, снова вывести, освободить память. - Собрать с
-fsanitize=addressи убедиться, что программа не имеет утечек. - Ответить на вопросы в
05_task/README.md: почему нельзяvoid f(int arr[][])без размеров, чемint**отличается отint[rows][cols]в памяти, почему строки освобождают до массива указателей, что будет приdelete[]толькоmatrix, зачемint**&вresizeMatrix, какие риски уint**.
mkdir build && cd build
cmake ..
cmake --build .Исполняемые файлы появятся в build/:
01_task, 02_task, task03, 04_task, 05_task.
Задание 5 собирается с AddressSanitizer:
target_compile_options(05_task PRIVATE -fsanitize=address -g)
target_link_options(05_task PRIVATE -fsanitize=address)При наличии утечек, double free или использования после delete[]
ASan выведет подробный отчёт.
- C++23 (
set(CMAKE_CXX_STANDARD 23)) - CMake ≥ 3.10
- Никаких глобальных переменных — все данные передаются через параметры.
- Код разделён на
.h/.cpp, в заголовках используется#pragma once. - В каждом
README.mdзадания — ответы на контрольные вопросы.
Архив со всем проектом lab_pointers/ загружается в SmartLMS курса.