LINUX.ORG.RU

юнионы в C++

 


2

4

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

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

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

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

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

★★★★★

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

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

Если взять Оберон, то там есть тег типа, да, он жирноват. Но в Паскале-то что? Передаёшь адрес массива в любую функцию, если его размер известен статически. Если размер статически неизвестен, то размер всё равно надо как-то передать, т.е. и в Сях придётся задействовать два регистра. Если же у нас 0-терминированная ASCII строка - то да, она всегда требует ровно 1 байт для хранения длины. В то время как строка-массив потребует аж целых 2 байта, если я не обсчитался, на 32Кб. Ну ок, может быть это и так и действительно на сях можно чуть большую программу впихнуть в ту же память. Плавали, знаем. Насчёт итерации я уже меньше уверен, ведь есть же специальные инструкции для итераций по массивам с доп. регистром-счётчиком, если мне не мерещится, конечно. И что, именно по этой причине, ради экономии места на 32-килобайтных машинах в XXI веке операционка на миллиард строк, которая работает на гигабайтах памяти, должна взламываться через переполнение буфера? Ради того, что на этом же языке можно писать на 32-килобайтных МК или как их там называют?

Или я всё же ещё что-то упустил?

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

смотри на сишную строку как на тип «последовательность байт» - формально это тип вида

sequence of char(0)

что обозначает, что это последовательность символов с терминальным символом равным 0.

карочи сишная строка это не массив, а такой тип. такой тип передается по указателю на первый элемент.

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

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

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

Можно, например, вызвать исключение с помощью простого strlen.

если strlen не находит нуля - значит это ill-formed data. аналогично можно упасть на неверных данных любого типа. sequence тут не причем

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

Ок, а можно сделать такой компилятор Си, который скажет: эй, парниш, ты тут получаешь данные извне, и не проверяешь, что они соответствуют типу, как ты его назвал, sequence of char(0) ?

Или рантайм, который от этого защитит?

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

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

struct String {
  int refcount;
  int length;
  char data[0];
};

Строка с числом ссылок более единицы неизменяема. По крайней мере в культурном обществе.

Все это можно решать CoW-строками, но shared_ptr это НЕ CoW-строка

Да, shared_ptr — это костыль из дерьма. который призван подпирать недостатки архитектуры языка. А именно: финализация объекта и высвобождение памяти являются двумя различными процесссами для любой софтины сложнее hello world, но C++ жестко привязывает одно к другому, из-за чего ты не можешь выполнить финализацию после декремента последней жесткой ссылки, чтобы в результате этой финализации почистились мягкие ссылки, refcount упал до нуля, и все объекты спокойно высвободились с гарантией, что на них нет ссылок.

Вместо этого вводится понятие «weak reference», ссылка на вспомогательный управляющий блок, который и представляет собой «объект после финализации, но до высвобождения памяти». Потому что в C++ в принципе нет отдельных механизмов инициализации-финализации — это можно не замечать, на это можно плеваться, но эта проблема существует:

https://250bpm.com/blog:4/ — Why should I have written ZeroMQ in C, not C++ (part I)

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

Да, медленнее – но они предоставляют требуемые гарантии, а интрузивные – нет. Список требований сформирован так, чтобы удовлетворять большинству запросов, а не чтобы выжать максимум.

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

Большинство ошибок, связанных с повреждениями памяти, отлавливаются еще на этапе тестов

Или же до сих пор лежат в твоих проектах, никак не проявляя себя даже в проде, а когда проявляются — списываются на сбойную оперативу.

Ну как сказать: санитайзеры являются очень хорошим подспорьем в этом деле

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

Ну, и какие еще есть функции у union, которых нет у каста указателя?

strict aliasing и удобство использования

Strict aliasing — это одна из худших, но точно самая спорная фича стандарта Си. Тут регулярно по несколько раз в год происходят срачи по этому ее поводу. И предже всего спор идет о том, есть ли вообще strict aliasing в стандарте или же нам это показалось.

Далее, в чем удобство? Чем оно отличается от каста? Удобно тем, что ты можешь незаметно прочитать не то поле и получить UB?

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

просто проверяешь текущий индекс в буфере заданной длины. а другого способа и нет. во всех «защищенных языках» так.

