LINUX.ORG.RU

юнионы в C++

 


2

4

Пишу телегу против плюсов. В связи с этим вопрос - насколько широко в плюсах используются нуль-терминированные строки, юнионы, неумные указатели и всё такое плохое, что делает Си опасным языком.

Даже интересует не столько то, насколько они используются в существующих программах, а есть ли примеры программ, где хорошие средства плюсов сконсолидировались и поставили заслон от опасных конструкций Си, позволив полностью избежать их использования и избавиться от типичных ошибок Си. Можно ли так написать что-то существенно сложное? Сделано ли это в любимых библиотеках (Буст, QT и иже с ними)? Вторая часть вопроса - это неопределённое поведение. В Си его много. Это подаётся как фича, но с точки зрения безопасности это дыра. Меньше ли неопределённого поведения в С++?

Есть две полярные точки зрения на вопрос:

а) С++ перекрывает Си, поэтому там всё сделано по-другому, поэтому безопасность выше б) С++ - наследник Си и в целом наследует его недостатки.

Поскольку я мало пишу на Си и ещё меньше на Си++, у меня нет сложившегося мнения на эту тему. А у ЛОРа наверняка есть мнение, даже несколько.

★★★★★

Последнее исправление: xaizek (всего исправлений: 4)

Ответ на: комментарий от archie

системы сборки типа Cargo.

прочие процедурные симейки

Ты обделался. Любой крейт, хоть как-то взаимодействующий с системой, пишет свой процедурный build.rs, просто он спрятан под капот – в отличие от С++.

Siborgium ★★★★★
()
Ответ на: комментарий от X512

Оно слишком жирное для стандарта. Таблицы Юникода ICU десятки мегабайт занимают.

Стандарт уже должен учитывать Юникод для того чтобы корректно вычислять ширину std::format.

https://github.com/microsoft/STL/blob/main/stl/inc/__msvc_format_ucd_tables.hpp

https://github.com/microsoft/STL/blob/d89a32b2a4ee96de3bc95d30ecd1bec2a34f7122/stl/inc/format#L205-L304

https://github.com/microsoft/STL/blob/d89a32b2a4ee96de3bc95d30ecd1bec2a34f7122/stl/inc/format#L432-L517

И были мысли для этой цели использовать ICU в стандартной библиотеке: https://github.com/microsoft/STL/issues/1945

Так что нет, ICU возможная зависимость для стандарта…

fsb4000 ★★★★★
()
Последнее исправление: fsb4000 (всего исправлений: 1)

Почему ОС на Обероне, а не на C/C++

Язык Си ненадёжен. В нём нет понятия массива. Поэтому сведения о границах между разными данными в памяти легко теряются и возникают ошибки типа переполнения буфера. Явное управление памятью, а значит, возникают ошибки и уязвимости вида «использование после освобождения», в т.ч. повреждение стека. В Си интенсивно используется нестрогая типизация (void *, union), которая тоже приводят к ошибкам (уязвимостям). В своё время язык Си мог быть и прогрессивным, но с тех пор прошло 50 лет и он явно устарел. Лишь только большая инерция держит его «на плаву». Язык С++ более развит, но всё же по большей части совместим с Си, а значит, в какой-то мере наследует недостатки C. При этом, С++ переусложнён - он хорошо подходит, чтобы можно было блеснуть знаниями языка, но в промышленности нужно не это, а надёжный инструмент, позволяющий людям средних способностей достигать надёжного результата. Язык «Активный Оберон» существенно безопаснее Си и при этом существенно проще С++. Для надёжности программы также важна проблема доверенного компилятора. Существующие компиляторы С++ слишком сложны, в то время как компилятор Активного Оберона из ЯОС прост и в принципе позволяет двигаться в направлении создания доверенного компилятора. Кроме того, простота языка позволяет в сжатые сроки создавать специализированные версии языка, и такие версии созданы, например, Active Cells - это разновидность Активного Оберона для программирования FPGA.

