LINUX.ORG.RU

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

 


13

7

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

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

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

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

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

★★★★★

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

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

strcat/sptrinf это как бы и есть общепринятая либа, стандарт.

молись_чтобы_хватило

Зачем? Можно посчитать, сколько конкретно нужно.

no-such-file ★★★★★
()

как вы склеиваете строки?

size_t sumlen = strlen(a)+strlen(b);
char *concat = malloc(sumlen);
snprintf(concat, sumlen, "%s%s", a, b);

Элементарщина же!

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

strcat/sptrinf это как бы и есть общепринятая либа, стандарт
Зачем? Можно посчитать, сколько конкретно нужно

Может есть какая-нибудь общепринятая либа, которая считает длину, выделяет память и склеивает? Или каждый пишет это вручную?

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

которая считает длину, выделяет память и склеивает?

Нет, т.к. не нужно. А тебе зачем склеивать строки если не секрет. Просто ума не приложу нахрена это понадобилось?

no-such-file ★★★★★
()
Последнее исправление: no-such-file (всего исправлений: 1)
Ответ на: комментарий от Eddy_Em

(да, надо еще таки не забывать проверять результат malloc'а, несмотря на здравомыслие Царя)

Или так:

size_t sumlen = strlen(a)+strlen(b);
a = realloc(sumlen);
strcat(a, b);
В этом случае не нужно будет следить за новым malloc

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

Элементарщина же!

Поздравляю! В трёх строках «элементарщины» ты умудрился:

  • вызвать strlen четыре раза вместо двух;
  • создать строку, не оканчивающуюся нулём;
  • и тем самым подложить мину либо себе, либо другому.
i-rinat ★★★★★
()
Ответ на: комментарий от Eddy_Em

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

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

Может есть какая-нибудь общепринятая либа, которая считает длину, выделяет память и склеивает? Или каждый пишет это вручную?

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

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

либа, которая считает длину, выделяет память и склеивает? Или каждый пишет это вручную?

Тебе три строчки влом написать что ли?

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

вызвать strlen четыре раза вместо двух;

Где это там 4 раза?

создать строку, не оканчивающуюся нулём;

Врешь! Где? sprintf работал и работает!

УМВР.

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

Ну так оформи эту шнягу в отдельный макрос или функцию. Чего как маленький-то?

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

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

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

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

у sprintf емнип адовый оверхед, который при подобной работе со строками совершенно не нужен.

к слову, strlcpy() — потокобезопасна?

wakuwaku ★★★★
()

Если нужно именно что склеить строки — то strlen+memcpy. Оберни в функцию с переменным числом аргументов и всё. Ref: strjoin() в коде systemd.

Если нужно получить аналог asprintf — то snprintf (NULL, 0, ...) вернёт предполагаемый размер.

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

std::string быстрее

Такого бреда я еще не слышал! Ты еще скажи, что код на жабке будет шустрей ассемблерного работать!

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

Где это там 4 раза?

Два раза явно и по одному разу на каждое %s. Надо считать длины, запоминать, а потом печатать с помощью %.*s, указывая длину. И то, не факт, что snprintf не будет проверять каждый символ на \0. memcpy не проверяет, и поэтому сработает быстрее.

Врешь! Где?

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

УМВР.

Это не показатель.

i-rinat ★★★★★
()
Ответ на: комментарий от Eddy_Em

Код на жабке исполняется в VM, а std::string — это строка+длина. Поэтому при нормальной реализации и компиляторе, способном к инлайну, будет быстрее. Правда, только при количестве объединяемых строк, равном двум: иначе будут создаваться временные строки.

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

А если поломается, то починю.

А если из-за твоей ошибки у телескопа зеркало отвалится?

А если из-за его ошибки на нас метеорит упадёт?

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

у sprintf емнип адовый оверхед

Согласен. В микроконтроллерах printf и ей подобные используются только идиотами либо буржуями (у которых мегабайт-другой флеша в МК).

В данном случае вполне возможно, что шустрей всех будет memmove работать или memcpy.

strlcpy() — потокобезопасна?

Нет, конечно. Если кто-то начал менять буфер во время хоть strcpy, хоть memcpy, на выходе получишь черт-те что. Это ясно.

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

Спасибо Эдик

fail.c

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(){
  char* a = "Hello ";
  char* b = "World";
  size_t sumlen = strlen(a)+strlen(b);
  char *concat = malloc(sumlen);
  snprintf(concat, sumlen, "%s%s", a, b); 
  printf("%s\n", concat); 
  return 0; 
}
$ gcc fail.c -o fail && ./fail
Hello Worl
vertexua ★★★★★
()
    const char string1[] = "string1";
    const char string2[] = "string2";

    char combined[sizeof string1 + sizeof string2 - 1];
    memcpy (combined, string1, sizeof string1 - 1);
    memcpy (combined+sizeof string1-1, string2, sizeof string2);
waker ★★★★★
()
Ответ на: комментарий от Eddy_Em

ну у меня тут написано «The strcpy() and strncpy() functions are thread-safe», то же касается и memcpy, а вот про бсдшные варианты ничего нигде не сказано.

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

Если тебе часто нужно вычислять strlen, то запросто можно такое сделать:

typedef struct{
  char *str;
  size_t len;
} string;

и написать свои функции работы с ними:

string *allocstr(size_t L){
  string *S = malloc(sizeof(string));
  if(!S) return NULL;
  S->str = calloc(1, L + 1);
  if(!S->str){
    free(S);
    return NULL;
  }
  S-> len = L;
  return S;
}

void freestr(string *S){
  free((*S)->str);
  free(*S);
  *S = NULL;
}

string *concatstr(string A, string B){
  size_t newL = A->len + B->len + 1;
  if(! realloc(A->str, newL)) return NULL;
  memcpy(A->str + A->len, B->str, B->len);
  A->len = newL;
  return A;
}

и т.д., и т.п.

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

В очень быстром коде нужно учитывать что char* фиксированый длины будет лежать в стеке, а в std::string нужно включить аллокатор специальный. Конечно в глупости «С быстрее С++» только нуб вроде Эдика будет верить, но есть реальные вещи, которые просто нужно помнить и знать как обходить. В С++ увы нужно больше понимать о внутренностях STL чтобы знать откуда ждать горя

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

это вполне рабочий вариант. использую постоянно. если строки не константные — sizeof легко и непринужденно заменяется на strlen.

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

Я прекрасно это понимаю. Моё сообщение было к тому, что плюсовый std::string транслируется как раз во что-то подобное, и поэтому сравнение с жабой неуместно.

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

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

makoven ★★★★★
() автор топика

А я вот так велосипедил:

char * str_concat (const char *fmt, ...)
{

    va_list items;
    size_t  length;

    va_start (items, fmt);
    length = vsnprintf (0, 0, fmt, items) * sizeof(char) + 1;
    va_end (items);

    char *out = (char *) malloc (length);
    va_start (items, fmt);
    vsnprintf (out, length, fmt, items);
    va_end (items);

    return out;

}

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

гораздо более эффективно передавать в функцию указатель на выходной буфер, его размер, и размеры входных строк. если понатыкать malloc/free везде — твоя прога на C будет работать со скоростью жабы.

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

ТСу остается к моему коду еще десяток функций приплюсовать и получится свой велосипед =D

(еще resize нужны и всякие другие вещи + в структуру надо бы добавить реальную длину содержимого, ну или что там по желанию)

Кстати, если еще в объявлении структуры добавить правильно выравнивание, то можно будет тупо писать

string *S = allocstr(X);
// заполняем S, а затем
printf("The string S is %s\n", (char*)S);

// можно макрос оформить, чтобы (char*) не писать

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

string *S = malloc(sizeof(string));

А тут ты выделил 32 байта, из которых используешь только 16. И кучу фрагментируешь почём зря. Да и вообще, скоро ты так героически реализуешь часть GLib, только без тестов.

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

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

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

Велосипед хорош. Когда немножко поосвоюсь с языком - обязательно напишу с десяток велосипедов. Пока рано)

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

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

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