LINUX.ORG.RU

Шок от С. Как склеивать строки?

 


13

7

Осваиваю си. Всё шло хорошо пока внезапно не понадобилось склеить строки (константные и переменные). Покурил stackoverflow. Предлагают 2 варианта:

Первый - создать char buf[молись_чтобы_хватило] и делать str(n)cat/sprintf в этот buf.

Второй - использовать asprintf, который расширение, нестандарт и вообще.

Вопрос: как вы склеиваете строки? Может есть какая-нибудь общепринятая либа?

Простите за нубский вопрос

★★★★★

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

В «нормальных языках» по соплям бьют за «+» и разводят всякие там StringBuffer.

В нормальных языках компилятор заменяет + на, если я правильно понял, какой именно язык ты имеешь в виду, StringBuilder. Код при этом остается максимально легко читаемым.

f1xmAn ★★★★★
()

Осваиваю си.

Переходи на C++. Может даже с него стоит начать. Умеет все, что умеет Си. Содержит много дополнительных плюшек. Может быть быстрее Си. Имеет стандартную библиотеку. В которой есть, например, класс для строк.

как вы склеиваете строки? Может есть какая-нибудь общепринятая либа?

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

Классические же сишные строки даже размер свой не знают(просто оканчиваются на '\0', есть стандартная функция strlen с линейной сложностью(!). Потому многие проекты играются со своими структурками, где помимо строки хранят размер. Но, ИМХО, такие проекты просто следовало писать на C++ или даже на чем-то более высокоуровневом.

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

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

Где же это + есть а аналога StringBuilder нет?

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

Я и сам несколько удивлен) Словно у всех накипело с сишными строками.

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

PS: Еще нашел конкатенацию в Apache Portable Runtime: https://apr.apache.org/docs/apr/1.5/group__apr__strings.html#ga7bd80c95ffb7b3...

Прям идеальное API: сама выделяет память и принимает любое количество строк. Но судя по зависимостям от нее пакетов в арчике, популярность APR крайне мала. По сравнению с glib

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

У memcpy есть недостаток. Она неправильно отработает если области, которые ты ей подсунул, перекрываются между собой.

А разве malloc() может выдать кем-то перекрытую область?

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

