TGViewer
Channel Public Channel
C/C++ | Вопросы собесов

C/C++ | Вопросы собесов

@easy_c_plus

Сайт: https://easyoffer.ru/
Все каналы: t.me/+xGeAw6ckJ4liYzQy

Контакт для рекламы: @sendme_ads
Subscribers
4.19K
Photos
37
Videos
1
Links
1.6K

Showing posts older than #2135 · Back to latest

Older Posts 20 shown
Post #2134 907
🤔 Как проверять равенство двух float?

Равенство двух float проверяется с учётом допустимой разницы (эпсилон), чтобы избежать ошибок из-за неточности представления:
∣a−b∣<ϵ, где ϵ — небольшое значение, например 10−610^{-6}.
Такой подход помогает корректно сравнивать близкие числа, которые могут отличаться в пределах допустимой погрешности.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚Базу знаний
  • 👍 2
Post #2133 891
🤔 Какая есть оптимизация в строках С++?

Стандартный класс std::string в C++ имеет несколько механизмов оптимизации для повышения производительности и уменьшения расхода памяти. Рассмотрим основные из них.

🟠Small String Optimization (SSO) – Оптимизация малых строк
Если строка маленькая (обычно до 15-23 байт, зависит от реализации), std::string не выделяет динамическую память, а хранит символы прямо внутри объекта std::string.
Обычно std::string содержит:
- Указатель на динамическую память
- Размер строки
- Вместимость (capacity)
Но если строка короткая, память не выделяется в куче (heap), а данные хранятся прямо внутри объекта std::string.
#include <iostream>
#include <string>

int main() {
std::string small = "Hello"; // Использует SSO
std::string large = "This is a very long string that will not fit SSO"; // Динамическая память

std::cout << "Small string: " << small << "\n";
std::cout << "Large string: " << large << "\n";
}


🟠Copy-on-Write (COW) – Оптимизация копирования (устарела)
Раньше (до C++11) std::string использовал технику Copy-on-Write (COW), где несколько строк могли разделять одну и ту же память, пока одна из них не изменялась.
Почему отказались?
Не потокобезопасно (если один поток меняет строку, другой может получить неожиданные данные). В C++11 было добавлено std::move, которое делает копирование дешевле. SSO делает COW менее нужной.
std::string a = "Hello";
std::string b = a; // Копия не создается, оба указывают на одну память
b[0] = 'X'; // Теперь создаётся отдельная копия


🟠Move Semantics – Семантика перемещения (C++11)
Вместо дорогостоящего копирования std::string теперь можно перемещать с помощью std::move(), передавая владение ресурсом без копирования.
#include <iostream>
#include <string>

int main() {
std::string a = "Hello, world!";
std::string b = std::move(a); // Перемещение, a становится пустой
std::cout << "b: " << b << "\n"; // "Hello, world!"
}


🟠Reserve() – Избегание частых выделений памяти
Если заранее известно, сколько символов потребуется, можно использовать .reserve(size), чтобы избежать многократных перевыделений памяти.
std::string str;
str.reserve(100); // Сразу выделяет память на 100 символов
for (int i = 0; i < 100; i++) {
str += 'a'; // Не перевыделяет память 100 раз
}


Ставь 👍 и забирай 📚 Базу знаний
  • 👍 1
Post #2132 657
🤔 При помощи чего сохраняются exception?

Используется std::exception_ptr, который может сохранять и передавать исключения между потоками.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚Базу знаний
Post #2131 793
🤔 Когда возникает самый худший случай поиска в unordered_set?

В unordered_set, который основан на хеш-таблице, самый худший случай поиска возникает, когда структура данных деградирует до такой степени, что время поиска становится линейным, то есть \(O(n)\), где \(n\) — количество элементов в контейнере. Это происходит по нескольким причинам:

🟠Плохая хеш-функция
Играет ключевую роль в распределении элементов по "ведрам" (buckets) в unordered_set. Если хеш-функция плохо разрабатывается и генерирует одинаковые или близкие хеш-коды для большого количества ключей, элементы начинают накапливаться в очень небольшом числе ведер. Это приводит к тому, что большинство операций поиска, вставки и удаления начинают зависеть от числа элементов в самом длинном связном списке, а не от общего количества ведер, что сильно ухудшает производительность.

🟠Высокая степень загрузки
Загрузка хеш-таблицы (load factor) — это отношение количества элементов в хеш-таблице к количеству ведер. Когда load factor становится слишком высоким, вероятность коллизий увеличивается, что также приводит к увеличению длины цепочек в каждом ведре. Хотя unordered_set автоматически увеличивает количество ведер при увеличении количества элементов, в случаях с экстремально высоким load factor поиск может деградировать до линейного.

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

Ставь 👍 и забирай 📚 Базу знаний
Post #2130 728
🤔 Принцип подстановки Барбары Лисков?

Принцип Лисков (LSP) утверждает, что объект базового класса должен быть заменим объектом производного класса без изменения поведения программы. Производный класс не должен нарушать инварианты и контракт базового класса.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚Базу знаний
  • 🔥 1
Post #2129 809
🤔 Что такое placement new?

placement new – это специальная форма оператора new, которая размещает объект в уже выделенной памяти вместо того, чтобы выделять её заново.
placement new
НЕ выделяет память (мы сами передаём адрес).
Просто вызывает конструктор в указанном месте.
void* ptr = malloc(sizeof(int));   // Выделяем память вручную
int* num = new(ptr) int(42); // Размещаем объект в этой памяти


Синтаксис placement new
new (адрес) Type(аргументы конструктора);


адрес – указатель на уже выделенную память.
Type(аргументы конструктора) – объект создаётся в этом месте.
#include <iostream>

int main() {
char buffer[sizeof(int)]; // Буфер памяти (размер как у int)

int* p = new (buffer) int(123); // Размещаем int в этом буфере

std::cout << "Значение: " << *p << std::endl; // 123
}


🚩Когда используется `placement new`?

🟠Оптимизация выделения памяти
Например, если у нас часто создаются и удаляются объекты, но память уже есть. Не нужно каждый раз вызывать new и delete.

🟠Размещение объектов в особых областях памяти
Например, на стеке (char buffer[100]), в shared memory, или в выделенной заранее области (malloc).

🟠Создание объекта в заранее выделенном массиве
Стандартный std::vector использует placement new внутри себя!

🟠Кастомные аллокаторы (`std::allocator`)
Например, в играх и в embedded-разработке, где управление памятью критично.

🚩`placement new` и классы

Если объект – это класс, placement new вызывает его конструктор в выделенной памяти.
#include <iostream>
#include <cstdlib> // Для malloc/free

class MyClass {
public:
int x;
MyClass(int val) : x(val) { std::cout << "Конструктор!\n"; }
~MyClass() { std::cout << "Деструктор!\n"; }
};

int main() {
void* buffer = malloc(sizeof(MyClass)); // Выделяем сырую память
MyClass* obj = new (buffer) MyClass(42); // Размещаем объект

std::cout << "Значение: " << obj->x << std::endl;

obj->~MyClass(); // Ручной вызов деструктора!
free(buffer); // Освобождаем память
}


Вывод
Конструктор!
Значение: 42
Деструктор!


🚩Ошибки при `placement new`

Забыл вызвать деструктор
MyClass* obj = new (buffer) MyClass(10);
// delete obj; // НЕЛЬЗЯ! Обычный `delete` не работает
free(buffer); // Память утекла! Деструктор не вызвался


Нужно вызвать вручную
obj->~MyClass();
free(buffer);


placement new в одном буфере несколько раз (без очистки)*
void* buffer = malloc(sizeof(MyClass));
MyClass* obj1 = new (buffer) MyClass(1);
MyClass* obj2 = new (buffer) MyClass(2); // Перезапишет obj1!