fluorite ★★★★★
()
Ответ на: комментарий от archie

мое любимое UB месяца:

https://gcc.godbolt.org/z/rneh3fns5

<source>(5) : error C4716: 'do_nothing': must return a value
<source>(9) : error C4716: 'launch_missiles': must return a value

Не компилируется даже без флагов включения предупреждений и предупреждений как ошибок :)

fsb4000 ★★★★★
()

но с точки зрения безопасности это дыра

С точки зрения безопасности дыра – это отсутствие аудита кода. А неопределённое поведение ведь не потому существует, что авторы не осилили, или было лень. Так надо по вполне определённым причинам.

no-such-file ★★★★★
()
Ответ на: комментарий от alysnix

я чищу все варнинги, кроме - unused тра-та-та. и уровень варнингов - типа по максимуму..взял из текущего вот проекта..

Я рад за тебя. Если бы еще все остальные крестовики так же флаги дотошно прописывали, то цены бы им не было.

Впрочем то, что С++ представляет собой сплошное минное поле и нашпигован UB по самые гланды - это считается фичей, а не багом. Типа сложный мощный небезопасный язык для профессиАналов, бла-бла-бла. Ну ок. Я выше уже писал, что не считаю UB серьезным недостатком, это так и задумано с самого начала, проблемы скорее в тулинге и менеджменте пакетов.

archie
()
Ответ на: комментарий от kvpfs

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

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

archie
()
Ответ на: комментарий от archie

А почему такая ситуация происходит? Типа он не возвращает значения и счетчик инструкций натыкается на следующую функцию?

realbarmaley ★★
()
Ответ на: комментарий от Siborgium

Любой крейт, хоть как-то взаимодействующий с системой, пишет свой процедурный build.rs, просто он спрятан под капот – в отличие от С++.

И это замечательно! Можно писать нормальные скрипты сборки на полноценном языке со строгой статической типизацией и со всякими автодополнениями и рефакторингами в IDE, а не на вырвиглазном нетипизированном уродце CMake.

Я бы например не отказался описывать свойства С++ проекта в манифесте типа project.json, ставить все зависимости одной командой типа cpm install, а в каких-то нестандартных ситуациях дописывать скрипты сборки build.cpp на распоследнем С++20 с рейнджами, оопешечкой и прочими плюшками полноценного языка. А не возиться с нестандартной уродливой процедурщиной CMake, которая нигде больше не понадобится, кроме как для сборки крестопроектов. Вот уж воистину бесполезный скилл и напрасная потеря времени.

archie
()
Ответ на: комментарий от realbarmaley

А почему такая ситуация происходит? Типа он не возвращает значения и счетчик инструкций натыкается на следующую функцию?

Да, нету return какое-то число и выполнение идёт дальше, выполняется следующая функция, потом снова доходит до выполнения main.

Вот можно ассемблер посмотреть:

https://gcc.godbolt.org/z/rG6YWGc8r

fsb4000 ★★★★★
()
Ответ на: комментарий от fsb4000

Не компилируется даже без флагов включения предупреждений и предупреждений как ошибок :)

x64 msvc v19.latest

Пройдите на винфак, гражданин.

archie
()
Ответ на: комментарий от realbarmaley

Я просто всегда с -Wall -Wextra -pedantic -Werror работаю. Там не возвращение это ошибка

Если так делать, то и в С++ будет ошибка.

<source>:5:1: warning: non-void function does not return a value [-Wreturn-type]


<source>:9:1: warning: non-void function does not return a value [-Wreturn-type]

В Visual C++ это не предупреждение, а ошибка, там и без -Werror не скомпилируется.

fsb4000 ★★★★★
()
Ответ на: комментарий от fsb4000

Да, все верно. Если забыть ретурн в не-void функции, то компилятор ничего сам не вставляет и выполнение машинного кода благополучно продолжается за пределами функции do_nothing. А так уж получается, что дальше в памяти находится код launch_missiles и main, в результате чего С++ код начинает в бесконечном цикле пулять ядерные ракеты.

