uk
Feedback

Не попадись на ботовода! Telemetrio знаходить і позначає такі канали мітками 👉 Хочеш бачити мітку, оформляй підписку

C++ geek

C++ geek

Відкрити в Telegram

Учим C/C++ на примерах

Показати більше
3 504
Підписники
Немає даних24 години
-27 днів
-2630 днів
Архів дописів
Невидимый враг: std::vector<bool> Кажется, что std::vector<bool> — обычный вектор из bool. Но на самом деле это не так. Под капотом vector<bool> хранит биты плотно упакованными (1 бит на элемент), а не как bool (обычно 1 байт). Из-за этого: - Элемент — это не bool&, а прокси-объект (std::vector<bool>::reference). - Нельзя получить настоящий указатель на элемент: &v[0] не даст bool*. - Код работает медленнее из-за лишней возни с битовыми масками. Пример неожиданности:

std::vector<bool> v{true, false};
auto p = &v[0]; // ❌ Ошибка! Нет bool*
Хотите реально быстрый и предсказуемый контейнер? - Используйте std::vector<char> или std::vector<uint8_t> для хранения флагов. - Или подключите boost::dynamic_bitset, если нужна именно битовая упаковка. Итог: vector<bool> — это костыль ради экономии памяти. Если важны скорость и нормальная семантика — обходите стороной. ➡️ @cpp_geek

Почему std::vector<bool> — это ловушка Если вы думаете, что std::vector<bool> — обычный вектор, только для bool, то… сюрприз! Это не совсем так. Чтобы экономить память, стандартная библиотека делает его специализацией, хранящей биты упакованно. Цена — нетипичное поведение:

std::vector<bool> v{true, false, true};
bool* p = &v[0]; // Ошибка! Тут нет обычного bool*
Элементы vector<bool> — это не настоящие bool, а прокси-объекты. Они ведут себя как bool, но фактически это обёртки над битами. Итог: - нельзя получить указатель на элемент; - работа с ними медленнее, чем с bool; - поведение иногда ломает шаблонный код, который ожидает нормальные ссылки и указатели. Что делать? Если вам нужна коллекция булей — берите std::vector<char> или std::vector<uint8_t>. Памяти уйдёт чуть больше, но всё будет предсказуемо и быстро. std::vector<bool> стоит использовать только если критична экономия памяти, и вы понимаете все его подводные камни. ➡️ @cpp_geek

2 августа Яндекс проведет C++ Zero Cost Conf: конференцию о прикладном C++ Мероприятие для практикующих C++ разработчиков, где профессионалы из ведущих компаний поделятся реальными кейсами. В Москве (офлайн+онлайн), в Белграде (офлайн+онлайн) и Санкт-Петербурге (только офлайн). Часть программы в Москве: — «Алиасинг памяти в компиляторе и в вашей программе»: глубокий разбор строгого алиасинга и его влияния на производительность. Спикеры: Константин Владимиров и Владислав Белов, Syntacore. — «Performance Puzzlers»: анализируем производительность и учимся извлекать пользу через эксперименты с пайплайнами, векторизацией и не только. Спикер: Сергей Слотин, C++ эксперт и автор «Алгоритмики». — «C++20 Модули — практическое внедрение»: как интегрировать C++20 модули в большие существующие проекты с поддержкой старых стандартов. Спикер: Антон Полухин, Техплатформа Городских сервисов Яндекса. Полный список докладов по городам на сайте. Помимо этого, в Москве участники смогут поучаствовать в код-гольфе на C++, в воркшопе по Perforator и решить реальные задачи по надежности сервисов в Case Lab. Код-ревью пройдет во всех городах. Регистрация открыта! Участие бесплатно.

RAII — твой лучший друг (и почему не стоит бояться умных указателей) Старый добрый new / delete — это классика, но и источник утечек, крашей и боли. В современном C++ ручное управление памятью почти всегда антипаттерн. Решение — RAII (Resource Acquisition Is Initialization): ресурсы живут ровно столько, сколько объект, который ими владеет. Ушёл объект из области видимости — ресурс освободился. Пример с умными указателями:

#include <memory>
#include <iostream>

struct Foo {
    Foo() { std::cout << "Init\n"; }
    ~Foo() { std::cout << "Destroy\n"; }
};

