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

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

11 голосов
/ 07 января 2009

Хорошо, у меня есть следующий код, который добавляет строку к другой в C #, обратите внимание, что это просто пример , поэтому предоставление альтернативных методов конкатенации строк в 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;

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

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 и просто сделать это так же, как в C #?

Ответы [ 7 ]

19 голосов
/ 07 января 2009

Изменение

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

до

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

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

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

Редактировать: Согласно запросу из комментария Филипа, простая реализация strcat, основанная на буфере символа фиксированного размера:

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);
}

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

Приложение (Pax):

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

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

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

Конечное приложение (Pax):

8 голосов
/ 07 января 2009

Интересно, почему никто еще не упомянул 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() буфер, когда закончите с его использованием!

6 голосов
/ 07 января 2009

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

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

2 голосов
/ 07 января 2009

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

1 голос
/ 08 января 2009

Не было упоминания о функции 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);

Будет выводить "герро !!! бла"

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

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

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

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

1 голос
/ 07 января 2009

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

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

0 голосов
/ 07 января 2009

Безопасный способ сделать это в классическом стиле 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 (с нулевым символом в конце) будет делать (за исключением того, что большинство этих языков будет иметь встроенную сборку мусора).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...