Нужно сначала вызвать деструктор
obj1->~MyClass();
MyClass* obj2 = new (buffer) MyClass(2);


Освободил память перед вызовом деструктора
free(buffer);  // Память освободили
obj->~MyClass(); // ОШИБКА! Память уже не существует


Правильный порядок
obj->~MyClass();
free(buffer);


Ставь 👍 и забирай 📚 Базу знаний
  • 👍 2
Post #2128 643
🤔 В каком стандарте появился override?

Ключевое слово override было введено в стандарт C++11. До его появления разработчики не могли явно указывать, что метод должен переопределять виртуальный метод базового класса. Это приводило к ошибкам, если сигнатура метода в базовом классе менялась или имя метода было неправильно написано.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚Базу знаний
  • 👍 3
Post #2127 675
🤔 Для чего при перегрузке оператора присваивания возвращать ссылку?

При перегрузке оператора = в C++ принято возвращать ссылку на текущий объект (*this). Это позволяет цепочечное присваивание (a = b = c), а также улучшает производительность и удобство использования.

🚩Правильная сигнатура оператора `=`

class MyClass {
public:
MyClass& operator=(const MyClass& other) {
if (this != &other) { // Защита от самоприсваивания
// Копируем данные
}
return *this; // Возвращаем ссылку на текущий объект
}
};


🚩Что даёт возвращение ссылки (`T&`)?

Поддержка цепочки присваивания (a = b = c)
a = b = c;  // Интерпретируется как a = (b = c);


Пример
#include <iostream>

class MyClass {
public:
int value;
MyClass(int v) : value(v) {}

MyClass& operator=(const MyClass& other) {
if (this != &other) { // Защита от самоприсваивания
value = other.value;
}
return *this;
}
};

int main() {
MyClass a(1), b(2), c(3);
a = b = c; // Работает, потому что `b = c` возвращает ссылку на `b`
std::cout << "a: " << a.value << ", b: " << b.value << ", c: " << c.value << std::endl;
}


Вывод
a: 3, b: 3, c: 3


🟠Убираем лишнее копирование (повышаем производительность)
Если оператор = вернёт не ссылку, а объект, то произойдёт лишнее копирование.

Плохо (возвращаем объект, а не ссылку)
MyClass operator=(const MyClass& other) { // ⚠️ Ошибка: возвращаем объект
value = other.value;
return *this; // Здесь создаётся временный объект (лишняя копия!)
}


Хорошо (возвращаем T&)
MyClass& operator=(const MyClass& other) { // ✅ Возвращаем ссылку
value = other.value;
return *this;
}


🚩Поддержка `if (a = b)`

Если оператор = вернёт ссылку, мы можем использовать его в условии:
if ((a = b).value == 42) { // ОК
std::cout << "Присваивание выполнено, a == 42";
}


🚩Что если вернуть `void`?

Если оператор = вернёт void, цепочка a = b = c; не будет работать.
class MyClass {
public:
void operator=(const MyClass& other) { // ⚠️ Ошибка!
value = other.value;
}
};

int main() {
MyClass a, b, c;
a = b = c; // ❌ Ошибка: `b = c` возвращает void, а `a = void` не работает!
}


Ставь 👍 и забирай 📚 Базу знаний
Post #2126 677
🤔 Что будет, если вызвать оператор delete на nullptr?

Если вызвать оператор delete на nullptr, ничего не произойдет. Это безопасная операция, так как стандарт гарантирует, что освобождение nullptr не вызовет ошибок или исключений.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚Базу знаний
  • 👍 3
  • 🔥 3
Post #2125 795
🤔 Сложность поиска в бинарных деревьях логарифмическая, всегда ли так?

Нет, сложность поиска в бинарных деревьях не всегда логарифмическая. Она зависит от структуры дерева. Хотя логарифмическая сложность \(O(\log N)\) считается идеальной, это справедливо только для сбалансированных бинарных деревьев. Давайте разберём, когда эта сложность сохраняется, а когда может увеличиваться.