void bar() {
    std::unique_ptr<Foo> p = std::make_unique<Foo>(); // RAII
    // делаем что-то
} // тут автоматически вызовется ~Foo()
Что важно знать: * std::unique_ptr — владение в единственном числе, идеально для большинства случаев. * std::shared_ptr — разделённое владение (но дороже по производительности). * Никогда не делай new без обёртки — почти всегда лучше std::make_unique или std::make_shared. RAII работает не только для памяти: файлы, мьютексы, сокеты — всё. Достаточно обернуть ресурс в класс с деструктором. Профит: меньше багов, меньше утечек, чище код. ➡️ @cpp_geek

Move-семантика: где можно ловко сэкономить Многие знают про std::move, но не всегда используют его там, где это реально ускоряет код. Простой пример — возврат локального объекта из функции:

#include <string>

std::string make_string() {
    std::string s = "Hello";
    return s; // RVO или move
}
С C++17 тут почти всегда RVO (Return Value Optimization). Но если RVO невозможен (например, возвращаем тернарный оператор), компилятор применит move:

std::string make_string(bool flag) {
    std::string a = "foo", b = "bar";
    return flag ? a : b; // тут будет move
}
А вот так можно подсказать компилятору явно:

return std::move(flag ? a : b);
Но осторожно: не делайте std::move для локальной переменной в простом return — это может сломать RVO и привести к лишнему перемещению. Ещё полезно помнить: move не всегда бесплатный. Например, для std::vector он копирует указатель и размер, но не элементы. Для std::string — зависит от Small String Optimization: короткие строки перемещаются как копия. Вывод: используйте std::move там, где явно хотите отдать объект, а не копировать. Но не злоупотребляйте им: компилятор с C++17 сам неплохо справляется. ➡️ @cpp_geek

Зачем std::move от std::string в emplace_back? Кажется, emplace_back(std::move(str)) — избыточно: ведь emplace_back сам конструирует объект на месте. Но с std::string и другими перемещаемыми типами всё не так просто. Пример:

std::vector<std::string> vec;
std::string str = "hello";

vec.emplace_back(str);             // копия
vec.emplace_back(std::move(str));  // перемещение
Почему? Потому что: > emplace_back вызывает конструктор std::string(const std::string&), если аргумент — const std::string&, > и std::string(std::string&&), если аргумент — std::string&&. А str — это lvalue. Даже внутри emplace_back. Без std::move, ты просишь вектор скопировать строку. С std::move — переместить. Это относится ко всем emplace_*, когда ты передаёшь существующий объект. emplace_back("hello") — другое дело: тут создаётся временный const char*, и он уже используется для конструктора string. 👉 Правило: если аргумент — перемещаемый объект (например, std::string, std::vector), и ты передаёшь его в emplace_*, не забывай std::move. Иначе будет лишняя копия. ➡️ @cpp_geek

Что такое ADL и как она может вас подставить Argument-Dependent Lookup (ADL) — это фича, которая может как упростить код, так и привести к сюрпризам. Представьте:

namespace math {
    struct Vec {};

    void print(const Vec&) {
        std::cout << "Vec\n";
    }
}

void foo() {
    math::Vec v;
    print(v); // компилятор найдёт math::print через ADL!
}
Хотя print не в текущем скоупе, компилятор ищет её в namespace аргумента v, т.е. math. Это и есть ADL. Звучит удобно. Но... ⚠️ Опасность:

namespace lib {
    struct Widget {};
    void process(const Widget&) {
        std::cout << "lib::process\n";
    }
}

void process(int) {
    std::cout << "global::process\n";
}

void bar() {
    lib::Widget w;
    process(w); // вызовет lib::process, а не global::process!
}
Такой вызов может внезапно потянуть неожиданные функции, особенно в generic-коде (например, в шаблонах), где ADL может найти «лишнее». ✅ Best practices: * Не полагайся на ADL без крайней нужды. * Используй ::qualified::names для ясности. * В шаблонах — лучше явно передавать функции (через policy, traits, или параметры шаблона). ➡️ @cpp_geek

Почему std::move может не сработать Многие думают, что std::move всегда приводит к перемещению. Но это не так. std::move не двигает — он просто превращает объект в rvalue-ссылку. Всё остальное — за перегрузками. Вот ловушка:

void take(std::string s) {
    // s — копия, даже если передавали std::move
}

std::string str = "hello";
take(std::move(str)); // ❌ копирование, не перемещение
Почему? Потому что take принимает по значению, а значит, всегда делает копию. Даже если ты передал std::move(str), она скопируется в s. Чтобы работало перемещение, нужна rvalue-ссылка:

void take(std::string&& s) {
    // теперь s — это rvalue-ссылка
}
Но даже тут можно облажаться:

void take(std::string&& s) {
    process(s); // ❌ снова копия!
}

void process(std::string); // по значению
Нужно снова вызвать std::move, если хочешь передать дальше как rvalue:

void take(std::string&& s) {
    process(std::move(s)); // ✅ перемещение
}
Правило: std::move — это обещание, что объект можно "украсть". Но воровать будет только та функция, которая перегружена для rvalue. ➡️ @cpp_geek

💰Олимпиада по программированию с призовым фондом 50 000 рублей! 🏃💨Для школьников от 10 до 16 лет, задачи можно решать на я
💰Олимпиада по программированию с призовым фондом 50 000 рублей! 🏃💨Для школьников от 10 до 16 лет, задачи можно решать на языках GO, Python, JavaScript, C++ 🏆Решить олимпиаду можно 23 июля (среда) с 11:00 до 19:00 🗣Регистрация закроется 20 июля в 23:55 1️⃣ место - 25 000 рублей 2️⃣ место - 15 000 рублей 3️⃣ место - 10 000 рублей 😎Регистрируйся по ссылке, участие бесплатное Олимпиада ZamaCode 📲А также подписывайся на наш телеграм-канал, чтобы не пропустить новые олимпиады и розыгрыши!

Узнаем длину аудио файла В этом примере мы открываем файл с помощью std::ifstream, находим его размер, а затем вычисляем длит
Узнаем длину аудио файла В этом примере мы открываем файл с помощью std::ifstream, находим его размер, а затем вычисляем длительность аудио файла, зная частоту дискретизации (в данном случае 44100 Гц). Выводим длительность в минутах и секундах. Убедитесь, что заменили "your_audio_file.wav" на путь к вашему аудио файлу, а также, если частота дискретизации вашего аудио файла отличается от 44100 Гц, замените это значение на соответствующее значение вашего аудио файла. ➡️ @cpp_geek

std::thread std::thread является частью стандартной библиотеки C++ и предоставляет возможность создания и управления потоками
std::thread std::thread является частью стандартной библиотеки C++ и предоставляет возможность создания и управления потоками выполнения. Он позволяет запускать функции в отдельных потоках, обеспечивая параллельное выполнение кода. Обратите внимание, что после создания потока std::thread, вы должны вызвать join() или detach() для корректной обработки завершения потока. В приведенном примере мы использовали join(), чтобы основной поток дождался завершения потока t1. Если вы вызываете join() после завершения потока, программа может выдать исключение или вызвать неопределенное поведение. ➡️ @cpp_geek

Полезные функции, которые будут полезны продвинутым C++ разработчикам. 1. void assert_or_throw(bool cond, const std::string& msg) Универсальная замена assert в runtime-среде:

void assert_or_throw(bool cond, const std::string& msg) {
    if (!cond) throw std::runtime_error(msg);
}
2. template<typename T> std::string to_string_precise(const T& val, int precision = 6) Преобразование чисел с точностью:

template<typename T>
std::string to_string_precise(const T& val, int precision = 6) {
    std::ostringstream out;
    out << std::fixed << std::setprecision(precision) << val;
    return out.str();
}
3. template<typename F> auto scope_exit(F&& f) RAII-функция для отложенного вызова:

template<typename F>
class ScopeExit {
    F func;
    bool active = true;
public:
    ScopeExit(F&& f) : func(std::forward<F>(f)) {}
    ~ScopeExit() { if (active) func(); }
    void dismiss() { active = false; }
};

template<typename F>
ScopeExit<F> scope_exit(F&& f) {
    return ScopeExit<F>(std::forward<F>(f));
}
4. template<typename T> constexpr bool is_power_of_two(T x) Компилируемая проверка степени двойки:

template<typename T>
constexpr bool is_power_of_two(T x) {
    return x > 0 && (x & (x - 1)) == 0;
}
5. template<typename T> void hash_combine(std::size_t& seed, const T& val) Для реализации собственного std::hash:

template<typename T>
void hash_combine(std::size_t& seed, const T& val) {
    seed ^= std::hash<T>()(val) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}
6. std::vector<std::string> split(const std::string& str, char delimiter) Полезна для парсинга CSV, логов и т.д.:

std::vector<std::string> split(const std::string& str, char delimiter) {
    std::vector<std::string> out;
    std::istringstream ss(str);
    std::string token;
    while (std::getline(ss, token, delimiter)) {
        out.push_back(token);
    }
    return out;
}
7. template<typename T> T clamp(T val, T min_val, T max_val) Ручной аналог std::clamp (если нужна совместимость со старым C++):

