LINUX.ORG.RU

Правильный(???) стиль работы со строковыми данными на С

 ,


2

4

Иногда (прямо сейчас) приходится обрабатыавть строки на C.
Меня коробит от громоздкости операций выделения памяти, конкатенации и самое главное это snprintf с проблемой размера буфера под конечную строку, гигантское поле для выращивания вских мемориликов по невнимательности.

К примеру, размеры mult1_str, mult2_str и equal_str известны, нужно выделить память под всю строку:

snprintf(
    buf,
    buflen,
    "%s miltilple %s equals %s",
    mult1_str,
    mult2_str,
    equal_str
    );
варианты:
- махнуть шашкой и сделать килобайт на стеке ( ((( )
- ничем не размахивать и посчитать руками. (еще хуже)
- написать функцию которая будет вычислять длину «%s miltilple %s equals %s» без символов подстановки (уже лучше)
- отказаться от snprintf и собирать строку пачкой конкатенаций с аллокациями памяти и прочим...

А как делаешь ты?


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

Ок, std::string. Они могут храниться где угодно.

А где угодно - это где? (без собственного кастомного аллокатора). Вот к примеру, написал ты:

std::string s1 = s2;
где s2 - это тоже std::string переменная с каким-то значением. Что в этот момент происходит внутри STL-класса?

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

На стеке ...

Не-не-не. Строчка в s2 у нас очень длинная оказалась ) Что в этот момент происходит внутри STL-класса?

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

А где угодно - это где? (без собственного кастомного аллокатора)

О, неужели наконец прочитал про кастомный аллокатор? И сфига ли без него?

Где угодно, это, например, на стеке, потому что SSO. Что позволяет писать высокоуровневый код работающий со строками без вызовов new вообще. С использованием кастомного аллокатора, как ты наконец догадался, данные строк также можно хранить где тебе нравится - в специально выделенном для этого глобальном куске памяти, на стеке, в TLS, в регистрах, где угодно. Так что всю чушь которую ты нёс про необходимость рантайма, якобы предоставляющего некую магическую кучу, непременно написанную на C, можешь забыть. Аллокатор - это всего лишь функция, возвращающая кусок памяти, в зависимости от твоих нужд она может занимать 5 строк и при этом будет работать весь stl.

Далее, std::string - это не единственная реализация строк, и, понятное дело, не самая удобная для embedded программирования, коли мы о нём заговорили. Боюсь разрушить твой мир, но есть реализации строк которые вообще не хранят содержимое строки. Можно хранить дерево, листьями которого являются константы и ссылки на переменные, а узлами - операции конкатенации. Она ничего не аллоцирует, она ленивая, она умеет считать длину строки в compile time (constexpr), при этом взаимозаменяема с std::string + std::to_string, что позволяет один и тот же код (а у нас большая часть кода прошивки рисует менюшки и взаимодействует с пользователем, помимо неё есть только загрузчик и минимальный HAL) собрать как в автоматические тесты (и иметь в итоге 100% покрытие), так и в десктопное приложение эмулирующее девайс, так и в собственно прошивку, изменением одного флага компилятора.

Я хочу чтобы ты как следует подумал как реализоваыл бы всё это на C.

Вот к примеру, написал ты std::string s1 = s2:

Удивительно, но мы опять возвращаемся к вопросу о реализации строк. Потому что в libstdc++ до c++11, когда были позволены refcounted строки, в этот момент происходило только инкремент счётчика ссылок. Не то что ты ожидал?

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

О, неужели наконец прочитал про кастомный аллокатор?

У тебя воспаленная фантазия. Я много лет программировал платежные терминалы на C++ )

«Сфига ли без него» ...

ну так ты ж у нас про прелести высокоуровневого C++ ратуешь, стало быть не должен жизнь усложнять такими сложностями. Чем писать собственную хитрую реализацию хранения строк (еще лишних ошибок наделаешь), может проще и компактнее просто прямо всё контролировать на С? И с чего ты взял, что мы говорим только об «embedded» и все можно посчитать во время «compile time». Ты ж влез в обычную C-шную тему...

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

У тебя воспаленная фантазия. Я много лет программировал платежные терминалы на C++ )

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

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

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

может проще и компактнее просто прямо всё контролировать на С?

Не может. Проще и компактнее это так:

temp_message = "temperature="s + to_string(temp) + "°C"s;  // temp - показание с датчика

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

На C так сделать нельзя. Ничего из перечисленного. Это не язык программирования, это древность, музейный экспонат.

всё контролировать

Постоянно слышу это от начинающих программистов. Потом это проходит.

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

temp_message = «temperature=„s + to_string(temp) + “°C"s;

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

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

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

Скажи, ты специально придуриваешься? Кода этих менюшек в одном девайсе - под тысячу sloc, и он постоянно дорабатывается. И ошибка не должна оказаться там, а не в двух страницах реализации строк, написанных 5 лет, покрытых тестами и с тех пор не менявшихся, за исключением добавления to_string для пары дополнительных типов. Я очень хотел бы посмотреть как ты напишешь хотя бы одну десятую этих менюшек на C.

И не дай бог еще за границу массива вылезешь… )

О чём я и говорю. В C всё «не дай бог». В C++ этот класс ошибок также можно полностью исключить, при желании на этапе компиляции.

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

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

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

не дай бог - оказаться в одном рабочем пространстве вот с таким фанатиком

годы разработки менюшек в конторе менюшкошлёпов - у любого крыша съедет

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

И ведь так и не захотел явно признать, что в C++ напрямую встроены динамические запросы памяти. Даже когда уже прямо про new-delete несколько раз напомнили и попросили конкретно объяснить, что происходит при присвоении значений произвольной длины в std::string, ужом стал изворачиваться и вести себя как нашкодивший кошак, которого мордой в лужу тыкают. Сначала попытался смухлевать ссылкой на SSO. Не вышло - на какую-то свою персональную реализацию хранения строк попытался стрелки перевести. Демагог и трепло...

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

slovozap упертый хам, но я бы предпочел работать с ним, у тебя какое-то очень альтернативное восприятие происходящего. Ибо C++ без кучи тебе показали. И, между прочим, new-delete запросто могут не использовать кучу. Короче нет, не «напрямую встроены».

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

И, между прочим, new-delete запросто могут не использовать кучу. Короче нет, не «напрямую встроены».

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

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

Под словом «куча»/heap просто подразумевается вариант постоянного хранения данных и объектов - как альтернатива стеку. Без такого функционала это уже не C++, о преимуществах которого принято твердить. Ну и, естественно, такие альтернативные, самописные «кучи» уж точно не добавляют надежности коду. Си ведь ругают за «ненадежность» - а тут на тебе... «улучшили» C++ )

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

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

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

Если использование даже своей кучи недопустимо и в си ты обошелся без нее, то и в C++ обойдешься так же. Контейнеры STL отвалятся большей частью, алгоритмы останутся. И останется еще 100500 фич языка. Не надо думать что он тут же скатится до си.

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

Если использование даже своей кучи недопустимо и в си ты обошелся без нее, то и в C++ обойдешься так же

Т.е. сначала заявляется: С++ такой весь из себя классный и универсальный, а C - «говно» и его нигде использовать нельзя. Ну какой-нибудь молодой студент думает: Во, зашибись - напишу-ка я начальный загрузчик, а потом и новое ядро на C++. Зачем мне этот древний С? И тут начинаются тонкости: этого оказывается нельзя использовать, того тоже нельзя... С runtime-средой непонятки... И вообще чего можно, может рассказать только абстрактный «Вася» - и то, в отношении конкретного компилятора. Зашибись супер-замена.

И останется еще 100500 фич языка.

Ну, это очень сильное преувеличение в отношении системных задач.

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

И тут начинаются тонкости

Есть ещё один момент, который стараются замолчать: на Си можно написать «любой» компилятор. Не на многих «любых» компиляторах можно написать сам компилятор Си.

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

И вообще чего можно, может рассказать только абстрактный «Вася» - и то, в отношении конкретного компилятора

Не эмбеддщик, но открытые ОС на плюсах есть, значит есть база знаний. Целая подгруппа в комитете этим занимается. А с динамической памятью всё довольно ясно и от компилятора не зависит.