и если всякие обормоты так не делают - это их проблема.

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

А именно: финализация объекта и высвобождение памяти являются двумя различными процесссами для любой софтины сложнее hello world, но C++ жестко привязывает одно к другому, из-за чего ты не можешь выполнить финализацию после декремента последней жесткой ссылки, чтобы в результате этой финализации почистились мягкие ссылки, refcount упал до нуля, и все объекты спокойно высвободились с гарантией, что на них нет ссылок.

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

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

Ну вот видишь, попытка сделать строку в виде последовательности байтов, завершающихся нулём, не удалась сразу же. Получился опять же массив. Нет, в защищённых языках не так. Элементарная строчка из Паскаля хранит свою длину в отдельном поле и этой проблемы там нет. При том, Паскаль - вовсе не защищённый язык, а самый что ни на есть обычный. Хотя я хз, есть ли строчки в Паскале - говорю про Дельфи версии где-нибудь седьмой. Притом ЕМНИП в турбопаскале была предельно тупая вещь - строки были не длиннее 255 символов, и поэтому длина строки помещалась в 1 байт. Но я могу уже не помнить - это было слишком уж давно. Конечно же, ничто не мешало закодировать число как-нибудь а-ля utf-8, чтобы у коротких строк длина занимала 1 байт, у более длинных - 2 байта и т.п. Хотя, конечно, алгоритм итерации по строке усложнялся бы.

и если всякие обормоты так не делают - это их проблема.

А если ты - сертификационная лаборатория и тебе принесли программу, в которой есть strlen, то что тебе сделать? Уволиться и пойти домой к голодным детям и злой жене? До посинения пытаться доказать, что строка придёт всегда хорошая? Или надо зажмуриться и сделать вид, что всё норм, а потом иметь неприятный разговор с апостолом Петром (или как его там зовут)?

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

Покажите мне адекватных людей, которые будут считать двухбуквенные макросы в нижнем регистре нормой. И для которых нормой будут рукописные (не сгенерированные автоматом) функции на 350 строк

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

Далее ты не понял, что такой код вообще ни на какой стандартной либе не напишется, поскольку это lock-free код, да еще и в разделяемой памяти, а большая часть стандартной либы C++ в таком режиме работать не умеет в принципе. Более того, у lock-free кода есть такая особенность, что изменение одной строчки логики приводит не к изменению поведения, а к полному отказу системы. Потому разбросать цельный lock-free алгоритм по модулям — это худшее, что можно сделать в плане читаемости и поддерживаемости. Да, это одна из самых больших функций в проекте, я тут не спорю.

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

Это называется «у меня есть DRY — зачем мне мозг?». Ты снова и снова прилипаешь к вещам с ничтожной значимостью — это дублирование дает плюс-минус ничего в плане читаемости, не всякий дублированный код является злом, и просто не стоит того, чтобы вообще об этом дублировании париться, когда дублируются пять строчек в двух местах.

Во-первых, const был добавлен в язык только в 1989-ом, т.е. сильно позже появления Cfront 1.0

В заголовках Cfront 1.0 куча упоминаний const char *:
http://www.softwarepreservation.org/projects/c_plus_plus/cfront/release_1.0/s...
1984 год. Я еще раз подчеркиваю, что в Си того времени никакого const не было аж до 1988 года. Потому const 100% произошел от Струструпа, о чем он и пишет в мемуарах.

Во-вторых, мы обсуждаем тот const, который есть сейчас

Внезапно, у C++ до сих пор есть широко используемый стандарт C++11, в котором copnst и constexpr имеют значение, несколько отличное от C++14.

byko3y имеет право обзывать людей как ему вздумается, но в адрес byko3y по правилам LOR-а высказать такую же оценку его умственных способностей низзя

Я просто делаю достаточно очевидные выводы, подкрепляя их отдельными фактами. Дуглас Крокфорд, например, очень много намекает на умственную неполноценность некоторых кодеров, коучей. авторов книг, и представителей комитетов — он просто не называет кретинов кретинами. А я называю — вот и вся наша разница. Вроде пока что меня не забанили и травить не начали.

Ну как ты сделаешь иначе список const char *?