🚩Идеальный случай: сбалансированное бинарное дерево

Если бинарное дерево поиска (Binary Search Tree, BST) сбалансировано, глубина дерева пропорциональна \( \log_2 N \), где \(N\) – количество узлов. В этом случае поиск, вставка и удаление элемента выполняются за \(O(\log N)\).

🚩Худший случай: несбалансированное дерево

Если дерево несбалансировано, то оно может выродиться в связный список, где каждый узел имеет только одного потомка (левого или правого).

🚩Как избежать вырождения дерева?

Чтобы поддерживать сложность операций \(O(\log N)\), используют сбалансированные бинарные деревья, такие как:
🟠AVL-деревья
Поддерживают балансировку после каждой операции вставки/удаления.
🟠Красно-чёрные деревья
Гарантируют, что глубина дерева остаётся \(O(\log N)\).
🟠B-деревья и B+ деревья
Используются для работы с большими объёмами данных, например, в базах данных.

Ставь 👍 и забирай 📚 Базу знаний
Post #2124 652
🤔 Как работает priority_queue?

priority_queue управляет элементами на основе их приоритетов, используя бинарную кучу: на вершине всегда элемент с наивысшим приоритетом, а операции добавления и удаления имеют сложность O(log n).

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚Базу знаний
  • 👍 2
Post #2123 773
🤔 Чем отличаются STL контейнеры vector и array?

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

🟠Различия в управлении памятью
std::vector использует динамическую память, выделяемую в куче (heap). Его размер может изменяться во время выполнения.
std::array использует статическую память, выделяемую в стеке (stack) или в статической области памяти, и его размер фиксирован на этапе компиляции.
#include <vector>
#include <array>
#include <iostream>

int main() {
std::vector<int> vec = {1, 2, 3}; // Размер может изменяться динамически
vec.push_back(4); // Добавляем новый элемент

std::array<int, 3> arr = {1, 2, 3}; // Размер фиксирован, нельзя добавить новый элемент

std::cout << "Vector size: " << vec.size() << std::endl; // Выведет 4
std::cout << "Array size: " << arr.size() << std::endl; // Выведет 3

return 0;
}


🟠Гибкость и изменение размера
std::vector позволяет изменять размер в процессе работы, автоматически выделяя новую память при необходимости.
std::array имеет фиксированный размер, который нельзя изменить после создания.
std::vector<int> v = {1, 2, 3};
v.push_back(4); // Увеличиваем размер

std::array<int, 3> a = {1, 2, 3};
// a.push_back(4); // Ошибка! У std::array нет метода push_back


🟠Производительность
std::array работает быстрее, так как все данные хранятся в непрерывном участке памяти и нет затрат на динамическое выделение.
std::vector может требовать дополнительное время при изменении размера, так как может потребоваться новое выделение памяти и копирование элементов.
#include <vector>
#include <array>
#include <chrono>
#include <iostream>

int main() {
constexpr int N = 1'000'000;

std::vector<int> vec(N, 1); // Динамический массив
std::array<int, N> arr{}; // Статический массив

auto start = std::chrono::high_resolution_clock::now();
for (int i = 0; i < N; ++i) vec[i] += 1;
auto end = std::chrono::high_resolution_clock::now();
std::cout << "Vector time: "
<< std::chrono::duration_cast<std::chrono::microseconds>(end - start).count()
<< " us" << std::endl;

start = std::chrono::high_resolution_clock::now();
for (int i = 0; i < N; ++i) arr[i] += 1;
end = std::chrono::high_resolution_clock::now();
std::cout << "Array time: "
<< std::chrono::duration_cast<std::chrono::microseconds>(end - start).count()
<< " us" << std::endl;

return 0;
}