Это вообще прекрасный пример кресто убэшечки, я щитаю. Прямо из палаты мер и весов. Чем-то напоминает проваливание в следующую ветку switch при забытом break. Только для свитча это говноповедение официально стандартизовано, а здесь чистое незамутненное UB, полностью зависящее от того, как компилятор/линкер разложат код в памяти.

archie
()

Пишу телегу против плюсов.

А для кого эта телега пишется и зачем?

У меня де жа вю какое то, буквально вот неделю назад объяснял студенту почему мы будем писать на С++ (хотя студент его не знает, не любит и вообще С++ ужасный ЯП) а не на питоне/С#/…

AntonI ★★★★
()
Ответ на: комментарий от no-such-file

Значит, во многих популярных программах, написанных на Си, аудит кода отсутствует или делается неосиляторами. Иначе не было бы дыр, который живут в важных кусках кода по много лет, вызванных особенностями Си. Дыра типа «переполнение буфера» возникает в результате сочетания предпосылок в языке, работы программиста и недостаточного аудита. При том, «достаточный» аудит - это скорее всего алгоритмически неразрешимая задача (смотря как его определять).

den73 ★★★★★
() автор топика
Последнее исправление: den73 (всего исправлений: 1)
Ответ на: комментарий от AntonI

Для кого и зачем пишется, раскрою в течение 2 месяцев у себя в телеге, ссылку где-то выше в теме кидал. Если реально интересно, может быть и здесь сделаю сообщение. И что Вы сказали студенту? «Потому что так сказал преподаватель»? Или что-то иное?

den73 ★★★★★
() автор топика
Последнее исправление: den73 (всего исправлений: 2)
Ответ на: комментарий от den73

Потому что для наших задач (HPC, сиречь числодробилки) ничего лучше пока нет. А «С++ ужасный язык» это мои слова, не его.

AntonI ★★★★
()
Ответ на: комментарий от archie

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

den73 ★★★★★
() автор топика
Ответ на: комментарий от AntonI

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

den73 ★★★★★
() автор топика
Ответ на: комментарий от den73

Я вот не понимаю, что там хорошего

Вот это декларация, а «для наших задач (HPC, сиречь числодробилки) ничего лучше пока нет.» это констатация факта. Не выдергивайте пожалуйста слова из констекста. Я не утверждаю что С++ лучший ЯП для всех задач - я говорю только за свою полянку, и когда я говорю «ничего лучше нет» я говорю о всей совокупности факторов. Там где удобнее использовать другие ЯП (питон/баш/тех) используются другие ЯП.

Вот моя аргументация для того студента (копипаста из телеги):

  1. кодовая база и поиск кадров (это таки мейнстрим)
  2. хорошие оптимизирующие компилятора (потому что п1)
  3. сочетания низкоуровневых возможностей (всякая байтофилия и голые пойнтеры как в С) и высокоуровненвые вещи вроде шаблонов, лямбд, классов и пр

Вообще холивары на тему какой ЯП/ОС/граф.редактор лучше свойственны неофитам. Профи понимают что это просто инструменты, где то лучше один инструмент, где то другой. Зависит от задачи и бэкграунда.

AntonI ★★★★
()
Последнее исправление: AntonI (всего исправлений: 1)
Ответ на: комментарий от den73

Ну вроде вот, https://en.cppreference.com/w/cpp/language/eval_order

#include <cstdio>
int a() { return std::puts("a"); }
int b() { return std::puts("b"); }
int c() { return std::puts("c"); }
void z(int, int, int) {}
int main() {
    z(a(), b(), c());       // все 6 перестановок возможны
    return a() + b() + c(); // все 6 перестановок возможны
}