ибо как «доказанная теорема уальсом эндрю» формулировка доступна любому , а чистая реализация :(

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

А malloc здесь причем? Я писал про весьма конкретную функцию.

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

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

Единственных 2 вызова memcpy:

memcpy(c, a, lena);
memcpy(c + lena, b, lenb + 1);

Где c выделялся malloc'ом, а значит ни с кем не перекрывается, а значит вызовов memcpy() для перекрытых областей нет.

Я это имел ввиду.

А перекрытие входящих строк не проблема. Если a - указатель на середину b. (Ноль в конце у обоих общий).

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

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

ты так говоришь, как будто-бы я сложить строки в JavaScript'е или в php не могу. Поверь — могу, это любой дебил может. Т.ч. тебе нечем гордиться.

emulek
()

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

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

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

код должен быть прежде всего ЧИТАЕМЫЙ ЧЕЛОВЕКОМ. Это выше экономии, особенно такой копеечной. Если ты не согласен, пиши на асме.

Ну и потом, конкатенация строк нужна обычно для:

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

2. отладка. См. п1

3. каких-то операций с именами файлов, см п1.

И главное: мы не видели код ТСа целиком, вполне возможно, что там у него лишней работы в strcat и нет, и очень вероятно, что в другом коде лишней работы в Over9000 раз больше.

Преждевременная оптимизация есть зло.

Надеюсь, ты не начнёшь сейчас кукарекать про «что плохого в лишнем strlen»?

говнокодер — это тот, кто экономит на спичках в угоду трём тактам.

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

Для меня загадка, почему для плюсов есть весьма навороченный stl, а помимо него ещё есть просто overkill в виде boost'a и Qt, а для C толком ничего и нет.

ВНЕЗАПНО: есть php написанный на сишке. Сишкак как раз и нужна, если задача _нестандартная_. А пейсать домашнюю страничку на сях можно либо jff, либо еси ты Эдик.

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

код должен быть прежде всего ЧИТАЕМЫЙ ЧЕЛОВЕКОМ. Это выше экономии, особенно такой копеечной. Если ты не согласен, пиши на асме.

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

говнокодер — это тот, кто экономит на спичках в угоду трём тактам.

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

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

printf достаточно удобный инструмент для логов.

вот у тебя есть структура, которая as_is не выводится, её надо как-то обработать и передать на вывод (в лог). Как передавать будешь? printf(«моя структура: %s»…) и функцией, которая строку возвращает? А кто эту строку будет потом освобождать?

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

Сишкак как раз и нужна, если задача _нестандартная_

Задача самая типичная и решается чем угодно. Где С и С++ надо - это где требуется тонкая оптимизация, да и вообще максимальное быстродействие, плюс реальная переносимость на разные платформы.

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

И ты действительно считаешь, что это говно быстрее std::string?

смотря что считать при сравнении. если строки константные, и использовать sizeof, то будет очень существенно быстрее. если считать strlen каждый раз — то вполне может быть и медленнее, даже несмотря на гору оверхеда от std::string. также, зависит от входных данных.

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

Так речь про strcat была.

ну уж если у тебя длинная всё равно есть, то замени ты

strcpy(str3+len1, str2);
если тебя так уж волнует спичечная экономия в угоду удобочитаемости.

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

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

Если строки константные - можно вообще копипастой их объединить. Максимальное быстродействие и минимум кода.

то вполне может быть и медленнее, даже несмотря на гору оверхеда от std::string

Нет там горы оверхеда:

template<class _CharT, class _Traits, class _Allocator>
basic_string<_CharT, _Traits, _Allocator>
    operator+(const basic_string<_CharT, _Traits, _Allocator>& __lhs,
const basic_string<_CharT, _Traits, _Allocator>& __rhs)
{
    basic_string<_CharT, _Traits, _Allocator> __r(__lhs.get_allocator());
    typename basic_string<_CharT, _Traits, _Allocator>::size_type __lhs_sz = __lhs.size();
    typename basic_string<_CharT, _Traits, _Allocator>::size_type __rhs_sz = __rhs.size();
    __r.__init(__lhs.data(), __lhs_sz, __lhs_sz + __rhs_sz);
    __r.append(__rhs.data(), __rhs_sz);
    return __r;
}

Да выглядит страшно, но реально превратится в аллокацию памяти размером __lhs_sz + __rhs_sz и два копирования. Т.е. в то, что надо. Оверхед есть, но весьма скромный. Это libc++, если что, у макак из gcc как всегда руки из жопы и менее эффективный код.

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

Задача самая типичная и решается чем угодно.

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

да и вообще максимальное быстродействие, плюс реальная переносимость на разные платформы.

вот именно потому я предложил самое простое, со стандартной strcat(3). Т.к. дополнительных условий я тоже не знаю.

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

примитивным подходом:

printf("My struct %p state: ", &instance_of_my_struct);
print_mystruct(&instance_of_my_struct);
puts("");

Ну или если прямо сильно нужно единой строкой

char* p = serialize_mystruct(&myinstance);
printf("My struct %p state: %s\n", &myinstance, p);
free(p);

ihanick
()

Первый - создать char buf[молись_чтобы_хватило] и делать str(n)cat/sprintf в этот buf.

Допустим есть 2 строки a и b.

Сделать char buf[n+m+1] где n - длина строки a, а m - длина строки b - не вариант?

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

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

А закодить один раз это в процедуру никак-никак нельзя? :-)
Или пользоваться готовыми, благо библиотек для работы со строками полно

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

Какой цикл нужно написать, чтобы не заменяло?

String str = "str";
for (int i = 0; i < 10; i++) {
    str = str + i;
}
0: ldc           #2                  // String str
2: astore_1
3: iconst_0
4: istore_2
5: iload_2
6: bipush        10
8: if_icmpge     36
11: new           #3                  // class java/lang/StringBuilder
14: dup
15: invokespecial #4                  // Method java/lang/StringBuilder.«<init>»:()V
18: aload_1
19: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
22: iload_2
23: invokevirtual #6                  // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
26: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
29: astore_1
30: iinc          2, 1
33: goto          5
36: return

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

нубовопрос

Кстати, как принято поступать после маллока и sz+1 для строк (возможно, изврат), это нормальная практика тут же после выделения памяти присваивать нуль последнему элементу массива (который, если объёма выделенной памяти хватит, никогда не должен будет использоваться)? Получается слишком много строк и неаккуратно, хотя и выносится в макрос. Повторюсь, я говорю про строки.

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

wakuwaku ★★★★
()
Ответ на: нубовопрос от wakuwaku

это нормальная практика тут же после выделения памяти присваивать нуль последнему элементу массива

либо зануляй весь массив(что излишне), либо не делай это при выделении памяти, но ОБЯЗАТЕЛЬНО делай при склеивании строк, например, штобы не начать потом printf-ом читать рандомный буллшит

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

Переходи на C++.

ох, спорно это

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

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

я говорил не столько об оверхед от operator +, сколько об оверхеде от std::string в целом. ты забыл про аллокации. тебе все строки сначала придется загнать в объекты, которые выделят память в хипе, и продублируют туда исходные данные. + аллокация для output string.

p.s.

std::string out = std::string("string1") + std::string("string2");

посмотри в дизассемблер вот такой программы. подумай.

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

У меня зануляется обычно элемент, следующий за последним байтом получившейся строки, а маллоки делаются в соответствии с limits.h (i.e. PATH_MAX (внезано, это including the terminating null character? совсем запамятовал), NAME_MAX+1 etc.), посему проблема кажется несколько надуманной, просто хотелось уточнить, так как пару раз накорябал что-то такое в прошлом, возможно, от скуки.

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

уже и за StringBuffer бьют) нынче модно StringBuilder, гы

anonymous
()
memcpy(c, a, lena);
memcpy(c + lena, b, lenb + 1);

lena

приятно наблюдать тян в треде!

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

ты забыл про аллокации. тебе все строки сначала придется загнать в объекты, которые выделят память в хипе, и продублируют туда исходные данные. + аллокация для output string

Как я понимаю, ты опять про константные строки - один static const std::string и нет постоянных аллокаций. Насчет «аллокация для output string» - тут целых два преимущества:

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

посмотри в дизассемблер вот такой программы. подумай.

Я давно не занимаюсь такой дуростью, реальную скорость и узкие места показывает профайлер.

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

регистрация температур

второстепенной хрени.

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

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

Кстати, почему в линукс (glibc) всё ещё не портировали семейство безопасных strl* функций?

char *
cat(char *a, char *b)
{
        char buf[128];
        strlcpy(buf, a, sizeof(buf));
        strlcat(buf, b, sizeof(buf));
        return strdup(buf);
}

Тоже самое можно конечно и на strn* сделать, но будет это гораздо громоздкей и с ошибками.

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

ты не обязан объяснять мне, чем отличается std::string от char *. я прекрасно осведомлен, и постоянно использую и то, и другое.

тут целых два преимущества:

мы вроде о скорости говорили, а не о философии.

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

printf достаточно удобный инструмент для логов.

Великолепный инструмент, кто ж спорит. Если бы была версия sprintf, выделяющая место в буффере куда он пишет, данной темы может быть и не было бы.

или использовать библиотеки.

подскажи аналог boost'a ;)

Самый идиотизм, что все эти свои выводы ты делаешь даже не открыв n1570 page 382

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

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

Вот, кстати, ИК-спектрометр делаю. Авось, к лету "первый свет" увидит. И нужно будет писать гору документации + методички.

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от Pinkbyte

зануляй весь массив(что излишне)

Не так уж и излишне, тем более, что поскольку calloc != malloc + memset, оверхеда там может и не быть.

post-factum ★★★★★
()
Ответ на: комментарий от waker

ты не обязан объяснять мне, чем отличается std::string от char *. я прекрасно осведомлен, и постоянно использую и то, и другое.

но пришлось

мы вроде о скорости говорили, а не о философии.

а это не философия, это реальность, строки надо не только генерировать, но и отдавать, хранить и передавать, да-да, ты прекрасно осведомлен, просто напоминаю

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

Но везде есть удобные инструменты для работы со строками (и не только).

и все несовместимы друг с другом :)

Кроме того в отличии от плюсов, в Си что-то с такими библиотек на все случаи жизни, при том настолько же портабельных, не очень хорошо. Их толком нету.

потому что есть libc, которого вполне достаточно. и это хорошо.

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