template<typename T>
T clamp(T val, T min_val, T max_val) {
    return std::max(min_val, std::min(val, max_val));
}
8. template<typename Container, typename Predicate> bool any_of(const Container& c, Predicate pred) Упрощённая обёртка над std::any_of:

template<typename Container, typename Predicate>
bool any_of(const Container& c, Predicate pred) {
    return std::any_of(c.begin(), c.end(), pred);
}
9. template<typename... Args> std::string format(const std::string& fmt, Args&&... args) Интерфейс к std::format (C++20+):

template<typename... Args>
std::string format(const std::string& fmt, Args&&... args) {
    return std::vformat(fmt, std::make_format_args(args...));
}
10. template<typename T> std::string type_name() Получение имени типа на этапе компиляции:

template<typename T>
std::string type_name() {
#ifdef __clang__
    std::string name = __PRETTY_FUNCTION__;
    return name.substr(31, name.length() - 32);
#elif defined(__GNUC__)
    std::string name = __PRETTY_FUNCTION__;
    return name.substr(49, name.length() - 50);
#elif defined(_MSC_VER)
    std::string name = __FUNCSIG__;
    return name.substr(38, name.length() - 45);
#else
    return "unknown";
#endif
}
➡️ @cpp_geek

🧵 Сегодня покажу вам простой способ логгировать вызовы функций в C++ — пригодится для отладки и анализа кода. Часто бывает нужно понять, какие функции вызываются, в каком порядке и с какими параметрами. Вручную вставлять std::cout — неудобно. Вместо этого используем RAII-макрос с выводом в консоль:

#include <iostream>
#include <string>

struct FunctionLogger {
    std::string func_name;
    FunctionLogger(const std::string& name) : func_name(name) {
        std::cout << ">> Entering: " << func_name << '\n';
    }
    ~FunctionLogger() {
        std::cout << "<< Exiting: " << func_name << '\n';
    }
};

#define LOG_FUNCTION() FunctionLogger logger(__FUNCTION__)
Теперь в любой функции достаточно просто написать LOG_FUNCTION();, и вы получите автоматический лог при входе и выходе:

void do_work() {
    LOG_FUNCTION();
    // Работаем...
}
Это особенно удобно в больших проектах, когда нужно быстро локализовать ошибку или понять структуру вызовов. Можно доработать: лог в файл, потокобезопасность, включение по флагу компиляции и т.д. ➡️ @cpp_geek

Невидимый UB: возвращаем ссылку на локальную переменную Одна из самых коварных ошибок в C++ — возврат ссылки на переменную, срок жизни которой закончился. Казалось бы, всё компилируется, запускается... и даже иногда "работает". А под капотом — undefined behavior. Пример:

const std::string& getName() {
    std::string name = "John";
    return name;  // 💥 Возвращаем ссылку на локальный объект
}
Функция возвращает ссылку на name, но как только getName() завершится, name уничтожается. Ссылка указывает в никуда. Использование этой ссылки:

std::cout << getName() << "\n";  // UB: может напечатать мусор, может упасть
👀 Хитрость: такая ошибка часто прячется внутри более сложных функций, и ловится не сразу. Особенно в шаблонном коде или при рефакторинге. 🔒 Как безопасно? * Возвращайте по значению, если объект небольшой или RVO (return value optimization) работает:

std::string getName() {
    std::string name = "John";
    return name;  // ок, RVO устранит копирование
}
* Или передавайте результат через параметр:

void getName(std::string& out) {
    out = "John";
}
💡 Профит: избежите UB, багов-призраков и бессонных ночей. ➡️ @cpp_geek

🚀 Станьте C++ разработчиком и откройте для себя новые возможности в IT. Актуальное обучение от OTUS — это ваш старт в масшта
🚀 Станьте C++ разработчиком и откройте для себя новые возможности в IT. Актуальное обучение от OTUS — это ваш старт в масштабную разработку на современном подмножестве C++! 👨‍💻 На курсе вы освоите все ключевые аспекты разработки на C++ — от основ синтаксиса до идиом и паттернов языка, продвинутой многопоточности и работы с базами данных Мы подготовим вас для работы с высоконагруженными приложениями, IoT-устройствами и сложными проектами. ⚡️ Изучите C++ с нуля и пройдите два этапа обучения: от Junior до Middle Developer. Реальные кейсы, лучшие практики и советы экспертов помогут вам освоить язык и уверенно претендовать на востребованные позиции. ❗️ Запись на курс закрывается! Оставьте заявку и получите скидку на обучение по промокоду CPPspec_6: https://vk.cc/cN4KR8 Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576, www.otus.ru

Тема: Почему std::vector<bool> - не совсем std::vector На первый взгляд std::vector<bool> — обычный вектор, только из булевых значений. Но это особенный шаблон. Вместо хранения bool как полноценного байта, он упаковывает их в биты. Экономит память? Да. Но есть нюансы. Пример:

std::vector<bool> flags = {true, false, true};
auto x = flags[0];        // Не bool, а прокси-объект!
bool y = flags[0];        // OK — копия значения
bool& z = flags[0];       // Ошибка компиляции
flags[0] возвращает proxy-объект, а не bool&, потому что нельзя вернуть ссылку на бит. Из-за этого: * Нельзя взять адрес элемента * Нельзя использовать std::vector<bool> с API, ожидающим bool* или bool& * Некоторые шаблоны не работают (особенно в generic-коде) Хочешь экономии — будь готов к сюрпризам. Хочешь предсказуемости — используй std::deque<bool> или std::vector<char>. ⚠️ Кстати, std::vector<bool> — единственная специализация STL-контейнера в стандартной библиотеке. ➡️ @cpp_geek

Ключевые библиотеки Boost, которые полезно знать каждому C++ разработчику Вот топ-5 библиотек Boost: 1️⃣ Boost.Asio Асинхронный ввод-вывод и сетевое программирование. Незаменим для серверных приложений. 2️⃣ Boost.Beast HTTP и WebSocket клиенты/серверы. Построен на Asio. 3️⃣ Boost.Serialization Сериализация сложных структур данных в потоки байтов и обратно. 4️⃣ Boost.Graph Алгоритмы на графах: поиск путей, обходы, топологическая сортировка и др. 5️⃣ Boost.Spirit Создание парсеров прямо в коде C++ без внешних генераторов. ➡️ @cpp_geek

Зачем [[nodiscard]] нужен не только для возврата значения Если ты думаешь, что [[nodiscard]] — это просто защита от игнора Result, то вот фокус: его можно вешать и на классы, и на функции, и даже на enum — и это реально помогает избежать багов. Пример:

struct [[nodiscard]] Error {
    std::string message;
};

Error do_something() {
    return {"Что-то пошло не так"};
}

void foo() {
    do_something(); // warning: ignoring return value of nodiscard type
}
А теперь магия с методами:

struct Connection {
    [[nodiscard]] bool is_valid() const {
        return valid_;
    }

private:
    bool valid_ = false;
};

void check_connection(const Connection& conn) {
    conn.is_valid(); // warning: result of 'is_valid' is unused
}
📌 Даже если функция возвращает bool — компилятор предупредит, если ты его проигнорируешь. Это круто, когда метод что-то проверяет, ищет или сигналит об ошибке — и ты точно не хочешь забыть проверить результат. ⚠️ Но аккуратно: [[nodiscard]] не бросает исключения и не делает функцию безопасной. Это подсказка компилятору и твой напарник по коду. Если хочешь писать более надёжный и самодокументируемый код — юзай [[nodiscard]] не только по дефолту, а осознанно. ➡️ @cpp_geek

Сейчас покажу вам простой, но очень полезный приём, как аккуратно и безопасно управлять ресурсами в C++ с помощью RAII (Resource Acquisition Is Initialization). Когда вы работаете с ресурсами (файлы, сокеты, мьютексы и т.д.), важно не забывать освобождать их. Особенно если программа может завершиться по исключению. И вот тут RAII — наш лучший друг. Рассмотрим пример:

#include <fstream>
#include <string>

void writeToFile(const std::string& filename, const std::string& data) {
    std::ofstream file(filename);
    if (!file) {
        throw std::runtime_error("Unable to open file");
    }
    file << data;
} // файл автоматически закрывается здесь
Мы открыли файл — и не закрыли его вручную! Почему? Потому что std::ofstream сам закроет его в своём деструкторе. Это и есть RAII в действии. И теперь представьте: вы можете создавать свои классы с таким же поведением! Например, класс-обёртку над pthread_mutex_t или системным дескриптором. RAII — это стиль. И это стиль надёжного кода. ➡️ @cpp_geek