🟠Совместимость с C-API
std::array хранит данные как обычный C-массив, поэтому можно легко передавать его в функции, ожидающие int*.
std::vector использует динамическую память, но можно получить указатель на внутренний буфер с помощью data().
void processArray(int* arr, size_t size) {
for (size_t i = 0; i < size; ++i) {
std::cout << arr[i] << " ";
}
}

int main() {
std::array<int, 3> arr = {1, 2, 3};
std::vector<int> vec = {4, 5, 6};

processArray(arr.data(), arr.size()); // std::array можно передавать в C-функции
processArray(vec.data(), vec.size()); // std::vector тоже можно передавать

return 0;
}


🟠Итераторы и стандартные алгоритмы
Оба контейнера поддерживают итераторы и совместимы со стандартными алгоритмами из #include <algorithm>
#include <iostream>
#include <vector>
#include <array>
#include <algorithm>

int main() {
std::vector<int> vec = {3, 1, 4, 1, 5};
std::array<int, 5> arr = {3, 1, 4, 1, 5};

std::sort(vec.begin(), vec.end());
std::sort(arr.begin(), arr.end());

for (int n : vec) std::cout << n << " "; // 1 1 3 4 5
std::cout << std::endl;
for (int n : arr) std::cout << n << " "; // 1 1 3 4 5

return 0;
}


Ставь 👍 и забирай 📚 Базу знаний
Post #2122 663
🤔 Что такое рекурсивный mutex?

Рекурсивный mutex — это тип мьютекса, который позволяет одному и тому же потоку захватывать его несколько раз без блокировки. Это полезно, когда одна и та же функция вызывает себя рекурсивно или косвенно через другие функции и при этом требуется захватить один и тот же мьютекс. В отличие от обычного мьютекса, рекурсивный мьютекс учитывает количество захватов и освобождается только после того, как поток выполнит соответствующее количество вызовов unlock(). Это предотвращает взаимоблокировки при рекурсивных вызовах.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚Базу знаний
Post #2120 741
🤔 Что такое POD тип ?

POD (Plain Old Data) тип — это простой тип данных в C++, который совместим с C и обладает определенными свойствами, делающими его поведение предсказуемым и простым.

🚩Основные характеристики POD типов

🟠Тривиальность (Trivial)
Тривиальный конструктор (конструктор по умолчанию, копирования и перемещения). Тривиальный деструктор. Компилятор генерирует их автоматически, если они не определены явно.

🟠Стандартная макетируемость (Standard Layout)
Все нестатические члены данных имеют один и тот же доступ (public, private, или protected). Нет виртуальных функций и нет виртуальных базовых классов. Все базовые классы являются POD типами. Стандартные правила выравнивания и макетирования данных в памяти.

struct MyPOD {
int x;
float y;
};


Пример НЕ POD типа
struct NonPOD {
int x;
virtual void func() {} // Наличие виртуальной функции делает тип не POD
};


🚩Применение POD типов

🟠Совместимость с C
Поскольку POD типы совместимы с C, они могут быть безопасно использованы в смешанных C/C++ программах.

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

🟠Оптимизация
Компиляторы могут лучше оптимизировать код с POD типами из-за их простоты и предсказуемости.

🚩Проверка, является ли тип POD

В C++11 и новее можно использовать стандартную библиотеку для проверки, является ли тип POD
#include <type_traits>

struct MyPOD {
int x;
float y;
};

struct NonPOD {
int x;
virtual void func() {}
};

int main() {
std::cout << std::is_pod<MyPOD>::value << std::endl; // вывод: 1 (true)
std::cout << std::is_pod<NonPOD>::value << std::endl; // вывод: 0 (false)
return 0;
}


Ставь 👍 и забирай 📚 Базу знаний
  • 👍 1
  • 🔥 1
Post #2119 710
🤔 Что будет, если несколько раз вызвать lock?

1. Если используется обычный std::mutex, повторный вызов lock из того же потока вызовет deadlock.
2. Для избежания этой ситуации можно использовать std::recursive_mutex, который позволяет одному потоку многократно блокировать мьютекс


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚Базу знаний
Post #2118 790
🤔 В каких STL контейнерах внутри находится хеш таблица?

