Как объединить константные / литеральные строки в C? - PullRequest
299 голосов
/ 21 ноября 2008

Я работаю в C, и мне нужно объединить несколько вещей.

Прямо сейчас у меня есть это:

message = strcat("TEXT ", var);

message2 = strcat(strcat("TEXT ", foo), strcat(" TEXT ", bar));

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

Ответы [ 17 ]

328 голосов
/ 21 ноября 2008

В C "строки" - это просто char массивы. Следовательно, вы не можете напрямую объединять их с другими «строками».

Вы можете использовать функцию strcat, которая добавляет строку, на которую указывает src, к концу строки, на которую указывает dest:

char *strcat(char *dest, const char *src);

Вот пример от cplusplus.com :

char str[80];
strcpy(str, "these ");
strcat(str, "strings ");
strcat(str, "are ");
strcat(str, "concatenated.");

Для первого параметра вам необходимо указать сам буфер назначения. Буфер назначения должен быть буфером массива символов. Например: char buffer[1024];

Убедитесь, , что у первого параметра достаточно места для хранения того, что вы пытаетесь скопировать в него. Если вам доступно, безопаснее использовать такие функции, как: strcpy_s и strcat_s, где вам явно необходимо указать размер буфера назначения.

Примечание : строковый литерал нельзя использовать в качестве буфера, поскольку он является константой. Таким образом, вы всегда должны выделять массив символов для буфера.

Возвращаемое значение strcat может быть просто проигнорировано, оно просто возвращает тот же указатель, который был передан в качестве первого аргумента. Это сделано для удобства и позволяет вам объединять вызовы в одну строку кода:

strcat(strcat(str, foo), bar);

Таким образом, ваша проблема может быть решена следующим образом:

char *foo = "foo";
char *bar = "bar";
char str[80];
strcpy(str, "TEXT ");
strcat(str, foo);
strcat(str, bar);
227 голосов
/ 21 ноября 2008

Избегайте использования strcat в C-коде. Самый чистый и, самое главное, самый безопасный способ - это использовать snprintf:

char buf[256];
snprintf(buf, sizeof buf, "%s%s%s%s", str1, str2, str3, str4);

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

23 голосов
/ 21 ноября 2008

Народ, используйте str n cpy (), str n cat () или s n printf ().
Превышение вашего буферного пространства приведет к тому, что все остальное будет в памяти!
(И не забудьте оставить пробел для завершающего нулевого символа '\ 0'!)

16 голосов
/ 21 ноября 2008

Также malloc и realloc полезны, если вы заранее не знаете, сколько строк объединяются.

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

void example(const char *header, const char **words, size_t num_words)
{
    size_t message_len = strlen(header) + 1; /* + 1 for terminating NULL */
    char *message = (char*) malloc(message_len);
    strncat(message, header, message_len);

    for(int i = 0; i < num_words; ++i)
    {
       message_len += 1 + strlen(words[i]); /* 1 + for separator ';' */
       message = (char*) realloc(message, message_len);
       strncat(strncat(message, ";", message_len), words[i], message_len);
    }

    puts(message);

    free(message);
}
14 голосов
/ 19 сентября 2015

Строки также могут быть объединены во время компиляции.

#define SCHEMA "test"
#define TABLE  "data"

const char *table = SCHEMA "." TABLE ; // note no + or . or anything
const char *qry =               // include comments in a string
    " SELECT * "                // get all fields
    " FROM " SCHEMA "." TABLE   /* the table */
    " WHERE x = 1 "             /* the filter */ 
                ;
5 голосов
/ 21 ноября 2008

Не забудьте инициализировать выходной буфер. Первый аргумент strcat должен быть строкой с нулевым символом в конце и достаточным дополнительным пространством, выделенным для результирующей строки:

char out[1024] = ""; // must be initialized
strcat( out, null_terminated_string ); 
// null_terminated_string has less than 1023 chars
4 голосов
/ 08 апреля 2013

Лучший способ сделать это без ограничения размера буфера - использовать asprintf ()

char* concat(const char* str1, const char* str2)
{
    char* result;
    asprintf(&result, "%s%s", str1, str2);
    return result;
}
4 голосов
/ 08 декабря 2011

Как отмечали люди, обработка строк значительно улучшилась. Поэтому вы можете узнать, как использовать библиотеку строк C ++ вместо строк в стиле C. Однако вот решение в чистом виде C

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

void appendToHello(const char *s) {
    const char *const hello = "hello ";

    const size_t sLength     = strlen(s);
    const size_t helloLength = strlen(hello);
    const size_t totalLength = sLength + helloLength;

    char *const strBuf = malloc(totalLength + 1);
    if (strBuf == NULL) {
        fprintf(stderr, "malloc failed\n");
        exit(EXIT_FAILURE);
    }

    strcpy(strBuf, hello);
    strcpy(strBuf + helloLength, s);

    puts(strBuf);

    free(strBuf);

}

int main (void) {
    appendToHello("blah blah");
    return 0;
}

Я не уверен, правильно ли это / безопасно, но сейчас я не смог найти лучшего способа сделать это в ANSI C.

4 голосов
/ 21 ноября 2008

Первый аргумент strcat () должен содержать достаточно места для объединенной строки. Поэтому выделите буфер с достаточным пространством для получения результата.

char bigEnough[64] = "";

strcat(bigEnough, "TEXT");
strcat(bigEnough, foo);

/* and so on */

strcat () объединит второй аргумент с первым аргументом и сохранит результат в первом аргументе. Возвращенный символ * является просто первым аргументом и только для вашего удобства.

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

3 голосов
/ 21 ноября 2008

Неопределенное поведение пытаться изменить строковые литералы, что-то вроде:

strcat ("Hello, ", name);

попытается сделать. Он попытается привязать строку name к концу строкового литерала "Hello, ", который не является четко определенным.

Попробуйте что-нибудь такое. Он достигает того, что вы пытаетесь сделать:

char message[1000];
strcpy (message, "TEXT ");
strcat (message, var);

Это создает буферную область, которую можно разрешить изменять, а затем копирует в нее как строковый литерал, так и другой текст. Просто будьте осторожны с переполнением буфера. Если вы управляете входными данными (или проверяете их заранее), можно использовать буферы фиксированной длины, как у меня.

В противном случае вам следует использовать стратегии смягчения, такие как выделение достаточного количества памяти из кучи, чтобы обеспечить ее обработку. Другими словами, что-то вроде:

const static char TEXT[] = "TEXT ";

// Make *sure* you have enough space.

char *message = malloc (sizeof(TEXT) + strlen(var) + 1);
if (message == NULL)
     handleOutOfMemoryIntelligently();
strcpy (message, TEXT);
strcat (message, var);

// Need to free message at some point after you're done with it.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...