🚀 Подборка Telegram каналов для программистов Системное администрирование, DevOps 📌 https://t.me/bash_srv Bash Советы https://t.me/win_sysadmin Системный Администратор Windows https://t.me/sysadmin_girl Девочка Сисадмин https://t.me/srv_admin_linux Админские угодья https://t.me/linux_srv Типичный Сисадмин https://t.me/devopslib Библиотека девопса | DevOps, SRE, Sysadmin https://t.me/linux_odmin Linux: Системный администратор https://t.me/devops_star DevOps Star (Звезда Девопса) https://t.me/i_linux Системный администратор https://t.me/linuxchmod Linux https://t.me/sys_adminos Системный Администратор https://t.me/tipsysdmin Типичный Сисадмин (фото железа, было/стало) https://t.me/sysadminof Книги для админов, полезные материалы https://t.me/i_odmin Все для системного администратора https://t.me/i_odmin_book Библиотека Системного Администратора https://t.me/i_odmin_chat Чат системных администраторов https://t.me/i_DevOps DevOps: Пишем о Docker, Kubernetes и др. https://t.me/sysadminoff Новости Линукс Linux 1C разработка 📌 https://t.me/odin1C_rus Cтатьи, курсы, советы, шаблоны кода 1С https://t.me/DevLab1C 1С:Предприятие 8 https://t.me/razrab_1C 1C Разработчик https://t.me/buh1C_prog 1C Программист | Бухгалтерия и Учёт https://t.me/rabota1C_rus Вакансии для программистов 1С Программирование C++📌 https://t.me/cpp_lib Библиотека C/C++ разработчика https://t.me/cpp_knigi Книги для программистов C/C++ https://t.me/cpp_geek Учим C/C++ на примерах Программирование Python 📌 https://t.me/pythonofff Python академия. https://t.me/BookPython Библиотека Python разработчика https://t.me/python_real Python подборки на русском и английском https://t.me/python_360 Книги по Python Java разработка 📌 https://t.me/BookJava Библиотека Java разработчика https://t.me/java_360 Книги по Java Rus https://t.me/java_geek Учим Java на примерах GitHub Сообщество 📌 https://t.me/Githublib Интересное из GitHub Базы данных (Data Base) 📌 https://t.me/database_info Все про базы данных Мобильная разработка: iOS, Android 📌 https://t.me/developer_mobila Мобильная разработка https://t.me/kotlin_lib Подборки полезного материала по Kotlin Фронтенд разработка 📌 https://t.me/frontend_1 Подборки для frontend разработчиков https://t.me/frontend_sovet Frontend советы, примеры и практика! https://t.me/React_lib Подборки по React js и все что с ним связано Разработка игр 📌 https://t.me/game_devv Все о разработке игр Библиотеки 📌 https://t.me/book_for_dev Книги для программистов Rus https://t.me/programmist_of Книги по программированию https://t.me/proglb Библиотека программиста https://t.me/bfbook Книги для программистов БигДата, машинное обучение 📌 https://t.me/bigdata_1 Big Data, Machine Learning Программирование 📌 https://t.me/bookflow Лекции, видеоуроки, доклады с IT конференций https://t.me/rust_lib Полезный контент по программированию на Rust https://t.me/golang_lib Библиотека Go (Golang) разработчика https://t.me/itmozg Программисты, дизайнеры, новости из мира IT https://t.me/php_lib Библиотека PHP программиста 👨🏼‍💻👩‍💻 https://t.me/nodejs_lib Подборки по Node js и все что с ним связано https://t.me/ruby_lib Библиотека Ruby программиста https://t.me/lifeproger Жизнь программиста. Авторский канал. QA, тестирование 📌 https://t.me/testlab_qa Библиотека тестировщика Шутки программистов 📌 https://t.me/itumor Шутки программистов Защита, взлом, безопасность 📌 https://t.me/thehaking Канал о кибербезопасности https://t.me/xakep_2 Хакер Free Книги, статьи для дизайнеров 📌 https://t.me/ux_web Статьи, книги для дизайнеров Математика 📌 https://t.me/Pomatematike Канал по математике https://t.me/phis_mat Обучающие видео, книги по Физике и Математике https://t.me/matgeoru Математика | Геометрия | Логика Excel лайфхак📌 https://t.me/Excel_lifehack https://t.me/mir_teh Мир технологий (Technology World) Вакансии 📌 https://t.me/sysadmin_rabota Системный Администратор https://t.me/progjob Вакансии в IT