это очень сильное преувеличение в отношении системных задач

Навскидку, шаблоны и constexpr много стоят. И вообще язык сильно движется в сторону compile-time возможностей.

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

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

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

Лично мне в крестах не нравятся эти попытки заставить ежика летать. Декларативность шаблонов добавляет сложность, которая на мой взгляд нафиг никому не сдалась и растет как попало. Почему нельзя открыть новое направление, где на нормальных классах описали бы весь язык и ими генерировали саму программу? Чтобы долго не объяснять, вот пример:

template<T> T foo(T x) { return x+1; }

Здесь все ровно, но это простой случай. Что если для комплексных чисел нужно добавлять не 1, а 1+i? Или на основании наличия/имени какого-то поля в типе аргумента решать, что делать дальше? Это не движение в компайл-тайм, а просто макросы на стероидах. Метакод сейчас не может общаться с компилятором на равных, он просто имеет совещательный голос. Как насчет:

template<T> void complex_foo(T x) {
    auto m = std::meta(x);
    typedef void T::bar_proto();

    if (m.methods['bar'] && m.methods['bar'].proto == bar_proto) {
        x.bar();
    }

    x.baz();

    auto filename = std::string("./%s/null-members.txt").format(m.identifier);

    for (auto name : read_names_from_file(filename)) {
        m.set_member(name, nullptr); // x.<name> = nullptr
    }

    // прочие прямолинейные подходы
}

И пусть компилятор сам решает, что можно делать в compile-time, а что нет. Контраргументы типа «ну это слишком даст волю стрелять в ногу» уже слышал.

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

С++ такой весь из себя классный и универсальный

Твоя информация немного устарела. Актуальный С++ уже частично отвязан от рантайма.

И тут начинаются тонкости: этого оказывается нельзя использовать, того тоже нельзя... С runtime-средой непонятки...

Остались только исключения, но через пару лет(возможно, если опять не пожрут раст-говна) завезут статические исключения.

Ну, это очень сильное преувеличение в отношении системных задач.

В контексте актуального С++ - уже нет. На самом деле нет где-то уже с С++17.

Правда мало кто это всё использует, поэтому у рядовых адептов С++ без рантайма действительно днище.

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

Ты просто написал неведанную херню - ты явно из жабаскрипта сбежал.

По поводу сути твои рассуждений - они имеют смысл. Но ты неверно мыслишь. Т.к. ты адепт жабаскрипта - ты не разделяешь рантайм/компилтайм. В этом твоя проблема.

Если же ты начнёшь их разделять, но пойдёшь дальше и поймёшь текущую проблему С++. И ты, как жабаскриптер, её частично понимаешь.

Дело в том, что декларативность в С++ статичная. Т.е. у нас весь компилтайт - статичный. Нельзя ничего изменять, нельзя ничего переопределять, нельзя ничего удалять. Но.

Ведь по-сути это полная херня. Что нам мешает иметь не статичный, а динамический компилтайм. Ведь комплятор - это попросту интерпретатор конструкций, описанных в коде. А сейчас он ещё и интерпретатор кода.

Но зачем нам язык для интерпретатора ограничивать статикой? Незачем. Поэтому на уровне компилтайма С++ можно превратить в жаваскрипт. Где можно как угодно изменять поля, где можно передавать auto везде и всюду. Не делать никакой разницы между типами и значениями. Сейчас мы живём с костылями вида type_c<T>.

Но. Тут возникает фундаментальная проблема из-за которой все эти данамические «языки» такие примитивные. Никто не может понять как это должно выглядеть и никто не может реализовать подобное.

Допустим, как ты будешь реализовывать асист в ide тогда, когда у тебя пропадёт вся эта декларативность? Там асист до сих пор не может в шаблоны.

Поэтому все и идут по базовому пути. Пихаем что придумали, но ни о чём фундаментальном не думаем. Хотя С++ в этом плане ещё адекватен. Всякие там расты - это просто помойка.

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

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

Хейтеры С - они такие! Мозгов нет, вот и выдумывают всякие отмазки, чтобы С не изучать!!!

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.