C/C++ Ready | Программирование
15.8K subscribers
1.5K photos
94 videos
749 links
Авторский канал по разработке на C и C++.
Ресурсы, гайды, задачи, шпаргалки.
Информация ежедневно пополняется!

Cотрудничество: @energy_c

РКН: https://clck.ru/3QREHc
Download Telegram
Проверяем числовой аргумент командной строки в C++!

Допустим, утилита принимает номер порта. Преобразовать строку в число мало, нужно отклонить пустой ввод, лишние символы и значение вне допустимого диапазона. Иначе строка вроде 8080abc может превратиться в 8080.

Возьмём строку из аргументов программы и подготовим границы для разбора:
std::string_view input = argv[1];
const char* first = input.data();
const char* last = first + input.size();


Перед чтением argv[1] проверь, что argc равен 2. Пустую строку тоже лучше сразу отклонить, чтобы сообщение об ошибке было понятным.

Теперь вызовем std::from_chars. Он не создаёт временную строку и сообщает результат через код ошибки:
int port = 0;
auto [next, error] = std::from_chars(first, last, port);


Одного успешного преобразования недостаточно. Проверим, что функция действительно дошла до конца входа:
if (error != std::errc{} || next != last) {
std::cerr << "Нужен целый номер порта\n";
return 1;
}


Так мы отсекаем не только буквы после числа, но и переполнение int. В отличие от некоторых удобных преобразований, from_chars не пропускает пробелы в начале строки автоматически.

Осталось проверить предметное правило. Для TCP и UDP допустимы номера от 1 до 65535:
if (port < 1 || port > 65535) {
std::cerr << "Порт вне диапазона\n";
return 1;
}


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

📣 C++ Ready | #практика
Please open Telegram to view this post
VIEW IN TELEGRAM
❤8👍5🔥2🤝1
Шпаргалка по std::variant в C++!

На картинке показано, как один объект хранит значение одного из нескольких заранее известных типов. Видно, что у variant есть активная альтернатива, её индекс и место под самый большой из перечисленных типов.

Сохрани, чтобы не потерять!

📣 C++ Ready | #ресурс
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥14❤10👍4
Почему std::accumulate может потерять дробную часть?

Допустим, в векторе лежат измерения с дробной частью:
std::vector<double> v{1.5, 2.5};


Сумму хочется получить одним вызовом, поэтому пишем:
auto sum = std::accumulate(v.begin(), v.end(), 0);


По логики должно получиться 4.0. Но последний аргумент здесь не просто стартовое значение. Его тип определяет тип аккумулятора и результата. Целочисленный 0 заставляет std::accumulate складывать числа в int.

Проверим результат:
std::cout << sum;


На экране будет 3. После прибавления 1.5 аккумулятор станет 1, а после прибавления 2.5 станет 3. Дробная часть отбрасывается на каждом шаге, а не только в конце.

Для дробной суммы достаточно начать с double:
auto sum = std::accumulate(v.begin(), v.end(), 0.0);


Теперь получится 4.0. Если результат сразу присвоить переменной double, но оставить внутри вызова 0, это не поможет:
double sum = std::accumulate(v.begin(), v.end(), 0);


К моменту присваивания дробные части уже потеряны. Для больших целочисленных сумм действует та же логика, начальное значение типа 0LL позволяет считать в long long и снижает риск переполнения int.

📣 C++ Ready | #совет
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥11❤7👍4
Как не дать медленной нейросети превратить прямой эфир в просмотр прошлого?

RTSP-конвейер принимает видео с камеры и передаёт кадры в YOLO для распознавания объектов.

В статье разбирают
• почему очередь кадров увеличивает задержку
• как ограничить накопление и обрабатывать свежие кадры
• как C++20 помогает управлять потоками

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

Продолжай читать на Habr!


📣 C++ Ready | #статья
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥8❤5👍4
Шпаргалка по интерфейсу std::string!

На картинке собраны основные части std::string: создание и изменение строки, доступ к символам, поиск, сравнение и преобразование. Это компактная карта стандартного типа, к которой удобно возвращаться при работе с текстом.

Сохрани, чтобы не потерять!

📣 C++ Ready | #ресурс
Please open Telegram to view this post
VIEW IN TELEGRAM
❤14🔥9👍4😁1
Почему std::scoped_lock удобнее, когда нужно захватить несколько mutex?

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

Наивный вариант выглядит просто:
from.mutex.lock();
to.mutex.lock();

from.balance -= amount;
to.balance += amount;

to.mutex.unlock();
from.mutex.unlock();


Но у такого кода сразу несколько проблем. Если между lock и unlock произойдёт исключение, mutex может остаться заблокированным. А если другой поток захватит те же mutex в обратном порядке, можно получить deadlock.

Чуть лучше использовать lock_guard:
std::lock_guard first(from.mutex);
std::lock_guard second(to.mutex);