🚩В стандартной библиотеке шаблонов (STL) C++ хеш-таблица используется для реализации следующих контейнеров

🟠std::unordered_map
Ассоциативный контейнер, который хранит пары ключ-значение, с уникальными ключами. Обеспечивает амортизированное среднее время доступа, вставки и удаления за O(1).

🟠std::unordered_multimap
Ассоциативный контейнер, который хранит пары ключ-значение, где ключи могут повторяться. Обеспечивает амортизированное среднее время для основных операций за O(1), несмотря на дублирование ключей.

🟠std::unordered_set
Ассоциативный контейнер, который хранит уникальные элементы, неупорядоченные. Обеспечивает амортизированное среднее время для основных операций за O(1).

🟠std::unordered_multiset
Ассоциативный контейнер, который хранит элементы, где значения могут повторяться, неупорядоченные. Обеспечивает амортизированное среднее время для основных операций за O(1), несмотря на дублирование элементов.

Ставь 👍 и забирай 📚 Базу знаний
Post #2117 712
🤔 Какая польза от безымянного пространства имен?

Безымянное пространство имен используется для ограничения области видимости имен в файле. Оно предотвращает конфликты имен при работе с глобальными переменными или функциями, так как они становятся доступными только в этом файле.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚Базу знаний
  • 👍 2
Post #2115 744
🤔 Что можешь сказать плохого про MVC?

МVC (Model-View-Controller) – это классический паттерн разделения логики приложения, но у него есть недостатки, особенно в сложных проектах.

🚩Запутанные связи между компонентами

Взаимодействие между Model, View и Controller может стать сложным. Когда модель изменяется, нужно обновлять все представления (View), которые её используют. Если контроллер слишком тесно связан с представлением, код становится сложно поддерживать.
В сложных GUI-приложениях (например, Qt, MFC) модель может изменяться из разных мест, и контроллеру трудно управлять обновлениями.

🚩Слабая масштабируемость в больших проектах

MVC не даёт чёткого разделения ответственности, если приложение очень сложное.
Один контроллер может управлять сразу несколькими представлениями, что создаёт запутанный код. Часто приходится создавать "промежуточные" контроллеры для разруливания логики → код становится сложнее.
В сложных веб-приложениях (React, Angular) MVC превращается в "спагетти", потому что представления (View) и контроллеры (Controller) начинают выполнять логические задачи, которые должны быть в модели.
Использовать слоистую архитектуру (Layered Architecture) или Service Layer.

🚩"Толстый" контроллер (Fat Controller)

Если бизнес-логика попадает в контроллер, он становится раздутым и сложно поддерживаемым. Контроллер начинает обрабатывать данные, проверять их, а не просто передавать управление.
class UserController {
public:
void login(std::string username, std::string password) {
if (username.empty() || password.empty()) {
std::cout << "Ошибка: пустые данные\n";
return;
}

if (username == "admin" && password == "1234") {
std::cout << "Вход выполнен!\n";
} else {
std::cout << "Неверный логин/пароль\n";
}
}
};


Здесь контроллер сам проверяет данные и логику аутентификации, что нарушает принцип разделения ответственности (SRP).
Вынести бизнес-логику в Model или Service.
Использовать слоистую архитектуру (Service Layer, Repository Pattern).
class AuthService {
public:
bool authenticate(const std::string& username, const std::string& password) {
return (username == "admin" && password == "1234");
}
};

class UserController {
AuthService authService;
public:
void login(const std::string& username, const std::string& password) {
if (authService.authenticate(username, password)) {
std::cout << "Вход выполнен!\n";
} else {
std::cout << "Неверный логин/пароль\n";
}
}
};


🚩Трудности тестирования (особенно View и Controller)

Model тестируется легко, потому что она "чистая" (без UI-кода).
Controller и View сложно тестировать, потому что они зависят от пользовательского ввода и интерфейса.
Unit-тестирование UI-кода практически невозможно, требуется мокирование.

