Использование strcat в C

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

string Data = "";
Data +="\n\nHTTP/1.1 " + Status_code;
Data += "\nContent-Type: " + Content_Type;
Data += "\nServer: PT06";
Data += "\nContent-Length: " + Content_Lengt;
Data += "\nDate: " + Date;
Data += "\n" + HTML;

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

time_t rawtime;

time ( &rawtime );

char *message = "\n\nHTTP/1.1 ";
message = strcat(message, Status_code);
message = strcat(message, "\nContent-Type: ");
message = strcat(message, Content_Type);
message = strcat(message, "\nServer: PT06");
message = strcat(message, "\nContent-Length: ");
message = strcat(message, Content_Lengt);
message = strcat(message,  "\nDate: ");
message = strcat(message, ctime(&rawtime));
message = strcat(message, "\n");
message = strcat(message, HTML);

Теперь это дает мне ошибку Сегмента, я знаю, почему, я получаю доступ и читаю в памяти, что я не должен. Но вопрос в том, как я его решаю? Могу ли я использовать string.h и просто сделать это так же, как в С#?

Ответ 1

Изменить

char *message = "\n\nHTTP/1.1 ";

к

char message[1024];  
strcpy(message,"\n\nHTTP/1.1 ");

и вы должны быть в порядке, до общей длины сообщения 1023.

Изменить: (согласно комментарию mjy). Использование strcat таким способом - отличный способ получить переполнение буфера. Вы можете легко написать небольшую функцию, которая проверяет размер буфера и длину входящего добавления строки, чтобы преодолеть это, или использовать realloc в динамическом буфере. IMO, бремя на программиста, чтобы проверить правильные размеры буфера, где они используются, например, с помощью sprintfs и других функций C строк. Я предполагаю, что C используется по С++ по соображениям производительности, и, следовательно, STL не является вариантом.

Изменить: согласно запросу от комментария Filip простая реализация strcat на основе буфера с фиксированным размером char:

char buffer[MAXSIZE] = "";

int mystrcat(char *addition)
{
   if (strlen(buffer) + strlen(addition) + sizeof(char)  >= MaxSize)
     return(FAILED);
   strcat(buffer,addition);
   return(OK);
}

Использование динамического распределения:

char *buffer = NULL;

int mystrcat(char *addition)
{
   buffer = realloc(buffer, strlen(buffer) + strlen(addition) + sizeof(char));
   if (!buffer)
     return(FAIL);
   strcat(buffer, addition);
   return(OK);
}

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

Добавление (Pax):

Хорошо, поскольку на самом деле вы не объяснили, почему вам нужно было создать message[1024], вот оно.

С char * x = "hello" фактические байты ('h', 'e', ​​'l', 'l', 'o', 0) (нуль на конце) сохраняются в область памяти, отдельную от переменных (и, вполне возможно, только для чтения), и переменная x устанавливается на нее. После нулевого, возможно, что-то еще очень важно. Таким образом, вы не можете добавить к этому вообще.

С char x[1024]; strcpy(x,"hello"); вы сначала выделяете 1K om память, которая полностью посвящена x. Затем вы копируете "привет" в него и все еще оставляете немного места в конце для добавления большего количества строк. У вас не будет проблем, пока вы не добавите больше, чем разрешено 1K.

Конечное добавление (Pax):

Ответ 2

Интересно, почему никто не упомянул snprintf() от stdio.h. То, что C способ выводить несколько значений, и вам даже не придется предварительно конвертировать ваши примитивы в строки.

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

char buffer[1024];
int len = snprintf(buffer, sizeof(buffer), "%s %i", "a string", 5);
if(len < 0 || len >= sizeof(buffer))
{
    // buffer too small or error
}

Изменить: Вы также можете использовать функцию asprintf(). Это широко распространенное расширение GNU и часть TR 24731-2 (что означает, что это может привести к следующему стандарту C). Пример сверху читал

char * buffer;
if(asprintf(&buffer, "%s %i", "a string", 5) < 0)
{
    // (allocation?) error
}

Помните free() буфер, когда он будет выполнен с его помощью!

Ответ 3

Начните с использования более безопасной функции strncat. В общем, всегда используйте более безопасные "n" функции, которые не будут переполняться, если размер строки больше определенного размера.

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

Ответ 4

указывает на char const [], с которым вы не можете писать, но точно, где именно пишет strcat. Вам нужно malloc() достаточно большой буфер.

Ответ 5

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

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

Ответ 6

Не видел упоминания функции strlcpy, strlcat, которая похожа на функции "n", кроме того, учитывает конечный 0. Оба имеют третий аргумент, указывающий максимальную длину выходного буфера и находятся в string.h.

Пример:

char blah[]="blah";
char buffer[1024];
strlcpy(buffer,"herro!!!",sizeof(buffer));
strlcat(buffer,blah,sizeof(buffer));
printf("%s\n",buffer);

Выведет "herro!!! blah"

char blah[]="blah";
char buffer[10];
strlcpy(buffer,"herro!!!",sizeof(buffer));
strlcat(buffer,blah,sizeof(buffer));
printf("%s\n",buffer);

выводит "herro!!! b" из-за ограниченного размера буфера [], без segfault. ^^

Только проблема заключается не в том, что все платформы, как представляется, включают ее в свой libc (например, linux._.), большинство из всех BSD-переменных DO, похоже, имеют это.

В этом случае код для обеих функций можно найти здесь и легко добавить: strlcpy, strlcat, остальная часть string.h

Ответ 7

Безопасный способ сделать это в классическом стиле C:

 char *strconcat(char *s1, char *s2)
 {
    size_t old_size;
    char *t;

    old_size = strlen(s1);

    /* cannot use realloc() on initial const char* */
    t = malloc(old_size + strlen(s2) + 1);
    strcpy(t, s1);
    strcpy(t + old_size, s2);
    return t;
  }

  ...

  char *message = "\n\nHTTP/1.1 ";
  message = strconcat (message, Status_code);
  message = strconcat (message, "\nContent-Type: ");

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