Но порядок всё равно остаётся важным. Если в другом месте кода сначала захватят to.mutex, а потом from.mutex, риск взаимной блокировки никуда не исчезает.

Для таких случаев в C++17 есть std::scoped_lock:
std::scoped_lock lock(from.mutex, to.mutex);

from.balance -= amount;
to.balance += amount;


std::scoped_lock умеет захватывать несколько mutex сразу и использует алгоритм, который помогает избежать deadlock при одновременном захвате. Освобождение произойдёт автоматически при выходе из области видимости.

Это особенно удобно в функциях, где есть ранний return:
std::scoped_lock lock(a.mutex, b.mutex);

if (a.closed || b.closed) {
return false;
}


Код становится короче, а главное, намерение читается сразу. Мы не вручную управляем блокировками, а объявляем область, внутри которой защищены сразу несколько ресурсов.

📣 C++ Ready | #совет
Please open Telegram to view this post
VIEW IN TELEGRAM
❤9🔥6👍4
Считаем частоту ошибок в логах на C++!

Представим, что у нас есть строки логов, и нужно быстро понять, какие типы ошибок встречаются чаще всего. Для этого удобно пройтись по строкам, вытащить код ошибки и накопить счётчик в unordered_map.

Начнём с набора строк:
std::vector<std::string> logs = {
"ERROR DB_TIMEOUT user=42",
"INFO request ok",
"ERROR AUTH_FAILED user=15",
"ERROR DB_TIMEOUT user=77"
};


Сделаем контейнер для статистики:
std::unordered_map<std::string, int> errors;


Теперь пройдёмся по логам и будем учитывать только строки, которые начинаются с ERROR:
for (const auto& line : logs) {
std::istringstream in(line);

std::string level;
std::string code;

in >> level >> code;

if (level == "ERROR") {
++errors[code];
}
}


Здесь operator[] удобен тем, что для нового ключа сразу создаёт значение по умолчанию. Для int это 0, поэтому ++errors[code] работает без отдельной проверки.

Выведем результат:
for (const auto& [code, count] : errors) {
std::cout << code << " " << count << "\\n";
}


На выходе можно увидеть, какие ошибки повторяются чаще:
DB_TIMEOUT 2
AUTH_FAILED 1


Можно считать не только ошибки, но и HTTP-коды, имена endpoint-ов, типы событий, пользователей или любые другие категории из текстового потока.

Если порядок вывода важен, после подсчёта можно переложить пары в vector и отсортировать по count:
std::vector<std::pair<std::string, int>> top(
errors.begin(),
errors.end()
);


А затем отсортировать по убыванию:
std::ranges::sort(top, {}, [](const auto& item) {
return -item.second;
});


Получается маленькая, но полезная заготовка для анализа логов, отчётов и диагностических утилит.

📣 C++ Ready | #практика
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6❤4🔥3
Ищем пять самых частых событий на C++!

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

В этой задаче:
• считаем количество каждого события
• частично сортируем кандидатов
• выводим только первые пять


Такой подход удобен для небольшого отчёта по логам и метрикам. Для очень большого числа уникальных значений отдельно продумай ограничение памяти.

📣 C++ Ready | #задача
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
❤5🔥3👍2
Почему std::string_view может быть опасным?

std::string_view удобен, когда нужно передать строку без копирования. Он хранит только указатель и размер, поэтому работает быстро.

Но важный нюанс в том, что string_view не владеет памятью. Он просто смотрит на данные, которые должны жить где-то ещё.

Опасный код может выглядеть так:
std::string_view name = make_name();

std::cout << name;


Если make_name возвращает std::string, временная строка уничтожится сразу после выражения. string_view останется смотреть в память, которая уже не принадлежит строке.

Похожая проблема бывает при возврате view из функции:
std::string_view title() {
std::string value = "ready";
return value;
}


Здесь value живёт только внутри функции. После return ссылка на его данные становится висячей.

Безопаснее возвращать owning-строку:
std::string title() {
return "ready";
}


А string_view хорошо подходит для параметров функции, когда вызывающий код владеет строкой:
void log_name(std::string_view name) {
std::cout << name;
}


Так функция не копирует строку, но и не пытается продлить её жизнь.

string_view можно хранить недолго и принимать как параметр, но нельзя использовать как замену std::string для владения данными.

📣 C++ Ready | #совет
Please open Telegram to view this post
VIEW IN TELEGRAM
❤5🔥5👍4
Шпаргалка по основным операциям std::vector!

std::vector хранит элементы в непрерывной памяти и автоматически управляет их количеством. Карточка помогает быстро вспомнить не только названия методов, но и то, как меняется содержимое контейнера после вызова.

На картинке собраны создание и присваивание вектора, size и capacity, доступ к элементам, итераторы, вставка и удаление. Отдельно показаны reserve, push_back и emplace_back, а также разница между размером и выделенной памятью.

📣 C++ Ready | #ресурс
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10❤4🔥3
Почему operator[] у map может изменить контейнер?

