Библиотека собеса по C++ | вопросы с собеседований
19.4K subscribers
452 photos
5 videos
252 links
Вопросы с собеседований по C++ и ответы на них.

По рекламе: @proglib_adv

Учиться у нас: https://proglib.io/w/9ccf302b

Для обратной связи: @proglibrary_feeedback_bot
Download Telegram
Как получить размер файла в байтах?

В C++17 была добавлена библиотека filesystem, которая упрощает работу с файловой системой

#include <filesystem>

int main()
{
std::uintmax_t size = std::filesystem::file_size("file.txt");
}


❗️Стоит учитывать, что если файл был открыт не только вами, то размер может быть изменён извне
🔥7
🍔 Отладочные макросы

Отладочные макросы в C++ используются для упрощения процесса отладки и диагностики программного кода

Основные отладочные макросы включают assert, static_assert и пользовательские макросы

🍓 assert

Макрос assert определён в заголовочном файле <cassert> и используется для проверки логических выражений во время выполнения программы. Если выражение вернёт false, assert завершает выполнение программы и выводит сообщение об ошибке
#include <cassert>

void test(int x) {
assert(x > 0 && "x must be positive");
// остальной код функции
}

int main() {
test(5); // проходит проверку
test(-3); // приводит к ошибке во время выполнения
return 0;
}


🍒 static_assert

static_assert введён в C++11 и позволяет проверять условия на этапе компиляции. Это особенно полезно для проверки условий, которые должны выполняться всегда, независимо от состояния программы во время выполнения

#include <type_traits>

template <typename T>
void check() {
static_assert(std::is_integral<T>::value, "T must be an integral type");
}

int main() {
check<int>(); // проходит проверку
check<float>(); // ошибка компиляции: T must be an integral type
return 0;
}


❗️Начиная с C++11 и C23 static_assert является ключевым словом

🫐 Пользовательские отладочные макросы

Вы также можете определять собственные макросы для отладки, которые помогут вам выводить дополнительную информацию или выполнять специфические проверки

#include <iostream>

#define DEBUG

#ifdef DEBUG
#define DEBUG_PRINT(x) std::cout << x << std::endl
#else
#define DEBUG_PRINT(x)
#endif

int main() {
int value = 42;
DEBUG_PRINT("Value: " << value);
return 0;
}
👍5🔥1
🍯 Задача с собеседования

Входные данные:
- Дан массив состоящий из int'ов
- Повторяющихся элементов в списке нет

Задача:
- Нужно преобразовать этот массив в строку, сворачивая соседние по числовому ряду числа в диапазоны

Примеры:
[1,4,5,2,3,9,8,11,0] => "0-5,8-9,11"
[1,4,3,2] => "1-4"
[1,4] => "1,4"

#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <sstream>

std::string convertToRanges(std::vector<int>& nums) {
if (nums.empty()) {
return "";
}

// Сортируем вектор
std::sort(nums.begin(), nums.end());

std::vector<std::string> ranges;
int start = nums[0];
int prev = start;

for (size_t i = 1; i < nums.size(); ++i) {
if (nums[i] != prev + 1) {
// Если последовательность прервалась, добавляем диапазон
if (start == prev) {
ranges.push_back(std::to_string(start));
}
else {
ranges.push_back(std::to_string(start) + "-" + std::to_string(prev));
}
start = nums[i];
}
prev = nums[i];
}

// Добавляем последний диапазон
if (start == prev) {
ranges.push_back(std::to_string(start));
}
else {
ranges.push_back(std::to_string(start) + "-" + std::to_string(prev));
}

// Объединяем диапазоны в одну строку
std::ostringstream result;
for (size_t i = 0; i < ranges.size(); ++i) {
if (i > 0) {
result << ",";
}
result << ranges[i];
}

return result.str();
}

int main() {
std::vector<int> nums1 = { 1, 4, 5, 2, 3, 9, 8, 11, 0 };
std::cout << convertToRanges(nums1) << std::endl; // Ожидаемый вывод: "0-5,8-9,11"

std::vector<int> nums2 = { 1, 4, 3, 2 };
std::cout << convertToRanges(nums2) << std::endl; // Ожидаемый вывод: "1-4"

std::vector<int> nums3 = { 1, 4 };
std::cout << convertToRanges(nums3) << std::endl; // Ожидаемый вывод: "1,4"

return 0;
}
👍2
Почему использование std::endl может быть менее эффективным, чем \n в некоторых случаях?

Всё дело в буферизации вывода

- std::endl не только добавляет символ новой строки (\n) в поток, но и явно сбрасывает (flushes) буфер вывода. Это означает, что данные из буфера немедленно отправляются в устройство вывода (например, на экран или в файл).

- \n, напротив, просто добавляет символ новой строки в поток без принудительного сброса буфера. Буферизация остаётся управляемой самой стандартной библиотекой, и данные выводятся только тогда, когда буфер заполняется или программа явно вызывает std::flush.

#include <iostream>
#include <chrono>

int main() {
auto start = std::chrono::high_resolution_clock::now();

for (int i = 0; i < 10000; ++i) {
std::cout << "Hello, World!" << std::endl; // Принудительный сброс после каждой строки
}

auto middle = std::chrono::high_resolution_clock::now();

for (int i = 0; i < 10000; ++i) {
std::cout << "Hello, World!\n"; // Буферизация, меньше операций I/O
}

auto end = std::chrono::high_resolution_clock::now();

std::cout << "Time with std::endl: "
<< std::chrono::duration_cast<std::chrono::milliseconds>(middle - start).count()
<< " ms\n";

std::cout << "Time with \\n: "
<< std::chrono::duration_cast<std::chrono::milliseconds>(end - middle).count()
<< " ms\n";

return 0;
}


В результате выполния кода будет заметно, что время вывода с \n будет меньше.
👍4
📚 Загадочная библиотека setjmp.h — неочевидный инструмент для управления потоком выполнения


Библиотека setjmp.h предоставляет два необычных макроса:

setjmp — сохраняет текущее состояние программы (регистры, стек) в буфер jmp_buf

longjmp — «откатывает» выполнение к сохранённому состоянию, как прыжок во времени



#include <stdio.h>
#include <setjmp.h>

jmp_buf jump_buffer;

void risky_function() {
printf("Готовимся к прыжку...\n");
longjmp(jump_buffer, 42); // Прыжок обратно в setjmp!
}

int main() {
int ret = setjmp(jump_buffer);
if (ret == 0) {
printf("Первая инициализация...\n");
risky_function();
} else {
printf("Вернулись с кодом: %d\n", ret); // Выведет 42!
}
return 0;
}


⚠️ Осторожно!

• Не заменяет исключения — нет вызова деструкторов (как в C++)
• Опасность утечек — если между setjmp и longjmp выделялась память, она не освободится
• Портит стек — может сломать логику функций

🛠 Где может пригодиться?

• Обработка критических ошибок (а-ля «аварийный выход»)
• Код для встраиваемых систем, где нужно быстро восстановить состояние
• Нестандартные хаки (но лучше так не делать 😈)

Библиотека C/C++ разработчика
👍3🥱3
Какое утверждение о модулях C++20 является верным?
Anonymous Quiz
56%
Модули компилируются быстрее заголовочных файлов и не подвержены проблемам One Definition Rule
5%
Модули могут экспортировать только классы, но не функции
14%
Модули автоматически решают все проблемы циклических зависимостей
4%
Модули работают только с компилятором GCC
😃 Задача на выходные

#include <iostream>
#include <thread>
#include <chrono>

struct Counter {
long long a;
long long b;
};

void increment(long long& val) {
for (int i = 0; i < 100'000'000; i++) val++;
}

int main() {
Counter cnt{ 0, 0 };
auto start = std::chrono::high_resolution_clock::now();

std::thread t1(increment, std::ref(cnt.a));
std::thread t2(increment, std::ref(cnt.b));
t1.join(); t2.join();

auto end = std::chrono::high_resolution_clock::now();
std::cout << "Time: " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() << "ms\n";
return 0;
}


Вопрос: Как можно ускорить работу данного кода?


📍Навигация: ВакансииЗадачиСобесы

Библиотека C/C++ разработчика

#междусобойчик
Please open Telegram to view this post
VIEW IN TELEGRAM
😁4🔥1🌚1
🍴 Задача на выходные: до скольки считает стандарт?

Стандарт C++ гарантирует, что можно зарегистрировать минимум N функций через std::atexit. Найди баг в этом коде:

#include <cstdlib>
#include <iostream>

void dummy_handler() {
static int counter = 0;
std::cout << "Handler " << ++counter << "\n";
}

int main() {
// Регистрируем 100 обработчиков
for (int i = 0; i < 100; ++i) {
if (std::atexit(dummy_handler) != 0) {
std::cerr << "Failed at " << i << "\n";
return 1;
}
}

std::cout << "All registered!\n";
return 0;
}


Вопрос: Гарантирует ли стандарт, что все 100 обработчиков зарегистрируются? Какое минимальное количество гарантируется? Как правильно обработать ошибку регистрации?

📍Навигация: ВакансииЗадачиСобесы

Библиотека C/C++ разработчика

#междусобойчик
Please open Telegram to view this post
VIEW IN TELEGRAM
🧩 Выходной челлендж: дочисти парсер команд

У тебя есть заготовка интерактивной оболочки — read-eval-print loop. Осталось дописать несколько ключевых частей.

#include <iostream>
#include <sstream>
#include <vector>
#include <string>

std::vector<std::string> parseCommand(const std::string& line) {
// TODO: разбить строку на токены по пробелам
// Учти: несколько пробелов подряд — не ошибка
}

void execute(const std::vector<std::string>& tokens) {
if (tokens.empty()) return;

if (tokens[0] == "echo") {
// TODO: вывести все аргументы через пробел
} else if (tokens[0] == "exit") {
exit(0);
} else {
// TODO: вывести "Unknown command: <имя команды>"
}
}

int main() {
std::string line;
while (true) {
std::cout << "> ";
if (!std::getline(std::cin, line)) break;
execute(parseCommand(line));
}
}


Задача: заполни три TODO.

💬 Покажи своё решение — особенно интересны варианты с std::istringstream и ручным разбором.


📍Навигация: ВакансииЗадачиСобесы

Библиотека C/C++ разработчи
ка

#междусобойчик
Что такое линкер (linker) в процессе сборки C++ программы?
Anonymous Quiz
3%
Инструмент, преобразующий исходный код в объектный файл
91%
Инструмент, объединяющий объектные файлы в исполняемый файл
1%
Инструмент, оптимизирующий байт-код программы
1%
Инструмент, проверяющий синтаксис исходного кода
😁2🤔1
🔥 Найди баг: копирование строки сломало указатель

#include <string>
#include <iostream>

struct Token {
std::string value;
const char* ptr;

Token(const std::string& s) : value(s), ptr(value.data()) {}
};

int main() {
Token t1("hi");
Token t2 = t1; // копируем

std::cout << t1.ptr << "\n"; // "hi"
std::cout << t2.ptr << "\n"; // ???
}


‼️ Задача: найди баг (если он есть), объясни, почему он связан с SSO, и предложи исправление.

📍Навигация: ВакансииЗадачиСобесы

Библиотека C/C++ разработчика

#междусобойчик
Please open Telegram to view this post
VIEW IN TELEGRAM
🩹 Задача на выходные: const-итерация по Range

#include <ranges>
#include <vector>

void process(const auto& view) {
for (auto x : view) { (void)x; }
}

int main() {
std::vector<int> data = { 1, 2, 3, 4, 5 };
auto pred = [](int x) {
return x % 2 == 0;
};

auto filtered = data | std::views::filter(pred);
process(filtered); // не компилируется
}


Объясни механизм: почему begin() const невозможен в принципе, а не по недосмотру комитета.


📍Навигация:
ВакансииЗадачиСобесы

Библиотека C/C++ разработчика

#междусобойчик
Please open Telegram to view this post
VIEW IN TELEGRAM
❤‍🔥182💯169150🔥150🥰148
🏆 Задача на выходные: string_view vs string в API

Перед тобой два варианта API логгера. Выбери лучший и обоснуй. Нет единственно верного ответа — важна аргументация.

#include <string>
#include <string_view>

// Вариант А
class LoggerA {
public:
void log(const std::string& msg);
void setPrefix(const std::string& prefix);
std::string getLastMessage() const;
};

// Вариант Б
class LoggerB {
public:
void log(std::string_view msg);
void setPrefix(std::string_view prefix);
std::string_view getLastMessage() const;
};

// Контекст использования:
// 1. log() вызывается тысячи раз в секунду
// 2. setPrefix() — один раз при старте
// 3. getLastMessage() — для диагностики
// 4. Логгер хранит последнее сообщение внутри



✏️ Напиши в комментариях: какой метод в каком варианте правильный, а какой — нет. Предложи «идеальный» LoggerC.


📍Навигация: ВакансииЗадачиСобесы

Библиотека C/C++ разработчика

#константная_правильность
Please open Telegram to view this post
VIEW IN TELEGRAM