Сортировка
std::sort - самый нужный и наиболее часто используемый алгоритм из библиотеки STL. Не зря мир серьезных алгоритмов маленькие хакеры начинают познавать с сортировки. Конечно после изучения всех базовых концепций языка и программных сущностей. Это реально нужная на практике вещь.
Давайте на пальцах.
Есть у вас вектор для каких-то объектов. Вы заполняете этот вектор объектами просто с помощью push_back. Здесь есть 2 варианта.
Первый вариант - порядок элементов неважен, работаем со всеми элементами по очереди или только с крайними(ну например очередь задач или список слушателей евентов класса). Тут все просто, испотльзуй методы контейнера и живи счастливо.
Второй вариант - вы хотите переупорядочить элементы в контейнере или сделать вставку/удаление/поиск/изменение элемента из середины контейнера. Тут все сложнее. Для начала надо подумать, потому что вы скорее всего выбрали неправильный контейнер. Алгоритмы find, remove и тому подобное - от Иблиса. Может показаться, что линейная сложность не проблема, не так уж это и много. Но нет. Вряд ли вам нужно один раз найти какой-то элемент к контейнере и скорее всего там будет квадратичная сложность. Про удаление я вообще молчу, внутреннее устройство вектора даст вашему первомансу отдохнуть.
Присмотритесь к ассоциативным контейнерам, когда вам нужны такие операции. И если нужно переупорядочивать элементы, то тоже проверьте себя еще раз.
Проверили? Идем дальше. У нас остается вектор и вы хотите его переупорядочить. Переупорядочить - назначить отношение порядка. Значит можно сказать, какой элемент идет за каким. Значит можно сказать, какой элемент "меньше" другого. Так и приходим к сортировке.
По факту, сортировка - единственный алгоритм, который реально можно применить для последовательных контейнеров. Ну ладно не единственный. Но это уже тема для другого поста.
Stay cool.
#STL #algorithms
std::sort - самый нужный и наиболее часто используемый алгоритм из библиотеки STL. Не зря мир серьезных алгоритмов маленькие хакеры начинают познавать с сортировки. Конечно после изучения всех базовых концепций языка и программных сущностей. Это реально нужная на практике вещь.
Давайте на пальцах.
Есть у вас вектор для каких-то объектов. Вы заполняете этот вектор объектами просто с помощью push_back. Здесь есть 2 варианта.
Первый вариант - порядок элементов неважен, работаем со всеми элементами по очереди или только с крайними(ну например очередь задач или список слушателей евентов класса). Тут все просто, испотльзуй методы контейнера и живи счастливо.
Второй вариант - вы хотите переупорядочить элементы в контейнере или сделать вставку/удаление/поиск/изменение элемента из середины контейнера. Тут все сложнее. Для начала надо подумать, потому что вы скорее всего выбрали неправильный контейнер. Алгоритмы find, remove и тому подобное - от Иблиса. Может показаться, что линейная сложность не проблема, не так уж это и много. Но нет. Вряд ли вам нужно один раз найти какой-то элемент к контейнере и скорее всего там будет квадратичная сложность. Про удаление я вообще молчу, внутреннее устройство вектора даст вашему первомансу отдохнуть.
Присмотритесь к ассоциативным контейнерам, когда вам нужны такие операции. И если нужно переупорядочивать элементы, то тоже проверьте себя еще раз.
Проверили? Идем дальше. У нас остается вектор и вы хотите его переупорядочить. Переупорядочить - назначить отношение порядка. Значит можно сказать, какой элемент идет за каким. Значит можно сказать, какой элемент "меньше" другого. Так и приходим к сортировке.
По факту, сортировка - единственный алгоритм, который реально можно применить для последовательных контейнеров. Ну ладно не единственный. Но это уже тема для другого поста.
Stay cool.
#STL #algorithms
👍28❤7🔥2
Small string optimization
Люди, которые разрабатывают компилляторы - гении. Никто не знает тех маленьких индусов, которые рвут свои маленькие попки, чтобы из твоего зачастую дурнопахнущего кода на С++ сделать конфетку. Эта фича не является геймченджером или сильным бустром перфоманса. Это просто крутая оптимизация, которая исходит из понимания работы с памятью на низком уровне и юз-кейсов класса строки.
Дело в том, что в основе плюсовой строки лежит обычный сишный массив. Точнее не обычный, а выделенный в куче. Так вот, когда вы пишите std::string var = "Like this post", по идее на куче должен выделяться блок памяти, равный размеру этой строки. Но. Зачастую пользователь класса std::string хочет присвоить переменной какую-нибудь короткую строку, которую он менять не собирается. Зачем тогда тратить драгоценные клоки проца и делать дорогостоящий вызов, когда можно поместить эту короткую строку в маленький внутренний буффер самого объекта строки и жить счастливо? Не дергать аллокатор, не фрагментировать память. Хорошо звучит? Вот и индусы так же подумали и запилили small string optimization.
Замечу, что этой фичи нет в стандарте. Это просто хорошая практика реализации стандартной библиотеки. Длина буферизируемой строки может отличатся от одной реализации к другой, а также может варьирываться с помощью опций компиллятора. Для гцц по дефолту она равна 15 байт.
Давайте все быть такими же умными, как индусы.
Stay cool.
#optimization #datastructures
Люди, которые разрабатывают компилляторы - гении. Никто не знает тех маленьких индусов, которые рвут свои маленькие попки, чтобы из твоего зачастую дурнопахнущего кода на С++ сделать конфетку. Эта фича не является геймченджером или сильным бустром перфоманса. Это просто крутая оптимизация, которая исходит из понимания работы с памятью на низком уровне и юз-кейсов класса строки.
Дело в том, что в основе плюсовой строки лежит обычный сишный массив. Точнее не обычный, а выделенный в куче. Так вот, когда вы пишите std::string var = "Like this post", по идее на куче должен выделяться блок памяти, равный размеру этой строки. Но. Зачастую пользователь класса std::string хочет присвоить переменной какую-нибудь короткую строку, которую он менять не собирается. Зачем тогда тратить драгоценные клоки проца и делать дорогостоящий вызов, когда можно поместить эту короткую строку в маленький внутренний буффер самого объекта строки и жить счастливо? Не дергать аллокатор, не фрагментировать память. Хорошо звучит? Вот и индусы так же подумали и запилили small string optimization.
Замечу, что этой фичи нет в стандарте. Это просто хорошая практика реализации стандартной библиотеки. Длина буферизируемой строки может отличатся от одной реализации к другой, а также может варьирываться с помощью опций компиллятора. Для гцц по дефолту она равна 15 байт.
Давайте все быть такими же умными, как индусы.
Stay cool.
#optimization #datastructures
👍31🔥9❤2👎1
struct vs class
Если ты перед каждым объявлением класса думаешь над тем, какое ключевое слово использовать - ты по адресу.
Я лично всегда триггерился, когда полноценный класс объявляют структурой. Да где это виданно?!? Ты объявляешь класс, в языке есть очень похожее ключевое слово, которое говорит, что началось объявление класса. И ты, как полностью психически здоровый человек, пишешь struct. Круто че. Мои полномочия тут все.
И вот ты хочешь сделать "по правильному" и объявить класс классом. А потом понимаешь, что половина проекта использует struct, да еще в коде, для которого твой класс предназначен, тоже используются структуры. И закрадываются сомнения. Может это не мир неправильный, а я?
Но потом пришло принятие и понимание. Структуры проще писать. Может я один такой, конечно, невнимательный. Но насколько часто вы встречаете ошибку с использованием приватного метода вне класса? У меня это ошибка постоянно всплывает, когда я использую class. Меня это ужасно раздражало, поэтому сдался и начался просто всегда писать struct.
Все потому, что struct обладает двумя преумуществами в реальной разработке по сравнению с class. Все поля публичные и наследование тоже публичное.
Вспомните, сколько раз вам было нужно приватное наследование? Если хоть раз и оправданно, то вы бриллиант. А челяди эта фича не нужна. То есть уже на одно слово меньше нужно писать.
А исходя из концепции ООП, нам обычно нужно скрыть данные от чужих глаз. Я не думаю над тем, как открыть мой публичный интерфейс. Я думаю над тем, что мне нужно скрыть. Поэтому очень органично после публичных методов я пишу private: и дальше пишу всю запрещенку. Да и принято так, что публичный интерфейс описывается всегда первым. Поэтому я могу не писать лишний раз public:.
Экономится целых 1 слово на класс и еще одно на наследуемый класс. Это ли не причина перейти на struct?)
Stay cool.
#cppcore
Если ты перед каждым объявлением класса думаешь над тем, какое ключевое слово использовать - ты по адресу.
Я лично всегда триггерился, когда полноценный класс объявляют структурой. Да где это виданно?!? Ты объявляешь класс, в языке есть очень похожее ключевое слово, которое говорит, что началось объявление класса. И ты, как полностью психически здоровый человек, пишешь struct. Круто че. Мои полномочия тут все.
И вот ты хочешь сделать "по правильному" и объявить класс классом. А потом понимаешь, что половина проекта использует struct, да еще в коде, для которого твой класс предназначен, тоже используются структуры. И закрадываются сомнения. Может это не мир неправильный, а я?
Но потом пришло принятие и понимание. Структуры проще писать. Может я один такой, конечно, невнимательный. Но насколько часто вы встречаете ошибку с использованием приватного метода вне класса? У меня это ошибка постоянно всплывает, когда я использую class. Меня это ужасно раздражало, поэтому сдался и начался просто всегда писать struct.
Все потому, что struct обладает двумя преумуществами в реальной разработке по сравнению с class. Все поля публичные и наследование тоже публичное.
Вспомните, сколько раз вам было нужно приватное наследование? Если хоть раз и оправданно, то вы бриллиант. А челяди эта фича не нужна. То есть уже на одно слово меньше нужно писать.
А исходя из концепции ООП, нам обычно нужно скрыть данные от чужих глаз. Я не думаю над тем, как открыть мой публичный интерфейс. Я думаю над тем, что мне нужно скрыть. Поэтому очень органично после публичных методов я пишу private: и дальше пишу всю запрещенку. Да и принято так, что публичный интерфейс описывается всегда первым. Поэтому я могу не писать лишний раз public:.
Экономится целых 1 слово на класс и еще одно на наследуемый класс. Это ли не причина перейти на struct?)
Stay cool.
#cppcore
🤷♀23😁15👎9❤5👍3🔥3
CTAD - Class Template Argument Deduction
Одним из приятных нововведений C++ 17 стандарта стало автоматическое выведение типа классов при объявлении шаблонных объектов, функций и т.д.
Этот механизм позволяет вам явно не указывать тип аргументов шаблонных классов там, где они могут быть выведены компилятором. Пример:
Давайте посмотрим на живой пример. Обратите внимание, что в строке параметров компилятора указан 17 стандарт
Ключевым моментом CTAD для компилятора является возможность вывести этот тип. Если при попытке выведения появляется несколько кандидатов, то попытка выведения типа приведет к ошибке. Пример:
В данном случае тип передаваемых значений различен: double и int. Компилятор не будет брать на себя ответственность выбирать какой-то тип самостоятельно. Вдруг это принципиально важно для корректности исполнения ваших программ?
Однако, так же стоит и помнить про неожиданные эффекты CTAD. При инициализации из одного аргумента типа, который является специализацией рассматриваемого шаблона класса, вывод копирования обычно предпочтительнее переноса по умолчанию:
Компилятор дает большее предпочтение конструкторам initializer-list, но при list-initialization остается неизменным:
Подробнее: cppreference
#cpp17
Одним из приятных нововведений C++ 17 стандарта стало автоматическое выведение типа классов при объявлении шаблонных объектов, функций и т.д.
Этот механизм позволяет вам явно не указывать тип аргументов шаблонных классов там, где они могут быть выведены компилятором. Пример:
// vector declaration before C++17
std::vector<double> v1 = {1.0, 2.0, 3.0};
// CTAD vector declaration since C++17
std::vector v2 = {1.0, 2.0, 3.0};
Давайте посмотрим на живой пример. Обратите внимание, что в строке параметров компилятора указан 17 стандарт
-std=c++17. Если его попробовать переключить на более ранний стандарт -std=c++14, то возможность компиляции будет утрачена.Ключевым моментом CTAD для компилятора является возможность вывести этот тип. Если при попытке выведения появляется несколько кандидатов, то попытка выведения типа приведет к ошибке. Пример:
// Error: no viable constructor or deduction guide for deduction of template arguments of 'vector'
std::vector data = {1.0, 2.0, 3, 4};
В данном случае тип передаваемых значений различен: double и int. Компилятор не будет брать на себя ответственность выбирать какой-то тип самостоятельно. Вдруг это принципиально важно для корректности исполнения ваших программ?
Однако, так же стоит и помнить про неожиданные эффекты CTAD. При инициализации из одного аргумента типа, который является специализацией рассматриваемого шаблона класса, вывод копирования обычно предпочтительнее переноса по умолчанию:
// The type is std::vector<int>
std::vector v1{1, 2};
// The type is std::vector<int>
std::vector v2{v1};
// The type is std::vector<std::vector<int>>
std::vector v3{v1, v2};
Компилятор дает большее предпочтение конструкторам initializer-list, но при list-initialization остается неизменным:
// The type is std::vector<int>
std::vector v1{1, 2};
// The type is std::vector<int>
std::vector v2(v1.begin(), v1.end());
// The type is std::vector<std::vector<int>::iterator>
std::vector v3{v1.begin(), v1.end()};
Подробнее: cppreference
#cpp17
👍15❤4🔥2
# Комментарии в коде
Комментарии выполняют очень важную функцию - говорят программисту то, что не может сказать код. А если код чего-то недоговаривает программисту - значит он не очень хороший. В большинстве случаев. Либо в нем используются 200 IQ концепции, которые программист не может вычленить из кода без помощи. Но это действительно редкости.
Если вы перед функцией пишите, что она делает - это плохая функция. Она либо плохо названа, либо делает несколько вещей за раз, либо занимает больше 100 строк кода и без бутылки тут не разберешься.
Давайте длинные и информативные имена переменным и методам. Это настолько приятно читать и даже жить как-то проще становится.
Разбивайте код на элементарные части. Чтобы каждый вызов метода был информативным и точно отражал свою функцию. И чем больше элементарных структурных элементов в коде (элементарных не значит минимального размера), тем лучше и более читаемым он становится.
Пишите комменты только для действительно нетривиальных вещей. Когда у потенциального читателя может возникнуть вопрос: "а как получается, что эта последовательность действий приводит к этому результату?" - вот то место, куда нужно вставить коммент.
Stay cool.
#goodpractice
Комментарии выполняют очень важную функцию - говорят программисту то, что не может сказать код. А если код чего-то недоговаривает программисту - значит он не очень хороший. В большинстве случаев. Либо в нем используются 200 IQ концепции, которые программист не может вычленить из кода без помощи. Но это действительно редкости.
Если вы перед функцией пишите, что она делает - это плохая функция. Она либо плохо названа, либо делает несколько вещей за раз, либо занимает больше 100 строк кода и без бутылки тут не разберешься.
Давайте длинные и информативные имена переменным и методам. Это настолько приятно читать и даже жить как-то проще становится.
Разбивайте код на элементарные части. Чтобы каждый вызов метода был информативным и точно отражал свою функцию. И чем больше элементарных структурных элементов в коде (элементарных не значит минимального размера), тем лучше и более читаемым он становится.
Пишите комменты только для действительно нетривиальных вещей. Когда у потенциального читателя может возникнуть вопрос: "а как получается, что эта последовательность действий приводит к этому результату?" - вот то место, куда нужно вставить коммент.
Stay cool.
#goodpractice
👍23👎4❤🔥2❤2🔥1🌚1
Циклы
Было время, работал я датасайентистом. Написал я там на питоне один раз функцию, которая в цикле перебирает весь датасет и что-то делает с каждой строчкой. Тогда мне мой ментор сказал: "Циклы в чистом питоне, как мои нейроны, после разговора с феминиской - очень медленные. Используй pandas.apply". Дело в том, что питон - сам по себе очень медленный язык, а библиотеки под него, зачатую написаны на cython или вообще на плюсах. Поэтому они быстрые.
К чему это я. Плюсы хоть и очень быстрый язык, но наши косявые сардельки могут сильно заставить его пыхтеть. И когда мы пишем руками цикл, в котором делаем всякие непотребства, наша задача подумать: а нет ли стандартного алгоритма, который делает тоже самое? Или обобщенного алгоритма с кастомным функтором, который может подойти в этой ситации?
Потому что все, что есть в стандратной библиотеке - максимально оптимизированно. Многие будут спорить и писать в проекте свои алгоритмы и контейнеры, которые выжимают последние капли оптимизации из кода, но я немного про другие случаи. В большинстве случаев в бэкэнд разработке вам не нужно все самописное, здесь важно время выката бизнес фичи. А STL - готовый и хорошо оптимизированный инструмент, который позволит использовать функционал без затрат на кодирование и тестирование и ловлю багов.
Не придумывайте велосипеды. Оставьте это импортозаметителям.
Stay cool.
#goodpractice
Было время, работал я датасайентистом. Написал я там на питоне один раз функцию, которая в цикле перебирает весь датасет и что-то делает с каждой строчкой. Тогда мне мой ментор сказал: "Циклы в чистом питоне, как мои нейроны, после разговора с феминиской - очень медленные. Используй pandas.apply". Дело в том, что питон - сам по себе очень медленный язык, а библиотеки под него, зачатую написаны на cython или вообще на плюсах. Поэтому они быстрые.
К чему это я. Плюсы хоть и очень быстрый язык, но наши косявые сардельки могут сильно заставить его пыхтеть. И когда мы пишем руками цикл, в котором делаем всякие непотребства, наша задача подумать: а нет ли стандартного алгоритма, который делает тоже самое? Или обобщенного алгоритма с кастомным функтором, который может подойти в этой ситации?
Потому что все, что есть в стандратной библиотеке - максимально оптимизированно. Многие будут спорить и писать в проекте свои алгоритмы и контейнеры, которые выжимают последние капли оптимизации из кода, но я немного про другие случаи. В большинстве случаев в бэкэнд разработке вам не нужно все самописное, здесь важно время выката бизнес фичи. А STL - готовый и хорошо оптимизированный инструмент, который позволит использовать функционал без затрат на кодирование и тестирование и ловлю багов.
Не придумывайте велосипеды. Оставьте это импортозаметителям.
Stay cool.
#goodpractice
❤23👍8👏3🔥1
Инициализация глобальных объектов
Избегайте сложной инициализации глобальных переменных. То, как компиллятор построит порядок инициализации переменных в разных единицах трансляции - одному ему и известно. И то не всегда. Поэтому не надейтесь, что ваша логика и порядок работы будут соблюдаться.
Ладно, если в разных cpp файлах у вас весь глобальный или статический код иниациализируется независимо друг от друга. Куда ни шло. Но если объекты создаются с зависимостями в другой единице трансляции - ждите беды.
// file1.c
extern const X x;
const Y y = f(x); // read x; write y
// file2.c
extern const Y y;
const X x = g(y); // read y; write x
Смотрим на код. Поскольку x и y находятся в разных единицах трансляции, порядок вызовов f() и g() не определен; В любом из случаев здесь будет доступ к неинициализированной константе. Это показывает, что проблема порядка инициализации глобальных объектов не ограничивается глобальными переменными. Таким образом и статические объекты могут зафакапиться.
А если код конкурентный... Даже продолжать не буду.
Старайтесь по максимуму избегать глобальных объектов. Это типа антипаттерн и может привести к разным проблемам.
И старайтесь по максимуму использовать constexpr. Это упрощает инициализацию кода. А вы будете крутыми типами, которые используют фишки modern c++.
Stay cool.
#cppcore
Избегайте сложной инициализации глобальных переменных. То, как компиллятор построит порядок инициализации переменных в разных единицах трансляции - одному ему и известно. И то не всегда. Поэтому не надейтесь, что ваша логика и порядок работы будут соблюдаться.
Ладно, если в разных cpp файлах у вас весь глобальный или статический код иниациализируется независимо друг от друга. Куда ни шло. Но если объекты создаются с зависимостями в другой единице трансляции - ждите беды.
// file1.c
extern const X x;
const Y y = f(x); // read x; write y
// file2.c
extern const Y y;
const X x = g(y); // read y; write x
Смотрим на код. Поскольку x и y находятся в разных единицах трансляции, порядок вызовов f() и g() не определен; В любом из случаев здесь будет доступ к неинициализированной константе. Это показывает, что проблема порядка инициализации глобальных объектов не ограничивается глобальными переменными. Таким образом и статические объекты могут зафакапиться.
А если код конкурентный... Даже продолжать не буду.
Старайтесь по максимуму избегать глобальных объектов. Это типа антипаттерн и может привести к разным проблемам.
И старайтесь по максимуму использовать constexpr. Это упрощает инициализацию кода. А вы будете крутыми типами, которые используют фишки modern c++.
Stay cool.
#cppcore
🔥20👍7❤3
Магические числа
А что мы, программисты, знаем о магии? Правильно, магия - это зло, с которым нужно бороться и искоренять!
Сегодня речь пойдет о магических константах и переменных. Так называют разные числовые значения, смысл которых очень трудно понять. Типовые ситуации:
1. Готовый результат каких-то промежуточных операций
2. Прямая подстановка значений в вычисления
Главная проблема всего магического и загадочного - это отсутствие понимания, как и что работает у тех, кто должен внести изменения. Зачастую эта проблема причиняет большие страдания вовсе не автору кода, а его наследникам. Так получается, потому что автор на момент написания программы обладает большим количеством знаний. Он же это пишет, черт возьми, для него это вовсе не магия. Но у других людей, которые видят готовое решение впервые, этих знаний нет. Им приходится их приобрести путем прочтения этого кода. Хорошо, когда есть у кого можно спросить, а если нет? А автор кода готов рассказывать об алгоритме своего решения каждый раз, когда кто-то другой редактирует это место?
Качество преподнесенной информации напрямую влияет на процессы разработки и правильность принятых решений другими людьми. Неизвестность сильно тормозит процесс разработки, т.к. приходится тратить дополнительное время на понимание и отладку. Так же это потенциально увеличивает вероятность допущения новых ошибок, ведь никто не застрахован от ложного понимания логики работы чужого кода.
Я вам посоветую:
1. Проявляйте эмпатию и поберегите своих коллег.
Это универсальный совет! Поставьте себя на место своих коллег и попробуйте представить, будет ли им понятно, что написано в коде при первом прочтении?
Если вы учтёте все нюансы, напишите комментарии, то ваш код полюбят и будут беречь долгие годы.
2. Раскрывайте промежуточные вычисления.
Часть вычислений компилятор сможет оптимизировать при компиляции. Другую часть можно выполнить достаточно быстро во время выполнения программы. Подробнее эта тема будет раскрыта в следующих постах.
3. Используйте осмысленные имена для переменных.
Пусть имена ваших переменных будут прочтены, как слова в предложении. Пусть они добавляют дополнительный смысл к вашим константам.
4. Избегайте прямую подстановку значений.
Выносите значения из вычислений в константы или переменные, покажите какой смысл вносится в алгоритм этими значениями.
#goodpractice
А что мы, программисты, знаем о магии? Правильно, магия - это зло, с которым нужно бороться и искоренять!
Сегодня речь пойдет о магических константах и переменных. Так называют разные числовые значения, смысл которых очень трудно понять. Типовые ситуации:
1. Готовый результат каких-то промежуточных операций
2. Прямая подстановка значений в вычисления
Главная проблема всего магического и загадочного - это отсутствие понимания, как и что работает у тех, кто должен внести изменения. Зачастую эта проблема причиняет большие страдания вовсе не автору кода, а его наследникам. Так получается, потому что автор на момент написания программы обладает большим количеством знаний. Он же это пишет, черт возьми, для него это вовсе не магия. Но у других людей, которые видят готовое решение впервые, этих знаний нет. Им приходится их приобрести путем прочтения этого кода. Хорошо, когда есть у кого можно спросить, а если нет? А автор кода готов рассказывать об алгоритме своего решения каждый раз, когда кто-то другой редактирует это место?
Качество преподнесенной информации напрямую влияет на процессы разработки и правильность принятых решений другими людьми. Неизвестность сильно тормозит процесс разработки, т.к. приходится тратить дополнительное время на понимание и отладку. Так же это потенциально увеличивает вероятность допущения новых ошибок, ведь никто не застрахован от ложного понимания логики работы чужого кода.
Я вам посоветую:
1. Проявляйте эмпатию и поберегите своих коллег.
Это универсальный совет! Поставьте себя на место своих коллег и попробуйте представить, будет ли им понятно, что написано в коде при первом прочтении?
Если вы учтёте все нюансы, напишите комментарии, то ваш код полюбят и будут беречь долгие годы.
2. Раскрывайте промежуточные вычисления.
Часть вычислений компилятор сможет оптимизировать при компиляции. Другую часть можно выполнить достаточно быстро во время выполнения программы. Подробнее эта тема будет раскрыта в следующих постах.
3. Используйте осмысленные имена для переменных.
Пусть имена ваших переменных будут прочтены, как слова в предложении. Пусть они добавляют дополнительный смысл к вашим константам.
4. Избегайте прямую подстановку значений.
Выносите значения из вычислений в константы или переменные, покажите какой смысл вносится в алгоритм этими значениями.
#goodpractice
👍16❤3🔥1
Почему РКН не сможет полностью заблокировать VPN
Новость, которая всех нас на долю секунды заставила почувствовать капельку волнительного пота, стекающую по спине прямо в копилочку. Не знаю, как вы, но меня жизнь еще не допинала до аренды своего сервера для vpn. Поэтому вынужден иногда пользоваться всевозможными приложениями для своего смартфона. И в последние полгода-год ими реально стало сложнее пользоваться: они перестают работать и приходится скачивать новые. И тенденция идет по нисходящей. Но такл и все плохо и мы будем под железным занавесом?
За свою профессиональную карьеру плюсовика мне довелось плотно поработать с DPI. Работа как раз заключалась в анализе трафика сервиса, нахождении паттернов в полезной нагрузке пакетов или в метаданных и детектировании сервиса по этим паттернам. Это позволяло с высокой точностью определить протокол, сервис и даже пользовательскую активность в сервисе. То есть тема реально рабочая.
Однако есть и проблемы. Шаблоны-то не динамические. Если ты думаешь, что один раз нашел паттерн и он всегда будет работать - подумай еще раз. Ни что не вечно, а мир Айти - тем более. Проприетарные протоколы общения сервисов меняются в худшем случае с каждым обновлением аппки. Поэтому приходилось периодически перепроверять эти шаблоны и вносить изменения в систему, чтобы она снова работала с нужной точностью. Теперь представьте обилие ВПН протоколов и приложений, которые их используют. Теперь представьте, какое количество человекочасов нужно, чтобы проанализировать трафик, исправить паттерны в КОДЕ и провалидировать это дело. А теперь представьте количество программистов в компаниях, предоставляющих DPI решения (ведь РКН сам ничего не разрабатывает). И наконец, представьте обилие бюрократии, коммуникаций и времени, которое необходимо для того, чтобы запрос РКН на обновление сервиса выполнился.
Будет такая же история, как с телеграмм. Да, будет подвисать. Да, будут перебои. Да, жизнь станет потяжелее (скорее подороже). Да, возможно большая часть обывателей лишиться привелегии сидеть в инстаграмм.
Но даже опыт Китая показывает нам, что лазейки есть. О них расскажу как-нибудь позже.
Оставайся на связи. Stay cool.
#net #howitworks
Новость, которая всех нас на долю секунды заставила почувствовать капельку волнительного пота, стекающую по спине прямо в копилочку. Не знаю, как вы, но меня жизнь еще не допинала до аренды своего сервера для vpn. Поэтому вынужден иногда пользоваться всевозможными приложениями для своего смартфона. И в последние полгода-год ими реально стало сложнее пользоваться: они перестают работать и приходится скачивать новые. И тенденция идет по нисходящей. Но такл и все плохо и мы будем под железным занавесом?
За свою профессиональную карьеру плюсовика мне довелось плотно поработать с DPI. Работа как раз заключалась в анализе трафика сервиса, нахождении паттернов в полезной нагрузке пакетов или в метаданных и детектировании сервиса по этим паттернам. Это позволяло с высокой точностью определить протокол, сервис и даже пользовательскую активность в сервисе. То есть тема реально рабочая.
Однако есть и проблемы. Шаблоны-то не динамические. Если ты думаешь, что один раз нашел паттерн и он всегда будет работать - подумай еще раз. Ни что не вечно, а мир Айти - тем более. Проприетарные протоколы общения сервисов меняются в худшем случае с каждым обновлением аппки. Поэтому приходилось периодически перепроверять эти шаблоны и вносить изменения в систему, чтобы она снова работала с нужной точностью. Теперь представьте обилие ВПН протоколов и приложений, которые их используют. Теперь представьте, какое количество человекочасов нужно, чтобы проанализировать трафик, исправить паттерны в КОДЕ и провалидировать это дело. А теперь представьте количество программистов в компаниях, предоставляющих DPI решения (ведь РКН сам ничего не разрабатывает). И наконец, представьте обилие бюрократии, коммуникаций и времени, которое необходимо для того, чтобы запрос РКН на обновление сервиса выполнился.
Будет такая же история, как с телеграмм. Да, будет подвисать. Да, будут перебои. Да, жизнь станет потяжелее (скорее подороже). Да, возможно большая часть обывателей лишиться привелегии сидеть в инстаграмм.
Но даже опыт Китая показывает нам, что лазейки есть. О них расскажу как-нибудь позже.
Оставайся на связи. Stay cool.
#net #howitworks
👍15❤4❤🔥1
Указатель на void
Тот случай, когда тема сишная, но знать это надо и это довольно часто встречается в плюсовых проектах.
Естественно, это все про сишные интерфейсы и C-style функции. Мы все с этим сталкивается, ничего не поделать.
Нетипизированный указатель - очень крутая штука в контексте Си. Он предоставляет возможность до определенной степени использовать обобщенное программирование. Самый очевидный пример - malloc. Он выделяет память заданного размера и выдает указатель на начало этого куска. Функция не знает, как будет использоваться память и под какие структуры. Поэтому перекладывает на программиста ответственность за то, как дальше память будет использована. А программист должен скастовать void* к типизированному указателю, чтобы получить доступ к структуре. Но зачем?
Дело в том, что любой указатель - просто 8-мибайтное число, указывающее на какую-то точку в памяти. Но объекты и структуры - это не точки. Это отрезки. От и до. Поэтому компиллятору надо знать размер области памяти, чтобы достать оттуда информацию. Отсюда и правило, что разыменовывать void* нельзя. Отсюда и второе правило, что адресная арифметика неприменима к void*. Поэтому нужны типизированные указатели. Любой определенный тип имеет вполне конкретный размер, известный компилятору. И когда мы храним указатель на этот тип, мы говорим условному gcc "тут лежит объект и, чтобы откопать его, копай на 4 байта вправо от указателя".
В этом и прикол указателя на воид - если у функции есть такой параметр, мы можем передать туда все что угодно. Отсюда мы имеем 2 основных сценария использования таких указателей.
Первый - дженерик функции, работающие с любым типом. Главное знать размер этого типа. Типичные примеры: стандартный memcpy или какой-нибудь кастомный bytesToHexString
Второй - передача параметров callback функциям, когда типы параметров неизвестны вызывающему коду. Для примера, посмотрите функцию qsort. Она принимает компаратор с двумя параметрами - void*. Это необходимо, так как сама qsort не знает, с какими данными она работает и передает их в callback как есть, в виде void*.
Stay cool.
#cppcore #goodoldc #memory
Тот случай, когда тема сишная, но знать это надо и это довольно часто встречается в плюсовых проектах.
Естественно, это все про сишные интерфейсы и C-style функции. Мы все с этим сталкивается, ничего не поделать.
Нетипизированный указатель - очень крутая штука в контексте Си. Он предоставляет возможность до определенной степени использовать обобщенное программирование. Самый очевидный пример - malloc. Он выделяет память заданного размера и выдает указатель на начало этого куска. Функция не знает, как будет использоваться память и под какие структуры. Поэтому перекладывает на программиста ответственность за то, как дальше память будет использована. А программист должен скастовать void* к типизированному указателю, чтобы получить доступ к структуре. Но зачем?
Дело в том, что любой указатель - просто 8-мибайтное число, указывающее на какую-то точку в памяти. Но объекты и структуры - это не точки. Это отрезки. От и до. Поэтому компиллятору надо знать размер области памяти, чтобы достать оттуда информацию. Отсюда и правило, что разыменовывать void* нельзя. Отсюда и второе правило, что адресная арифметика неприменима к void*. Поэтому нужны типизированные указатели. Любой определенный тип имеет вполне конкретный размер, известный компилятору. И когда мы храним указатель на этот тип, мы говорим условному gcc "тут лежит объект и, чтобы откопать его, копай на 4 байта вправо от указателя".
В этом и прикол указателя на воид - если у функции есть такой параметр, мы можем передать туда все что угодно. Отсюда мы имеем 2 основных сценария использования таких указателей.
Первый - дженерик функции, работающие с любым типом. Главное знать размер этого типа. Типичные примеры: стандартный memcpy или какой-нибудь кастомный bytesToHexString
Второй - передача параметров callback функциям, когда типы параметров неизвестны вызывающему коду. Для примера, посмотрите функцию qsort. Она принимает компаратор с двумя параметрами - void*. Это необходимо, так как сама qsort не знает, с какими данными она работает и передает их в callback как есть, в виде void*.
Stay cool.
#cppcore #goodoldc #memory
❤18👍6🔥5
Что будет, если malloc'нуть 100 Гигабайт?
У одного моего друга недавно на ревью спросили этот вопрос. Вопрос интересный, и если не знать нюансов, то результат можно только предполагать. Друг накидал вариантов, но ревьюер ответа тоже не знал, поэтому это была задачка на подумать и порассуждать. А я решил не думать, а проверить. Так и родился этот пост.
У меня получилось 2 варианта развития событий: попробовать это провернуть на тачке с привычными характеристиками и на мощном сервере, где RAM много больше 100 Гб. Так уж вышло, что я имею доступ и к одному и к другому. Поэтому раскрою оба варианта.
Чтобы вдруг не заруинить сервер и свою тачку, я попробовал запустить код в онлайн компилляторе wandbox.
В мэйне было 2 строчки:
void *p = malloc(100000000000L); // да да 100 миллиардов байт это не 100 гб, знаю знаю... Jesus Christ...
printf("got %pn", p);
Запустил я это. И даже получил результат. Ничего не упало. Вывод: got (nil). То есть ОС просто отказала маллоку в выдаче такого большого объема памяти. И маллок вернул нулевой указатель. Это кстати одна из причин, почему всегда стоит проверять указатель, возвращенный маллоком, на равенство нулю. Иначе получите segfault.
Далее попробовал на своей локальной машинке. Результат тот же. Никто не хочет мне давать память😭. Но хотя бы комп не сломал.
Ну и наконец, на серваке получил валидный адрес: got 0x7f1e4f2e1010.
Прикольный эксперимент. У меня появились еще пара идей, связанных с выделением большого объема памяти. Буду раскрывать их в будущих постах.
Оставайся на связи. Stay cool.
#fun
У одного моего друга недавно на ревью спросили этот вопрос. Вопрос интересный, и если не знать нюансов, то результат можно только предполагать. Друг накидал вариантов, но ревьюер ответа тоже не знал, поэтому это была задачка на подумать и порассуждать. А я решил не думать, а проверить. Так и родился этот пост.
У меня получилось 2 варианта развития событий: попробовать это провернуть на тачке с привычными характеристиками и на мощном сервере, где RAM много больше 100 Гб. Так уж вышло, что я имею доступ и к одному и к другому. Поэтому раскрою оба варианта.
Чтобы вдруг не заруинить сервер и свою тачку, я попробовал запустить код в онлайн компилляторе wandbox.
В мэйне было 2 строчки:
void *p = malloc(100000000000L); // да да 100 миллиардов байт это не 100 гб, знаю знаю... Jesus Christ...
printf("got %pn", p);
Запустил я это. И даже получил результат. Ничего не упало. Вывод: got (nil). То есть ОС просто отказала маллоку в выдаче такого большого объема памяти. И маллок вернул нулевой указатель. Это кстати одна из причин, почему всегда стоит проверять указатель, возвращенный маллоком, на равенство нулю. Иначе получите segfault.
Далее попробовал на своей локальной машинке. Результат тот же. Никто не хочет мне давать память😭. Но хотя бы комп не сломал.
Ну и наконец, на серваке получил валидный адрес: got 0x7f1e4f2e1010.
Прикольный эксперимент. У меня появились еще пара идей, связанных с выделением большого объема памяти. Буду раскрывать их в будущих постах.
Оставайся на связи. Stay cool.
#fun
😁17🔥6👍4🦄2❤1
std::lower_bound
Продолжение басни с алгоритмами в STL. Предыдущие части можете найти тут жмак и жмак
Сегодня речь пойдет про бинарный поиск. Точнее немного более прикладную его вариацию - std::lower_bound.
На самом деле не удивительно, почему в списке 3-х самых используемых алгоритмов есть этот, учитывая, что на первом месте стояла сортировка. Если что-то и искать, то искать надо в упорядоченных структурах данных. Ведь преимущества очевидны: О(n) против O(logn) (можно и за О(1), но это другая уже другая история). Поэтому следует избегать использования линейного поиска (типа std::find), а использовать подходящий контейнер и эффективный алгоритм поиска над ним.
Функция выполняет поиск в отсортированном диапазоне и возвращает итератор на первый элемент, не меньший, чем заданное значение. За счет того, что поиск происходит в упорядоченном контейнере, алгоритмическая сложность такой операции - О(logn). То есть это очень эффективный способ найти нужный элемент в контейнере. В основе алгоритма лежит бинарный поиск, где на каждой итерации цикла диапазон поиска уменьшается вдвое.
Алгоритм может использоваться, как для поиска конкретного элемента, так и для поиска диапазона значений самостоятельно (открытый диапазон) или в сочетании с upper_bound (закрытый диапазон).
Немного более вариабельным lower_bound становится в применении к деревьям. Тогда возвращаемый итератор можно использовать для эффективной вставки ноды в дерево (например методом map::emplace_hint).
И не зря довольно часто на собесах встречаются задачи на бинарный поиск. Потому что это реально маст-хэв. Поэтому его в принципе полезно знать и уметь запрогать руками.
Stay cool.
#STL #algorithms #datastructures
Продолжение басни с алгоритмами в STL. Предыдущие части можете найти тут жмак и жмак
Сегодня речь пойдет про бинарный поиск. Точнее немного более прикладную его вариацию - std::lower_bound.
На самом деле не удивительно, почему в списке 3-х самых используемых алгоритмов есть этот, учитывая, что на первом месте стояла сортировка. Если что-то и искать, то искать надо в упорядоченных структурах данных. Ведь преимущества очевидны: О(n) против O(logn) (можно и за О(1), но это другая уже другая история). Поэтому следует избегать использования линейного поиска (типа std::find), а использовать подходящий контейнер и эффективный алгоритм поиска над ним.
Функция выполняет поиск в отсортированном диапазоне и возвращает итератор на первый элемент, не меньший, чем заданное значение. За счет того, что поиск происходит в упорядоченном контейнере, алгоритмическая сложность такой операции - О(logn). То есть это очень эффективный способ найти нужный элемент в контейнере. В основе алгоритма лежит бинарный поиск, где на каждой итерации цикла диапазон поиска уменьшается вдвое.
Алгоритм может использоваться, как для поиска конкретного элемента, так и для поиска диапазона значений самостоятельно (открытый диапазон) или в сочетании с upper_bound (закрытый диапазон).
Немного более вариабельным lower_bound становится в применении к деревьям. Тогда возвращаемый итератор можно использовать для эффективной вставки ноды в дерево (например методом map::emplace_hint).
И не зря довольно часто на собесах встречаются задачи на бинарный поиск. Потому что это реально маст-хэв. Поэтому его в принципе полезно знать и уметь запрогать руками.
Stay cool.
#STL #algorithms #datastructures
👍15🔥4❤2
Как найти минимум в несортированном массиве за константу
Ответ на первый взгляд простой и понятной - хрена с два у тебя такое прокатит. Но что, если вам говорят на собесе : "Вот есть класс стека с методами push_back и pop_back и есть метод min. Нужно, чтобы метод min работал за константное время". Как ни отникивайся, придется решать. Чем мы с вами и займемся. А перед этим делом сами подумайте: как бы вы это сделали? Бывалым ценителям алгоритмических задачек этот вопрос даже каплю пота не вызовет, а вот средних работяг может заставить попотеть.
Первое, что надо понимать - если у вас просто есть готовый массив и ничего больше, то вы не найдете минимум за константное время. Нужно что-то думать. Второе, что надо понимать - очень часто можно довольно просто понизить сложность операции за счет применения правильного хранилища. Например, если превратить массив в неупорядоченное мультимножество, то сложность поиска понижается с О(n) до О(~1). И с этой мысли нужно начинать, когда от вас требуют понизить сложность. Задумайтесь. Не зря нам дали удобную обертку для массива. Мы можем контролировать, как именно будут добавляться и удаляться данные из него, а также можем хранить в этом классе какие-то дополнительные структуры.
В данном случае, решением будет добавить в класс поле current_min_stack - стек, в который будет хранить текущие минимумы для каждого элемента исходного массива. Как только вызывается push_back(value), в current_min_stack добавляется минимум из value и текущей верхушки current_min_stack. На каждый вызов pop_back мы убираем из current_min_stack его последний элемент. А на вызов min мы возвращаем последний элемент из стека текущего минимума.
Не самый эффективный способ, знаем и получше. Вот и напишите в комментах, как улучшить алгоритм.
Stay cool.
#algorithms
Ответ на первый взгляд простой и понятной - хрена с два у тебя такое прокатит. Но что, если вам говорят на собесе : "Вот есть класс стека с методами push_back и pop_back и есть метод min. Нужно, чтобы метод min работал за константное время". Как ни отникивайся, придется решать. Чем мы с вами и займемся. А перед этим делом сами подумайте: как бы вы это сделали? Бывалым ценителям алгоритмических задачек этот вопрос даже каплю пота не вызовет, а вот средних работяг может заставить попотеть.
Первое, что надо понимать - если у вас просто есть готовый массив и ничего больше, то вы не найдете минимум за константное время. Нужно что-то думать. Второе, что надо понимать - очень часто можно довольно просто понизить сложность операции за счет применения правильного хранилища. Например, если превратить массив в неупорядоченное мультимножество, то сложность поиска понижается с О(n) до О(~1). И с этой мысли нужно начинать, когда от вас требуют понизить сложность. Задумайтесь. Не зря нам дали удобную обертку для массива. Мы можем контролировать, как именно будут добавляться и удаляться данные из него, а также можем хранить в этом классе какие-то дополнительные структуры.
В данном случае, решением будет добавить в класс поле current_min_stack - стек, в который будет хранить текущие минимумы для каждого элемента исходного массива. Как только вызывается push_back(value), в current_min_stack добавляется минимум из value и текущей верхушки current_min_stack. На каждый вызов pop_back мы убираем из current_min_stack его последний элемент. А на вызов min мы возвращаем последний элемент из стека текущего минимума.
Не самый эффективный способ, знаем и получше. Вот и напишите в комментах, как улучшить алгоритм.
Stay cool.
#algorithms
👍24🔥6❤4👎2🤪1
Идиома RAII
Наши подписчики присылают нам новые идеи и даже конспекты лекций! Поблагодарим Сергея Нефедова за это 🙂
В сообществе C++ разработчиков достаточно часто можно услышать об идиоме RAII (resource acquisition is initialization). Про нее уже было написано много и не раз, но и я попробую рассказать про нее по-своему.
Начну издалека, но так, чтобы вам было понятнее. Каждая вычислительная система обладает ресурсами - объектами вычислительной системы, которые ограничены в количестве и времени использования. Такой объект необходимо вернуть этой системе. Примером таких ресурсов могут стать оперативная память, доступ к файлу, сетевое соединение.
Каждый объект данного типа зачастую обладает обобщенным жизненным циклом:
1. Захват ресурса
Пример: выделение памяти, получение доступа, установка соединения.
2. Владение ресурсом
Пример: чтение/запись в память/файл, передача данных по соединению
3. Возврат ресурса
Пример: освобождение памяти, закрытие файла / соединения
Суть идиомы RAII заключается в том, чтобы данный жизненный цикл всегда был исполнен, т.е. ресурс всегда будет возвращен системе, когда он программе больше не нужен. Идиома предлагает вам создавать сущности в ваших программах таким образом, чтобы избежать утечки этих ограниченных ресурсов. Это может быть неочевидно в рамках учебных программ. Но в больших коммерческих проектах, которые могут эксплуатироваться годами без перерыва, отсутствие менеджмента ресурсов может привести к исчерпанию этих ресурсов и потере целевой функциональности. Срок годности вашего продукта будет ограничен временем исполнения или запасом этих ресурсов. Это уже не IT технологии...
Как же этого достичь? Обратите внимание на классы в C++, ведь они тоже соответствуют этому жизненному циклу. Давайте проведем аналогии:
1. Конструктор класса — захват ресурса
Начало жизни объекта: можно выделить память, открыть файл, установить соединение.
2. Ваши методы класса — владение ресурсом
Множество операций над ресурсом. Например, запись в память, файл или передача данных по соединению. Так же тут могут быть определены операции передачи владения ресурсом другому объекту. Данная тема будет подробнее раскрыта в следующих постах про move семантику.
3. Деструктор класса — возврат ресурса
Завершение жизни объекта: возвращаем системе захваченные ресурсы - освобождаем память, закрываем файлы и соединение.
Классы, созданные с подачи идиомы RAII, берут на себя ответственность менеджмента ресурсов и предоставления механизмов взаимодействия с ними.
Примечательно, что возврат ресурсов будет происходить во время уничтожения данного объекта - автоматически, при выходе из области видимости объекта этого класса. Это является очень удобным механизмом контроля за ресурсами для разработчика.
Примерами таких классов из стандартной библиотеки C++ являются std::string, а так же умные указатели:
1) std::unique_ptr<typename> - удаляет выделенный на куче объект типа typename при выходе из области видимости.
2) std::shared_ptr<typename> - удаляет выделенный на куче объект типа typename при выходе из области видимости всех копий данного умного указателя.
Если вам интересно узнать мнение авторов по конкретной теме, поделиться своими знаниями или задать интересный вопрос - пишите в комментах. Попробуем разобраться вместе.
#cppcore #goodpractice
Наши подписчики присылают нам новые идеи и даже конспекты лекций! Поблагодарим Сергея Нефедова за это 🙂
В сообществе C++ разработчиков достаточно часто можно услышать об идиоме RAII (resource acquisition is initialization). Про нее уже было написано много и не раз, но и я попробую рассказать про нее по-своему.
Начну издалека, но так, чтобы вам было понятнее. Каждая вычислительная система обладает ресурсами - объектами вычислительной системы, которые ограничены в количестве и времени использования. Такой объект необходимо вернуть этой системе. Примером таких ресурсов могут стать оперативная память, доступ к файлу, сетевое соединение.
Каждый объект данного типа зачастую обладает обобщенным жизненным циклом:
1. Захват ресурса
Пример: выделение памяти, получение доступа, установка соединения.
2. Владение ресурсом
Пример: чтение/запись в память/файл, передача данных по соединению
3. Возврат ресурса
Пример: освобождение памяти, закрытие файла / соединения
Суть идиомы RAII заключается в том, чтобы данный жизненный цикл всегда был исполнен, т.е. ресурс всегда будет возвращен системе, когда он программе больше не нужен. Идиома предлагает вам создавать сущности в ваших программах таким образом, чтобы избежать утечки этих ограниченных ресурсов. Это может быть неочевидно в рамках учебных программ. Но в больших коммерческих проектах, которые могут эксплуатироваться годами без перерыва, отсутствие менеджмента ресурсов может привести к исчерпанию этих ресурсов и потере целевой функциональности. Срок годности вашего продукта будет ограничен временем исполнения или запасом этих ресурсов. Это уже не IT технологии...
Как же этого достичь? Обратите внимание на классы в C++, ведь они тоже соответствуют этому жизненному циклу. Давайте проведем аналогии:
1. Конструктор класса — захват ресурса
Начало жизни объекта: можно выделить память, открыть файл, установить соединение.
2. Ваши методы класса — владение ресурсом
Множество операций над ресурсом. Например, запись в память, файл или передача данных по соединению. Так же тут могут быть определены операции передачи владения ресурсом другому объекту. Данная тема будет подробнее раскрыта в следующих постах про move семантику.
3. Деструктор класса — возврат ресурса
Завершение жизни объекта: возвращаем системе захваченные ресурсы - освобождаем память, закрываем файлы и соединение.
Классы, созданные с подачи идиомы RAII, берут на себя ответственность менеджмента ресурсов и предоставления механизмов взаимодействия с ними.
Примечательно, что возврат ресурсов будет происходить во время уничтожения данного объекта - автоматически, при выходе из области видимости объекта этого класса. Это является очень удобным механизмом контроля за ресурсами для разработчика.
Примерами таких классов из стандартной библиотеки C++ являются std::string, а так же умные указатели:
1) std::unique_ptr<typename> - удаляет выделенный на куче объект типа typename при выходе из области видимости.
2) std::shared_ptr<typename> - удаляет выделенный на куче объект типа typename при выходе из области видимости всех копий данного умного указателя.
Если вам интересно узнать мнение авторов по конкретной теме, поделиться своими знаниями или задать интересный вопрос - пишите в комментах. Попробуем разобраться вместе.
#cppcore #goodpractice
👍17❤10🔥3
Почему РКН не сможет полностью заблокировать VPN в России Ч2
Первая часть тут. Причин на самом деле немного больше, чем я описывал ранее, и они гораздо более абстрактные.
Начнём, как всегда, с базы, а точнее с экономики. Большинство адекватных IT-компаний в России использую впн для доступа к корпоративным ресурсам (надо ещё учитывать, что сейчас даже самая облезлая собака считает себя ИТ-компанией). С учётом увеличивающегося хакерского давления на отрасль, использование корпоративного VPN - критическая необходимость для сохранения конфиденциальности информации и защиты сети от несанкционированного доступа. Также специалистам нужны многие сервисы, к которым в России доступ запрещён: линкедин для эйчаров, quora для прогеров и прочих работяг, перечислять можно много. Исходя из всего этого, чисто экономической точки зрения, представьте потери отрасли от блокировки впн трафика. Это десятки и сотни миллиардов рублей. И непозволительная роскошь для страны, которая сильно нуждается в отечественных ИТ-продуктах.
Помимо этого есть и главный идейный фактор. Люди на атаку любой сложности рано или поздно изобретают защиту для этой атаки. Естественная гонка вооружений. На любой интернетный запрет от нашего правительства найдётся лазейка. VPN был такой лазейкой в 18 году, когда РКН блокировал телеграмм. Дальше на помощь придут так называемые технологии обфускации трафика.
Цель обфускации - маскировка VPN трафика, делая его похожим на обычный трафик, чтобы обойти DPI. Например, данные можно скрыть под видом изображений или звуковых файлов. Обфусцирующие протоколы, типа Shadowsocks, прекрасно проявляют себя в Китае, как средство обхода их великого файервола.
Так что заявления РКН нужны просто, чтобы пыль в глаза бросить. Пока есть хоть один провод, соединяющий РФ с остальным интернетом, наши кулибины найдут способы для связи с внешним миром.
Stay calm. Stay cool.
#net #howitworks #fun
Первая часть тут. Причин на самом деле немного больше, чем я описывал ранее, и они гораздо более абстрактные.
Начнём, как всегда, с базы, а точнее с экономики. Большинство адекватных IT-компаний в России использую впн для доступа к корпоративным ресурсам (надо ещё учитывать, что сейчас даже самая облезлая собака считает себя ИТ-компанией). С учётом увеличивающегося хакерского давления на отрасль, использование корпоративного VPN - критическая необходимость для сохранения конфиденциальности информации и защиты сети от несанкционированного доступа. Также специалистам нужны многие сервисы, к которым в России доступ запрещён: линкедин для эйчаров, quora для прогеров и прочих работяг, перечислять можно много. Исходя из всего этого, чисто экономической точки зрения, представьте потери отрасли от блокировки впн трафика. Это десятки и сотни миллиардов рублей. И непозволительная роскошь для страны, которая сильно нуждается в отечественных ИТ-продуктах.
Помимо этого есть и главный идейный фактор. Люди на атаку любой сложности рано или поздно изобретают защиту для этой атаки. Естественная гонка вооружений. На любой интернетный запрет от нашего правительства найдётся лазейка. VPN был такой лазейкой в 18 году, когда РКН блокировал телеграмм. Дальше на помощь придут так называемые технологии обфускации трафика.
Цель обфускации - маскировка VPN трафика, делая его похожим на обычный трафик, чтобы обойти DPI. Например, данные можно скрыть под видом изображений или звуковых файлов. Обфусцирующие протоколы, типа Shadowsocks, прекрасно проявляют себя в Китае, как средство обхода их великого файервола.
Так что заявления РКН нужны просто, чтобы пыль в глаза бросить. Пока есть хоть один провод, соединяющий РФ с остальным интернетом, наши кулибины найдут способы для связи с внешним миром.
Stay calm. Stay cool.
#net #howitworks #fun
👍15❤4🔥1
Предупреждения компилятора Ч1
Наверно многие попадали в такую ситуацию, когда ваша программа работала корректно, но компилятор заваливал вас предупреждениями о тех или иных потенциальных проблемах. Нередко в приоритет ставится в первую очередь возможность компиляции и запуска, а уже потом исправление предупреждений. Это зачастую приводит к привычке игнорировать их совсем...
Но давайте подумаем, зачем же они вообще выводятся? В конце концов, кто-то посчитал важным добавить в компиляторе эти сообщения. Неужели это сделано, чтобы подпортить вам и без того сложную жизнь C++ разработчика?
Конечно, нет. Скажу больше, предупреждения должны вам помогать. Тревожным сигналом является то, если они вам мешают! Забегая вперед, скажу сразу, что в достаточно крупных компаниях возникновение предупреждений считается недопустимым. Сборка проектов устроена так, что возникновение любого предупреждения приводит к ошибке компиляции. Всё строго.
Предупреждения указывают на потенциально опасное место, где может возникнуть ошибка. Возможно, что она возникает только в определенных сценариях, с определенным набором данных. Но можете ли вы гарантировать, что такие сценарии никогда не произойдут? А если можете, то зачем вам засорять вывод лишними предупреждениями?
Некоторые ошибки допускаются случайно, по невнимательности, даже несмотря на высокие компетенции разработчика. Если предупреждений будет много, как быстро он заметит свою ошибку? Отсутствие каких-либо предупреждений по умолчанию позволит однозначно обратить на это внимание, когда оно возникнет.
Исправляйте все предупреждения. Строгость и дисциплинированность будут играть вам на руку на поздних этапах разработки, до которых с другими подходами можно и вовсе не дотянуть.
#compiler #goodpractice
Наверно многие попадали в такую ситуацию, когда ваша программа работала корректно, но компилятор заваливал вас предупреждениями о тех или иных потенциальных проблемах. Нередко в приоритет ставится в первую очередь возможность компиляции и запуска, а уже потом исправление предупреждений. Это зачастую приводит к привычке игнорировать их совсем...
Но давайте подумаем, зачем же они вообще выводятся? В конце концов, кто-то посчитал важным добавить в компиляторе эти сообщения. Неужели это сделано, чтобы подпортить вам и без того сложную жизнь C++ разработчика?
Конечно, нет. Скажу больше, предупреждения должны вам помогать. Тревожным сигналом является то, если они вам мешают! Забегая вперед, скажу сразу, что в достаточно крупных компаниях возникновение предупреждений считается недопустимым. Сборка проектов устроена так, что возникновение любого предупреждения приводит к ошибке компиляции. Всё строго.
Предупреждения указывают на потенциально опасное место, где может возникнуть ошибка. Возможно, что она возникает только в определенных сценариях, с определенным набором данных. Но можете ли вы гарантировать, что такие сценарии никогда не произойдут? А если можете, то зачем вам засорять вывод лишними предупреждениями?
Некоторые ошибки допускаются случайно, по невнимательности, даже несмотря на высокие компетенции разработчика. Если предупреждений будет много, как быстро он заметит свою ошибку? Отсутствие каких-либо предупреждений по умолчанию позволит однозначно обратить на это внимание, когда оно возникнет.
Исправляйте все предупреждения. Строгость и дисциплинированность будут играть вам на руку на поздних этапах разработки, до которых с другими подходами можно и вовсе не дотянуть.
#compiler #goodpractice
❤15👍6❤🔥1
Предупреждения компилятора Ч2
Продолжаем тему предупреждений компилятора.
У каждого предупреждения существует некоторый паттерн, который компилятор пытается распознать и сообщить об этом разработчику.
Активировать данную функциональность можно двумя опциями компилятора:
Они включают в себя очень большое количество проверок. Рассмотрим некоторые типичные примеры:
1. Неиспользуемые переменные - пример
Переменная была объявлена, но не используется в коде. Разработчик забыл её использовать, либо появился так называемый мертвый код.
2. Неинициализированные переменные - пример
Значение переменной читается и используется до того, как ей было присвоено начальное значение. Достаточно серьезная ошибка, классифицируемая как UB - undefined behavior - неопределенное поведение.
3. Недостижимый код - пример
В вашей программе появился участок кода, который никогда не будет выполнен. Это может быть вызвано неправильной логикой или условиями, которые всегда являются ложными или всегда истинными.
4. Неправильное использование типов данных - пример
Преобразовании типов данных, которые могут привести к потере данных или неожиданному поведению программы.
5. Неоднозначные операции - пример
Компилятор может предупредить о неоднозначном использовании операций или перегруженных операторов, когда он не может определить, какой именно вариант операции вы хотите использовать.
6. Потенциальные проблемы безопасности - пример
Компилятор может выдавать предупреждения о возможных проблемах безопасности, таких как использование небезопасных функций или операций, которые могут привести к переполнению буфера.
К сожалению, не все проблемы кода могут быть распознаны компилятором на этапе компиляции, т.е. без ее запуска. Так что развивайте внутри недр своего мозга виртуальный компилятор - это необходимый инструмент каждого разработчика.
#compiler #goodpractice
Продолжаем тему предупреждений компилятора.
У каждого предупреждения существует некоторый паттерн, который компилятор пытается распознать и сообщить об этом разработчику.
Активировать данную функциональность можно двумя опциями компилятора:
g++ source.cpp -Wall -Wextra
Они включают в себя очень большое количество проверок. Рассмотрим некоторые типичные примеры:
1. Неиспользуемые переменные - пример
Переменная была объявлена, но не используется в коде. Разработчик забыл её использовать, либо появился так называемый мертвый код.
2. Неинициализированные переменные - пример
Значение переменной читается и используется до того, как ей было присвоено начальное значение. Достаточно серьезная ошибка, классифицируемая как UB - undefined behavior - неопределенное поведение.
3. Недостижимый код - пример
В вашей программе появился участок кода, который никогда не будет выполнен. Это может быть вызвано неправильной логикой или условиями, которые всегда являются ложными или всегда истинными.
4. Неправильное использование типов данных - пример
Преобразовании типов данных, которые могут привести к потере данных или неожиданному поведению программы.
5. Неоднозначные операции - пример
Компилятор может предупредить о неоднозначном использовании операций или перегруженных операторов, когда он не может определить, какой именно вариант операции вы хотите использовать.
6. Потенциальные проблемы безопасности - пример
Компилятор может выдавать предупреждения о возможных проблемах безопасности, таких как использование небезопасных функций или операций, которые могут привести к переполнению буфера.
К сожалению, не все проблемы кода могут быть распознаны компилятором на этапе компиляции, т.е. без ее запуска. Так что развивайте внутри недр своего мозга виртуальный компилятор - это необходимый инструмент каждого разработчика.
#compiler #goodpractice
❤12👍5❤🔥1🔥1