Т.е. если нам надо закодировать некую формулу и порядок сложения важен, то, видимо, нам придётся её разбить на предписания-выражения (expression statement), по одному для каждой операции, и будет не очень удобно читать. Если я правильно понял то, что там дальше написано про упорядочивание (особо некогда).

den73 ★★★★★
() автор топика
Ответ на: комментарий от AntonI

Я ничего не выдёргивал, может быть неоднозначно написал. Я имел в виду, что утверждение «C++ лучше всех для числодробилок» - именно это есть просто декларация. Не знаю, может сейчас уже что-то изменилось, а раньше никто не трогал блас-лапак. В Фортране a+b+c выполняется в определённом порядке. Мне приходилось писать программы для «вычисления формул», и мне представляется, что вычисление выражений в определённом порядке - это очень важно для них. Но я писал их недостаточно много. Наверное, возможность переопределять арифм. операции для своих типов - это достаточно хорошо, но и оно в Фортране есть.

den73 ★★★★★
() автор топика
Последнее исправление: den73 (всего исправлений: 1)
Ответ на: комментарий от den73

Но в целом я не собирался это оспаривать, а просто хотел узнать обоснование.

den73 ★★★★★
() автор топика
Ответ на: комментарий от AntonI

хорошие оптимизирующие компилятора (потому что п1)

Не приходилось ли наступать на грабли? Это опять же вопрос без всякого подтекста. Я знаю, что грабли в оптимизирующих компиляторах бывают, что ускорение - это важно, и что искать ошибки в числодробилках нелегко. Например, я как-то случайно пришёл в команду и увидел, что у них в уравнении Бернулли забыли на двойку поделить, но численный вклад его в общее давление был всегда мал и возможно это ни на что не влияло. У них эта ошибка долго жила.

С другой стороны, я слышал, что в llvm каждую неделю находят ошибки.

den73 ★★★★★
() автор топика
Последнее исправление: den73 (всего исправлений: 1)
Ответ на: комментарий от archie

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

den73 ★★★★★
() автор топика
Последнее исправление: den73 (всего исправлений: 2)
Ответ на: комментарий от den73

что утверждение «C++ лучше всех для числодробилок» - именно это есть просто декларация.

За этой «декларацией» 20 с гаком лет опыта (как личного так и целого ряда коллективов являющихся лидерами в этой области) и по крайней мере три аргумента которые я привел.

Можно открыть программу каких нить RuSCD или PaCT и поинтересоваться какая доля представленных там кодов написана на С++, какая на фортране а какая на всяких хаскелях и пр.

мне представляется, что вычисление выражений в определённом порядке - это очень важно

Нет. За 20 с гаком лет я ни разу не видел, что бы смена порядка вычислений выражений привела бы к ошибке. Да, это приводит к тому что ответ может меняться за счет ошибки округления - но это как правило неважно.

AntonI ★★★★
()
Ответ на: комментарий от den73

Не приходилось ли наступать на грабли?

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

У них эта ошибка долго жила.

Такое бывает, но опять таки - это не ошибка компилятора. Компиляторы конечно отттестированы куда лучше числодробилок (за редким исключением), потому что в тестирование компиляторов вкладываются несоизмеримо больше ресурсы.

AntonI ★★★★
()
Ответ на: комментарий от den73

мне представляется, что вычисление выражений в определённом порядке - это очень важно для них.

По умолчанию в gcc, clang, Visual C++ этих оптимизаций нет в -O2 или -O3

Нужно добавлять специальный флаг: -ffast-math для gcc/clang или /fp:fast

Ну и да, в документации написано что эти оптимизации делают и когда это может привести к проблемам.

Ну и статейки всякие есть, вот ознакомься: https://simonbyrne.github.io/notes/fastmath/

fsb4000 ★★★★★
()
Ответ на: комментарий от den73

И есть еще 4й аргумент в пользу С++ в HPC - CUDA. GPU нынче мейнстрим, а CUDA жрет плюсовый код (с минимальными правками) как родной. Это очень, очень дорогого стоит.