В C++ у std::map и std::unordered_map индексатор выглядит как обычное чтение значения по ключу.

Например, просто проверить счётчик:
std::map<std::string, int> hits;

std::cout << hits["guest"];


Код выведет 0, но важный момент в другом. Ключ guest будет добавлен в map, потому что operator[] создаёт значение по умолчанию, если ключа ещё нет.

После такого чтения контейнер уже не пустой:
std::cout << hits.size();


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

Если нужно только проверить наличие ключа, лучше использовать contains:
if (hits.contains("guest")) {
std::cout << "found";
}


Если нужно получить значение без вставки, подходит find:
auto it = hits.find("guest");

if (it != hits.end()) {
std::cout << it->second;
}


А operator[] оставляй для случаев, когда вставка действительно ожидается:
hits["guest"]++;


В такой строке поведение как раз удобно. Если ключа не было, счётчик создастся с нулём и сразу увеличится.

Для чтения используй find или contains, для создания и обновления можно использовать operator[].

📣 C++ Ready | #совет
Please open Telegram to view this post
VIEW IN TELEGRAM
❤7👍6🔥4
Схема раздельной компиляции проекта на C++!

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

Эта схема помогает понять, почему объявление функции обычно находится в заголовке, реализация в .cpp, а ошибки вроде undefined reference возникают на этапе линковки, а не компиляции исходного файла.

Сохрани, чтобы не потерять!

📣 C++ Ready | #ресурс
Please open Telegram to view this post
VIEW IN TELEGRAM
❤12👍3🔥1
Почему результат std::clamp не всегда стоит сохранять как ссылку?

std::clamp ограничивает число снизу и сверху. Это удобно для громкости, процентов, координат и других значений с допустимым диапазоном:
int volume = std::clamp(requested, 0, 100);


Но у функции есть деталь, которую легко пропустить. Она возвращает не новое значение, а константную ссылку на один из трёх аргументов.

Когда все аргументы живут достаточно долго, ссылка может быть полезна:
int value = 120;
int low = 0;
int high = 100;
const int& limited = std::clamp(value, low, high);


Здесь limited ссылается на high. Если изменить high, изменится и значение, видимое через limited.

А вот с временными границами хранить ссылку опасно:
int value = 120;
const int& limited = std::clamp(value, 0, 100);


В этом случае выбранная граница 100 существует только до конца выражения. После точки с запятой limited становится висячей ссылкой, и читать её нельзя.

Сохранить результат по значению:
int limited = std::clamp(value, 0, 100);


Для обычных чисел копия почти ничего не стоит. А код перестаёт зависеть от времени жизни аргументов. Отдельно проверяйте, что нижняя граница не больше верхней.

📣 C++ Ready | #совет
Please open Telegram to view this post
VIEW IN TELEGRAM
❤6👍4🔥4
Зачем сегодня выбирать C++ и где его возможности действительно нужны?

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

В статье разбирают:
• почему контроль над памятью даёт производительность и повышает цену ошибок
• как санитайзеры и профилировщики помогают искать проблемы до продакшена
• где C++ оправдан, а где проще выбрать другой язык и масштабировать систему

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

Продолжай читать на Habr!


📣 C++ Ready | #ресурс
Please open Telegram to view this post
VIEW IN TELEGRAM
❤8👍2🔥2
Почему std::move иногда всё равно копирует объект?

std::move сам ничего не переносит, а только приводит выражение к rvalue-ссылке, чтобы компилятор мог выбрать подходящий конструктор.

Посмотри на тип объекта до и после вызова:
const std::string source = "payload";
auto&& candidate = std::move(source);


candidate имеет тип const std::string&&. Константность не исчезает, потому что std::move сохраняет квалификаторы исходного объекта.

Обычный move-конструктор принимает неконстантную rvalue-ссылку:
std::string(std::string&& other);


Он может изменить исходную строку, забрав её внутренний буфер. С const std::string&& это невозможно, поэтому такой конструктор не подходит.

Для константного источника компилятор обычно выбирает копирующий вариант:
std::string::string(const std::string& other);


В результате код остаётся корректным, но большой буфер копируется. Сам факт, что написан std::move, не гарантирует перемещение.

Если владение можно передать, оставь источник неконстантным:
std::string source = "payload";
std::string result = std::move(source);


После операции source остаётся валидным объектом, но его значение не определено для прикладной логики. Его можно присвоить заново или уничтожить, но не стоит полагаться на прежнее содержимое.

При возврате локального объекта std::move обычно тоже не нужен:
std::string make_message() {
    std::string result = "ready";
    return result;
}


Компилятор может применить NRVO и создать результат сразу в месте вызова. Принудительный std::move в return способен помешать этой оптимизации и не делает код быстрее автоматически.

📣 C++ Ready | #совет
Please open Telegram to view this post
VIEW IN TELEGRAM
❤6👍5🔥3