На самом примитивном уровне:
struct list_node {
const char * m_payload;
list_node * m_prev;
list_node * m_next;
};
И в чем проблема?

Да. хорошо, сделали. Теперь как его скормить функции, у которой сигнатурка аргумента list<char *>? Она не изменяет значение, просто у нее сигнатура такая, потому что обычно принято не делать контейнеры из константных объектов. Выхода два: копировать объект в рантайме или копипастить функцию. Что и требовалось доказать.

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

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

Это только пример. Смотри на это как на две иерархии взаимосвязанных объектов (сущностей). Не всегда отношения 1 к 1 но часто.

Так что это не ребенка пополам, и даже не семью, и не два вида, а два рода (из биологической систематики).

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

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

Кто, что и как поменяет? Давайте уже перейдем к конкретным примерам кода

Вот я уже давно нацарапал пример:

https://godbolt.org/z/Krc73v5Te

Функция получается константную ссылку и меняет объект из поля. С таким же успехом эту ссылку можно вернуть из функции и пустить в свободное плавание.

В конкретном прикладном коде вы можете написать только то, что вам нужно здесь и сейчас

Жму руку, только это значит «перестать лепить сложные превращения const ссылок»

что это за цитата «перестать лепить сложные превращения const ссылок» и к чему она здесь?

Чтобы правильно описать, например, возврат ссылки на поле объекта-аргумент, нужно обрабатывать const/non-const, value/reference/rvalue reference, то есть, писать два, три, четыре дубля функции. Например, начиная с C++11 стало нормой с ходу писать три конструктора классу (создания, копирования, перемещения). Очевидно, что ни для какого «здесь и сейчас» такое складирование копий не нужно — это работа строго «на будущее». Если мне нужно «здесь и сейчас», то я пишу простой struct и простые функции с аргументами без модификаторов.

значения, спрятанные за define для C++ константами не являются поскольку подставляются они в код еще на этапе работы препроцессора. Т.е. еще до того, как в дело вступят сами правила языка C++

У вас C++ головного мозга, вам срочно нужна госпитализация. В математике и во многих адекватных ЯП, которые не используют знак тождества для операции присвоения, константа — это непосредственное значение, то есть, в записи

#define a 2
константа — это цифра «2», а не идентификатор «a». Удивительно то, что даже в K&R Си константа — это буквальное значение, а не какие-то там идентификаторы. Мода называть переменные константами произошла именно от Страуструпа — за что ему большое спасибо, хороший архитектор был.

Потроха реализации vector-а к семантике, с которой имеет дело пользователь vector-а, не имеет отношения

Но в shared_ptr почему-то имеют. А в optional снова не имеют, но по другому. Это я про чудеса соглашений по семантике разыменования указателя.

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

Это только пример. Смотри на это как на две иерархии взаимосвязанных объектов (сущностей). Не всегда отношения 1 к 1 но часто

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

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

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

Что намекает на превращение? Это именно создание. И, как я уже говорил, далеко не всегда 1 к 1 как количественно так и по типам.

Советы свалить все в один монолит - именно от вас такие советы ожидаемы.

:)

Но, по понятным причинам не подлежат рассмотрению.

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

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

Вот и я об этом. Компилятор сам разбирается, что там можно вычислить, а что — нельзя.

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

Уже срач на десятки страниц, а всё элементарные вещи понять не можете. Между const и constexpr почти ничего общего

Стандарт C++11 так не думает — constexpr в нем подразумевало const. Только начиная с C++14 они стали разныме вещами.

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

Когда математики родили новый алгоритм, или гугло вывалило какой-нибудь хттп100500, или ты вот принципиально новую БД написал

Какой ты новый алгоритм можешь вспомнить? Если говорить по поводу HTTP/2-3, то я не вижу новизны вообще — именно потому они и называются «HTTP». Двойка просто позволяла мультиплексировать соединения по одному каналу вместо инициализации независимых соединений, что дает плюс-минус ничего, если у тебя сайт при своей работе не дергает периодически большие пачки запросов — а это опасно для рандомного говносайта, потому что клиенты могут завалить неэффективные сервера через «эффективный» протокол.

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

Так что нового?

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

В девятнадцатом веке. На самом деле, современные кузнецы по прежнему относительно востребованы, потому что на заводе не сделаешь кастомную железяку: ножики, решетки, стальные двери.

Тринадцатилетние дети в бангладеше и пакистане (и у нас) будут лепить вебню за полчаса от получения ТЗ, по 12 часов в день, чтобы было чем наутро позавтракать. И уже лепят, и не только на вордпрессе, за пять баксов в час. Там, я думаю, за тот же прайс предлагаются и нейросети и блокчейн и глушитель приварить и котенка утопить.

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

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

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

Вместо ходьбы на двух хромых костылях надо было просто сразу взять коммон лисп, и всё бы вышло. Он и считать умеет, правда, по микробенчмаркам втрое медленнее Си, а биндинги особо тормозят (на последней задаче эпически слил питону и был выпилен)

На питоне писать DSL достаточно просто для того, чтобы можно было навсегда забыть про лисп. Вот почему биндинги тормозят в лиспе — это интересный вопрос, мне неочевиден ответ.

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

Ты ещё про более плохую часть программирования не сказал: в результате успехов программирования становятся не нужны не только программисты, а и вообще люди

Луддиты появились еще в начале 19-го века.

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

Мне нравится, как ООП приготовлено в питоне. Хороший пример «easy to learn, hard to master»

Использование метаклассов в Python

Моя любимая статья по теме:

https://habr.com/en/post/140581/ — Перестаньте писать классы

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

Я вообще не знал что так можно до твоего коммента

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

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

И ты надеешься попасть число оставшихся?..

Хороший вопрос, но у меня нет на него ответа. Я много лет хотел вообще уйти из айти.

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

Это что ли про ансейф? Оставим пока в стороне «независимость» этих «подъязыков» (с чем я не согласен), но как бы ты сделал? Выкинул бы ансейф?

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

Паскаль образца 1975 года уже обеспечивал гарантию корректной работы с указателями или смерть. Паскаль образца 1983 требовал делать три раза «ку» в виде преобразования указателя в число, операций над числом, и обратного преобразования в указатель — в коде сразу было видно, что это unsafe, без каких-либо дополнительных ключевых слов. Вся проблема корректной работы с указателями вызвана убогостью сишной методологии, а не является какой-то самостоятельной сложностью.

Такой раст был бы мало кому нужен - тогда уж имело бы смысл добавить/оставить GC

Уже есть D.

Оставить только ансейф (убрать гарантии от компилятора и пусть за всё отвечает программист)? Тоже такое себе

Оставить только unsafe, только сделать его безопасным. Ы-ы-ы.

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

Тебе нравится мести двор или ты посчитал, что твой шанс примерно в 1 пятимиллионную попасть в три целых семь десятых тебя устроит?

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

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

unsafe понял (он необходим для портирования программ с иных языков), а второй?

А второй — это Rust по умолчанию.

Так ты ж говорил, что const слишком простой и поэтому неадекватен. Ты предлагал его вообще убрать?

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

Да, нынче можно полностью устранить вредную привычку давать переменным модификаторы const, оставив только constexpr, дающий настоящие константы. По сути, Страуструп пытался сделать ручную оптимизацию через свой readonly/const, чтобы не инициализировать значение каждый раз, а ссылаться на уже однажды инициализированное. Еще он то же самое делал через явный модификатор inline у функций. По итогу явный инлайн стал мешать оптимизациям, а не помогать им, но инфраструктура для поддержки инлайна в виде бессменных текстовых файлов заголовков очень больно бьет по времени компиляции — иначе бы уже давно можно было с концами повыкидывать этот рудимент.

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

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

Например, в современных ОС очень солидную долю процессорного времени в системном коде занимают переключения контекстов — прерывания и системные вызовы. Если придумать инструменты написания программ для реализации надежных алгоритмов взаимодействия меж контекстов без переключения этих контекстов, аля io_uring, только человечнее, то экономия на переключениях контекстов вполне могла бы с лихвой окупить неоптимильность этого ЯП или компилятора. На нынешний момент эффективная асинхронность без переключения контекстов чудовищно сложна в реализации на тех же C/C++.

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

константных типов, короче, быть вообще не должно

Наконец на 15 странице первый плюсовик согласился со мной. Первая монетка в копилку «это не я необучаемый, а плюсовики зашоренные».

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

Простой пример - базовый шаблон итератора, который специализируется константным и неконстантным указателем для константного и неконстантного итератора соответсвенно

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

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

101 раз: ты неявно предполагаешь, что пишется большая программа, которой, в общем-то, наплевать на аппаратуру. А это не всегда так. Поэтому нужны и надёжные инструменты, и простые с минимальными накладными. Как их разделять, другой вопрос. да хоть через @safe / @system, как в D. Главное, чтобы они были в ассортименте

Для аппаратуры есть явные касты, для логики есть tagged union. Какой смысл делать тип union, если компилятор по нему не дает никаких гарантий? Всё остальное — от лукавого. В том числе от лукавого указатели, являющиеся одновременно массивами.

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

Минимальное? Это какое? Меньше, чем мой член?

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

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

Боюсь промазать, но с const разве не так же? С одной стороны это слово означает неизменный объект, а с другой - оно же означает объект, доступный только для чтения. Не?

Нет, потому что по изначальной интерпретации Страуструпа const — это модификатор типа переменной, которую нельзя изменять после вызова конструктора и перед вызовом деструктора.

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

на «рынке» безраздельно царили RSX, который стремились заменить на более гибкий UNIX и CP/M. VMS разрабатывалась позже, работала на дорогом оборудовании и никогда не была массовой

Ты не допускаешь, что если бы в то время не было Unix-а, то его место быстро бы заняло другое написанное на коленке решение? Примерно как на коленке был написан и сам Unix.

Бэйсик оставим отдельно, паскаль имел кучу ограничений и к тому-же, компилировал в тот-же Пи-код. Так что Си идеально пришёлся ко двору, тем более, что его конструкции идеально ложились на команды PDP и очень хорошо на 8080/8086

Наоборот, уже 8086 создавались под эти инструкции. Позже это стало еще более явно выражено. А вот портируемость пи-кода теперь внезапно стала недостатком — с какой стати?

С точки зрения тогдашнего пользователя, это был прорыв: можно было отказаться от ассемблера и одновременно практически не связывать себе руки ограничениями. На тогдашних убогих (по памяти) машинах это было огого, как необходимо

И потому MS DOS был переписан на чистом асме?

Да, в самом нет PL/M структур/массивов, но их можно было реализовать на препроцессоре

Ой. даже без комментариев

Нет уж с комментариями, пожалуйста, если речь зашла про «хорошо подошел для 8080». Разве Си вообще умел работать на 8-битках? Там даже на 16-битных процессорах мало где были сопроцессоры — им не так-то просто было реализовать сишные числа с плавающей точкой.

Заставлял. Та же причина, из которой ноги растут у позиционности Фортрана и известных фич Бэйсика: скудость ресурсов

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

Просто скажи мне, что заставило не иметь в C++ поддержки норм строк?

А что такое «нормальные» строки? Со счётчиком?

Не со счетчиком, а с длиной.

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

Лоровские растовики знают раст на уровне дна, они понятия не имеют если что-то не работает это потому что не должно работать или это баг в компиляторе
Точно также и в Javascript.

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

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

Нет, не ортогональна : опять исторический контекст. Си работал(!) на машине с 32Кб ОЗУ и генерировал код, который на этой-же машине можно было исполнить. Понятно, что оптимизаций в этом случае было ровно ноль

Первый турбопаскаль работал на 16 кб. Конечно, он был о-о-о-чень куцым, в подметки не годился той IDE, которой стала версия 7.0, но все же, это был редактор + запускалка команд + компилятор.

Это сейчас для контроллеров «с дюжиной кб» можно писать на ++, которые работают на машине с безразмерной памятью и могут себе позволить любые оптимизации. А в те времена классическое *(x++) = … было верхом изящества

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

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

Потому, то равенство a[0] и *a позволяет передавать массив в любую функцию без накладных и бежать по массиву парой команд ассемблера

Внезапно, паскаль бежит по массиву парой команд асма.

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

А если бы нативно работал, ему бы массивы помешали догнать и перегнать Си?

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

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

Советы свалить все в один монолит - именно от вас такие советы ожидаемы.
Но, по понятным причинам не подлежат рассмотрению

Да, причины ясны — Коран запрещает. Я вот как открывая сорцы Firefox, так мне сразу хочется плакать от десятков тысяч заголовочков на 10-20-30 строчечек.

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

Например, в современных ОС очень солидную долю процессорного времени в системном коде занимают переключения контекстов — прерывания и системные вызовы. Если придумать инструменты написания программ для реализации надежных алгоритмов взаимодействия меж контекстов без переключения этих контекстов, аля io_uring, только человечнее, то экономия на переключениях контекстов вполне могла бы с лихвой окупить неоптимильность этого ЯП или компилятора. На нынешний момент эффективная асинхронность без переключения контекстов чудовищно сложна в реализации на тех же C/C++.

Ну тут надо ещё учитывать, что программы в сумме могут запросить намного больше потоков, чем есть ядер (даже ядер*2) в системе. И тогда без вытесняющей многозадачности не обойтись.

Вообще, словосочетание "асинхронность без переключения контекстов" мне не нравится. Вот «синхронность без переключения контекстов» — это хорошо, я сам так делаю (на GPU). А асинхронность она как бы предполагает переключение контекстов by design.

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

в турбопаскале была предельно тупая вещь - строки были не длиннее 255 символов, и поэтому длина строки помещалась в 1 байт. Но я могу уже не помнить - это было слишком уж давно. Конечно же, ничто не мешало закодировать число как-нибудь а-ля utf-8, чтобы у коротких строк длина занимала 1 байт, у более длинных - 2 байта и т.п. Хотя, конечно, алгоритм итерации по строке усложнялся бы.

Недопустимо усложнялся бы. Эта проблема решалась просто — длина строки выносилась перед строкой и занимала платформоспецифичное число байт.

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

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

А я где-то так говорил? Константность прежде всего про интерфейс объектов, даёт возможность сохранять инварианты и не разломать внутреннее состояние, когда зовём условный auto &ref = obj.get_view(), честного слова программиста «не буду модифицировать» - мало.

ошибки класса «прочитал значение по указателю, ссылающемуся на высвобожденный участок памяти»

санитайзеры.

PS: в случае с итераторми STL, оно специализируется не константным указателем, а указателем на константу, пример был мимо. В общем const’ый typedef - какая-то странная хрень и зачем она нужна - не знаю.

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

Ну тут надо ещё учитывать, что программы в сумме могут запросить намного больше потоков, чем есть ядер (даже ядер*2) в системе. И тогда без вытесняющей многозадачности не обойтись

Никто не спорит, что иногда нужно переключать задачи. Другое дело, что уже примерно 30 лет технологии позволяют засовывать на один чип более четырех 32-битных процессоров — чаще всего проблема заключается в том, что ПО не способно утилизировать ядра, а не в том, что ядер мало.

Более коварная проблема заключается в том, что даже многоядерные процессоры последние годы активно оптимизируют для однопоточного выполнения, из-за чего выполнение серьезно замедляется, когда в одну область памяти начинают лезть несколько ядер, даже если это не атомарные операции. Особенно жестко это проявляется на x86.

Вообще, словосочетание «асинхронность без переключения контекстов» мне не нравится. Вот «синхронность без переключения контекстов» — это хорошо, я сам так делаю (на GPU). А асинхронность она как бы предполагает переключение контекстов by design

«Асинхронность» значит просто «процессы выполняются как попало относительно друг друга». К сожалению, ЦП безнадежно ушли в те дебри, где синхронное параллельное выполнение реализовать намного сложнее, чем асинхронное. Ну вот нет на ЦП команды __syncthreads.

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

даёт возможность сохранять инварианты и не разломать внутреннее состояние, когда зовём условный auto &ref = obj.get_view(), честного слова программиста «не буду модифицировать» - мало.

«auto &ref = obj.get_view()» — это приглашение к некорректному доступу к памяти. Одно из недавних нововведение в плюсах, string_view, позволяет легко и непринужденно написать use-after-free, который не выглядит как таковой. Ты уже напрашиваешься на чтение некорректной памяти — что ты там еще собрался защищать от разламывания?

ошибки класса «прочитал значение по указателю, ссылающемуся на высвобожденный участок памяти»

санитайзеры

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

Если тебе нужна безопасность, то ты получаешь доступ к объекту через безопасные интерфейсы, которые проверяют корректность доступа на каждом чихе. Если ты используешь view/iterator — ты готов выстрелить себе в ногу в любой момент. Так что либо крестик сними, либо трусы надень.

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

Одно из недавних нововведение в плюсах, string_view, позволяет легко и непринужденно написать use-after-free, который не выглядит как таковой.

А можно пример кода? Думаю, bugfixer тоже было бы интересно

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

А можно пример кода? Думаю, bugfixer тоже было бы интересно

Это баян же ж:

#include <iostream>
#include <string>
#include <string_view>

int main() {
  std::string s = "Hellooooooooooooooo ";
  std::string_view sv = s + "World\n";
  std::cout << sv;
}
byko3y ★★★★
()
Ответ на: комментарий от byko3y

что ты там еще собрался защищать от разламывания

консистентность данных объекта.

это приглашение к некорректному доступу к памяти. Одно из недавних нововведение в плюсах, string_view, позволяет легко и непринужденно написать use-after-free, который не выглядит как таковой.

Нет, это нормальный подход, кресты «пропитаны» этим на примере инвалидации тех же итераторов. Если с этим не умеешь жить, то ты явно ошибся с языком.

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

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

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

Мне достаточно санитайзеров. Делать дорогие и «безопасные» интерфейсы я точно не собираюсь.

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

Да, очень страшно

$ g++ 1.cpp -fsanitize=address
$ ./a.out

==3209==ERROR: AddressSanitizer: heap-use-after-free on address
kvpfs ★★
()
Ответ на: комментарий от byko3y

Этот макрос — тупо мусор, который я внес для бенчей и не выкинул обратно.

  1. Вы написали такой макрос. Он сам по себе говнокод, его в таком виде быть не должно было изначально. Тот факт, что вы сочли возможным его написать уже говорит многое о вас как о программисте.

  2. Вы оставили этот говномакрос в коде. Еще один факт, который говорит о вас как о программисте.

Функция на 350 строк на самом деле намного меньше 350 строк, потому что большая часть ее состоит из мертвого кода и комментариев — я специально посчитал, там 133 строки живого кода.

  1. Большое количество мертвого кода в функции == говнокод.

  2. Даже 133 строки – это в шесть раз больше приемлемого объема. Что == говнокод. Плюс демонстрирует вашу неспособность в композицию.

Далее ты не понял, что такой код вообще ни на какой стандартной либе не напишется, поскольку это lock-free код

Это не важно.

Ты снова и снова прилипаешь к вещам с ничтожной значимостью — это дублирование дает плюс-минус ничего в плане читаемости

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

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

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

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

Дуглас Крокфорд, например, очень много намекает на умственную неполноценность некоторых кодеров, коучей. авторов книг, и представителей комитетов — он просто не называет кретинов кретинами. А я называю — вот и вся наша разница. Вроде пока что меня не забанили и травить не начали.

Вы в очередной раз не поняли о чем вам говорят.

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

Но стоит только кому-нибудь (вроде меня) высказать в ваш адрес тоже самое, что вы говорите в адрес Страуструпа или членом комитета по стандартизации C++, как высказавший это попадет по санкции.

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

Жесть! Спасибо

Это уровень Эдика, который отключал предупреждения. Так и buko3y пишет. Intellesense это подчёркивает сразу же и пишет:

Предупреждение	C26815	Это висячий указатель, так как он указывает на временный экземпляр, который был уничтожен.

https://imgur.com/a/QP4qxvF

https://docs.microsoft.com/en-us/cpp/code-quality/c26815?view=msvc-170

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

Жесть! Спасибо

Существует множество способов различной степени извращённости как отстрелить себе ногу. Пример со string_view не очень хорош так как вот именно от такого misuse можно было легко защититься с zero cost in runtime просто запретив string_view(string&&), даже немножко удивлён что этого не было сделано. Можно привести гораздо более тонкие примеры которые даже очень опытные люди не сразу увидят. Но участвовать в дальнейшей дискуссии на тему какой C++ ужасный, мне, мягко говоря, не очень хочется. И вообще я считаю что потроллили нас тут всех здесь знатно…

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