Что касается лапака и пр - никто конечно не будет переписывать старые хорошие библиотеки, к ним просто пишут интерфейс на плюсах. Да и на фортране классические задачи которые хорошо ложатся на линейную алгебру иногда еще пописывают, но все реже и реже. За 20 с гаком летом я работал всего с двумя боевыми кодами на фортране (один до сих пор в строю, давно идут разговоры о том что бы его отрефакторить на плюсах). На лоре я знаю ровно одного человека который делает фортрановский код для предсказания погоды. Остальное плюсы или связка плюсы-питон, плюсы-луа и тд. Хаскель пытались у нас привить - не прижился.

AntonI ★★★★
()
Последнее исправление: AntonI (всего исправлений: 1)
Ответ на: комментарий от X512

Я не понял вопрос :( там есть скрипт (одна команда если доверяешь и три, если нет), который устанавливает никс в любой дистр, и есть вроде инструкция как собрать из исходников, я даже как то собрал пару лет назад.

zerhud
()
Ответ на: комментарий от AntonI

где то лучше один инструмент, где то другой

Я использовал и другие языки. Пока просто натыкаюсь на то, что после плюсов на них как говорить без пары зубов, хочешь что сказать а свистишь. Такое бывает и на плюсах, когда хочешь одно а они не могут (ну могут через одно место), но в остальных такое гораздо чаще. Так что тут я не совсем согласен. Бывает даже простенькие утилиты пишу на плюсах, так как легче и быстрее. Ну разве что всякие sed grep и прочие тулзы конечно.

zerhud
()
Ответ на: комментарий от zerhud
> bootstrap.sh 
autoreconf: export WARNINGS=
autoreconf: Entering directory '.'
autoreconf: configure.ac: not using Gettext
autoreconf: running: aclocal --force 
autoreconf: configure.ac: tracing
autoreconf: configure.ac: not using Libtool
autoreconf: configure.ac: not using Intltool
autoreconf: configure.ac: not using Gtkdoc
autoreconf: running: /packages/autoconf-2.71-1/.self/bin/autoconf --force
configure.ac:1: warning: AC_INIT: not a literal: "m4_esyscmd(bash -c "echo -n $(cat ./.version)$VERSION_SUFFIX")"
configure.ac:108: error: possibly undefined macro: AC_MSG_ERROR
      If this token and others are legitimate, please use m4_pattern_allow.
      See the Autoconf documentation.
autoreconf: error: /packages/autoconf-2.71-1/.self/bin/autoconf failed with exit status: 1

Как это исправить?

X512 ★★★★★
()

Особенностью и главной сложностью в С++ является то, что одно и то же можно реализовать многими способами.

Основным пробелом С++ на мой взгляд является то, что нет официальных рекомендация по тому, как делать лучше и правильно: какие конструкции использовать. как оформлять код и т. п. В том же Python это есть, и по опыту скажу это здоровски помогает.

Теперь к конкретике:

насколько широко в плюсах используются нуль-терминированные строки, юнионы, неумные указатели и всё такое плохое, что делает Си опасным языком.

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

а) С++ перекрывает Си, поэтому там всё сделано по-другому, поэтому безопасность выше б) С++ - наследник Си и в целом наследует его недостатки.

Моё мнение: C++ расширяет С конструкциями и библиотеками, которые позволяет сделать безопасно/оптимально и т. п. Но без включения мозга при использовании этих инструментов код не станет более безопасным или оптимальным, а за это отвечает программист. Это отличие от некоторых других языков, которые буквально навязывают лишь один способ написания программ, который весьма неплох в 90% случаев, и усложняет жизнь (иногда очень сильно) в оставшихся 10% случаев.

P. S. Для тех, кто придерётся к фразе «С++ расширяет С»: я в курсе, что это не совсем так, и есть несовместимости между С и С++, и что более правильно говорить, что С++ это другой язык. Но покуда мы не в суде и не в швейцарском банке, на мой взгляд выражение «C++ расширяет С» лучше всего отражает смысл.

всё такое плохое, что делает Си опасным языком.

Это не делает язык опасным. Это лишь повышает порог входа для программистов. Но чтобы писать безопасные программы используя другие языки программирования, всё равно придется погрузиться в том, как ресурсы выделяются и высвобождаются. Простыми словами, на джуна С++ нужно учиться дольше, чем на джуна, скажем, Java. Но путь с нуля до Senior Java developer насколько же долог и тернист, насколько и путь Senior С++ developer, а то и тернистее. (При этом под уровнем программиста я понимаю способность выполнять разного рода задачи, и при этом выдавать код одинакового качества).

Кстати, совсем недавно мы ловили memory leak в Java приложениях. И это был ад, так как в Java есть много очень «удобных инструментов по автоматическому управлению памятью», которые представляют собой еще ту магию в которой приходилось разбираться. И это могли сделать единицы из нашей немаленькой команды. И это заняло прилично времени. В С++ это было бы проще, я уверен.

Так что более опасно: декларировать что «теперь вы можете не думать о выделении и высвобождении памяти» и потом бороться с ситуациями когда это не сработало, или с самого начала приучать программиста о том, что он должен об этом думать? И, да, память - это не единственный ресурс: как на счёт автоматического постановки и освобождения блокировок (locks), открытия/закрытия соединений, выделения/освобождения идентификаторов и т. п.? Есть такие garbage collectors и умные… указатели?

Kroz ★★★★★
()
Последнее исправление: Kroz (всего исправлений: 5)
Ответ на: комментарий от Kroz

По-моему, в целом совпадает с моими выводами. Ну и плюс добавлены рассуждения про другое, а меня всё же интересовали отдельные вопросы. Утечка памяти в плюсах по идее ловится специально созданными для этого инструментами, про жабу не знаю. Наверное, тоже можно создать инструменты для поиска «живых» утечек - в JS вроде такие есть, но это не точно. Другое дело, что в плюсах сделать такую утечку несравнимо проще. А в Яве это будет либо «живая утечка», например, делаем какой-нибудь кеш и забываем его урезать до заданного размера, либо ошибка в самой реализации Явы, либо что-то на границе с Си (и ответственность можно свалить на Си). Других вариантов не знаю, хотя не джавист.

den73 ★★★★★
() автор топика
Последнее исправление: den73 (всего исправлений: 2)
Ответ на: комментарий от den73

В своём коде не видел. В чужом тоже :)

На самом деле, один раз, кажется, они мне встречались. Так видел автор, видимо.

Другое дело, когда надо использовать какой-нибудь C API, в котором они присутствуют - там уж никуда не деться

yoghurt ★★★★★
()
Ответ на: комментарий от den73

При том, «достаточный» аудит - это скорее всего алгоритмически неразрешимая задача

Как и достаточная «безопасность» языка. Ну т.е. по такой логике она и не нужна.

no-such-file ★★★★★
()
Ответ на: комментарий от den73

плюсах сделать такую утечку несравнимо проще

Утечку памяти - да, в С++ это сделать проще. Но и найти и устранить проще. В Jаva сделать это сложнее, но и отловить на порядок сложнее.

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

Kroz ★★★★★
()
Ответ на: комментарий от fluorite

Я тут небольшой специалист. OpenCL требует многобукоф + ЕМНИП ощутимо (по словам коллег которые в теме) сливает CUDA по скорости.

Как я понял SYCL довольно лаконичен, но вот что у него со скоростью хз.

AntonI ★★★★
()
Ответ на: комментарий от Kroz

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

den73 ★★★★★
() автор топика
Ответ на: комментарий от no-such-file

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

den73 ★★★★★
() автор топика
Последнее исправление: den73 (всего исправлений: 2)
Закрыто добавление комментариев для недавно зарегистрированных пользователей (со score < 50)