🚩Не всегда подходит для многопоточных приложений

В многопоточных приложениях несколько представлений могут обращаться к одной модели, вызывая гонки данных.
Если контроллер один на несколько потоков, он становится "узким местом" (bottleneck).

Ставь 👍 и забирай 📚 Базу знаний
Post #2114 622
🤔 В set сложность вставки, удаления, поиска - логарифмическая?

Да, в стандартной реализации `std::set` в C++ вставка, удаление и поиск выполняются за O(log n), так как `set` реализован как сбалансированное бинарное дерево (обычно красно-чёрное дерево). Логарифмическая сложность обеспечивается благодаря тому, что элементы хранятся в отсортированном порядке, и операция деления дерева пополам происходит при каждом шаге. Это делает операции эффективными даже для больших наборов данных.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚Базу знаний
  • 👍 1
Post #2113 618
🤔 Что в себе хранит указатель?

Указатель (pointer) — это переменная, которая хранит адрес другой переменной в памяти. По сути, указатель "указывает" на местоположение другого значения.

🚩Основные аспекты указателей

🟠Адрес памяти
Указатель хранит числовое значение, которое представляет адрес ячейки памяти. Этот адрес указывает на начальную позицию объекта, на который ссылается указатель.

🟠Тип данных
Указатели типизированы. Тип указателя указывает на тип данных, который находится по адресу. Это важно для правильной интерпретации данных, хранящихся по адресу.

🚩Объявление и инициализация указателей

🟠Объявление
Указатель объявляется с использованием символа * после типа данных.
      int* ptr; // Указатель на целое число
double* dptr; // Указатель на число с плавающей


🟠Инициализация
Указатели могут быть инициализированы адресом существующей переменной с помощью оператора & (адресной операции).
      int value = 42;
int* ptr = &value; // ptr хранит адрес переменной


Пример использования указателей
#include <iostream>

int main() {
int value = 42;
int* ptr = &value; // ptr хранит адрес переменной value

std::cout << "Значение value: " << value << std::endl; // Вывод: 42
std::cout << "Адрес value: " << &value << std::endl; // Вывод: Адрес переменной value
std::cout << "Значение, на которое указывает ptr: " << *ptr << std::endl; // Вывод: 42
std::cout << "Адрес, хранимый в ptr: " << ptr << std::endl; // Вывод: Адрес переменной value

// Изменение значения через указатель
*ptr = 10;
std::cout << "Новое значение value: " << value << std::endl; // Вывод: 10

return 0;
}


🚩Операции с указателями

🟠Доступ к значению по указателю
Используя оператор разыменования *, можно получить или изменить значение, на которое указывает указатель.
      int value = 42;
int* ptr = &value;
*ptr = 10; // Изменение значения value через


🟠Адресные операции
& оператор используется для получения адреса переменной.
* оператор используется для разыменования указателя.

🟠Арифметика указателей
Указатели поддерживают арифметические операции, такие как сложение и вычитание, что позволяет перемещаться по массивам.
      int arr[] = {1, 2, 3, 4, 5};
int* ptr = arr;
std::cout << *(ptr + 1) << std::endl; // Вывод: 2 (второй элемент массива


🚩Типы указателей

🟠Указатель на объект
Указатель, который хранит адрес конкретного объекта.
      int value = 42;
int* ptr = &value;


🟠Указатель на массив
Указатель может указывать на первый элемент массива, и арифметика указателей позволяет перемещаться по массиву.
   int arr[] = {1, 2, 3, 4, 5};
int* ptr = arr;


🟠Указатель на функцию
Указатель, который хранит адрес функции.
      void func() {
std::cout << "Hello, World!" << std::endl;
}

void (*funcPtr)() = func;
funcPtr(); // Вызов функции через указатель


Ставь 👍 и забирай 📚 Базу знаний
  • 👍 2
Older posts →
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →