Вопрос

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

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

message = strcat("TEXT ", var);

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

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

Это было полезно?

Решение

В C " строки " это просто char массивы. Поэтому вы не можете напрямую объединить их с другими & Quot; строками & Quot ;.

Вы можете использовать функцию 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(str, foo), bar);

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

char *foo = "foo";
char *bar = "bar";
char str[80];
strcpy(str, "TEXT ");
strcat(str, foo);
strcat(str, bar);

Другие советы

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

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

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

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

Кроме того, 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);
}

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

#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 */ 
                ;

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

char out[1024] = ""; // must be initialized
strcat( out, null_terminated_string ); 
// null_terminated_string has less than 1023 chars

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

char bigEnough[64] = "";

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

/* and so on */

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

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

Как отмечали люди, обработка строк значительно улучшилась. Поэтому вы можете узнать, как использовать библиотеку строк 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.

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

char* concat(const char* str1, const char* str2)
{
    char* result;
    asprintf(&result, "%s%s", str1, str2);
    return result;
}

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

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.

Вы можете написать свою собственную функцию, которая делает то же самое, что и strcat(), но это ничего не меняет:

#define MAX_STRING_LENGTH 1000
char *strcat_const(const char *str1,const char *str2){
    static char buffer[MAX_STRING_LENGTH];
    strncpy(buffer,str1,MAX_STRING_LENGTH);
    if(strlen(str1) < MAX_STRING_LENGTH){
        strncat(buffer,str2,MAX_STRING_LENGTH - strlen(buffer));
    }
    buffer[MAX_STRING_LENGTH - 1] = '\0';
    return buffer;
}

int main(int argc,char *argv[]){
    printf("%s",strcat_const("Hello ","world"));    //Prints "Hello world"
    return 0;
}

Если длина обеих строк превышает 1000 символов, строка обрезается до 1000 символов. Вы можете изменить значение MAX_STRING_LENGTH в соответствии со своими потребностями.

Предполагая, что у вас есть символ [fixed_size], а не символ *, вы можете использовать один креативный макрос, чтобы сделать все это сразу с помощью <<cout<<like упорядочивание ("скорее %s несвязанный %s ", "чем", "формат в стиле printf").Если вы работаете со встроенными системами, этот метод также позволит вам исключить malloc и большие *printf семейство функций, таких как snprintf() (Это также удерживает dietlibc от жалоб на *printf)

#include <unistd.h> //for the write example
//note: you should check if offset==sizeof(buf) after use
#define strcpyALL(buf, offset, ...) do{ \
    char *bp=(char*)(buf+offset); /*so we can add to the end of a string*/ \
    const char *s, \
    *a[] = { __VA_ARGS__,NULL}, \
    **ss=a; \
    while((s=*ss++)) \
         while((*s)&&(++offset<(int)sizeof(buf))) \
            *bp++=*s++; \
    if (offset!=sizeof(buf))*bp=0; \
}while(0)

char buf[256];
int len=0;

strcpyALL(buf,len,
    "The config file is in:\n\t",getenv("HOME"),"/.config/",argv[0],"/config.rc\n"
);
if (len<sizeof(buf))
    write(1,buf,len); //outputs our message to stdout
else
    write(2,"error\n",6);

//but we can keep adding on because we kept track of the length
//this allows printf-like buffering to minimize number of syscalls to write
//set len back to 0 if you don't want this behavior
strcpyALL(buf,len,"Thanks for using ",argv[0],"!\n");
if (len<sizeof(buf))
    write(1,buf,len); //outputs both messages
else
    write(2,"error\n",6);
  • Примечание 1, обычно вы бы не использовали argv [0] подобным образом - просто пример
  • Примечание 2, вы можете использовать любую функцию, которая выводит символ *, включая нестандартные функции, такие как itoa(), для преобразования целых чисел в строковые типы.
  • Примечание 3. если вы уже используете printf где-либо в своей программе, нет причин не использовать snprintf(), поскольку скомпилированный код будет больше (но встроенным и значительно быстрее)
int main()
{
    char input[100];
    gets(input);

    char str[101];
    strcpy(str, " ");
    strcat(str, input);

    char *p = str;

    while(*p) {
       if(*p == ' ' && isalpha(*(p+1)) != 0)
           printf("%c",*(p+1));
       p++;
    }

    return 0;
}

Вы пытаетесь скопировать строку в адрес, который выделен статически.Вам нужно поместить cat в буфер.

В частности:

...снип...

пункт назначения

Pointer to the destination array, which should contain a C string, and be large enough to contain the concatenated resulting string.

...снип...

http://www.cplusplus.com/reference/clibrary/cstring/strcat.html

Здесь тоже есть пример.

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

Теперь это довольно неудобно, так как вам нужно найти последний символ, чтобы добавить что-то. strcat сделаю это за вас.

Таким образом, strcat ищет в первом аргументе нулевой символ. Затем он заменит это на содержимое второго аргумента (до тех пор, пока оно не закончится нулевым значением).

Теперь давайте пройдемся по вашему коду:

message = strcat("TEXT " + var);

Здесь вы добавляете что-то к указателю на текст " TEXT " (тип " TEXT " является const char *. Указатель.).

Обычно это не работает. Также изменяет & Quot; TEXT & Quot; массив не будет работать, так как он обычно размещается в постоянном сегменте.

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

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

Я бы предложил сделать что-то вроде этого:

sprintf(message2, "TEXT %s TEXT %s", foo, bar);

Прочтите документацию sprintf, чтобы проверить ее параметры.

А теперь важный момент:

Убедитесь, что в буфере достаточно места для текста и нулевого символа. Есть пара функций, которые могут вам помочь, например, strncat и специальные версии printf, которые выделяют вам буфер. Несоблюдение размера буфера приведет к повреждению памяти и удаленному использованию ошибок.

Это было мое решение

#include <stdlib.h>
#include <stdarg.h>

char *strconcat(int num_args, ...) {
    int strsize = 0;
    va_list ap;
    va_start(ap, num_args);
    for (int i = 0; i < num_args; i++) 
        strsize += strlen(va_arg(ap, char*));

    char *res = malloc(strsize+1);
    strsize = 0;
    va_start(ap, num_args);
    for (int i = 0; i < num_args; i++) {
        char *s = va_arg(ap, char*);
        strcpy(res+strsize, s);
        strsize += strlen(s);
    }
    va_end(ap);
    res[strsize] = '\0';

    return res;
}

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

char *str = strconcat(3, "testing ", "this ", "thing");

Попробуйте что-нибудь похожее на это:

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

int main(int argc, const char * argv[])
{
  // Insert code here...
  char firstname[100], secondname[100];
  printf("Enter First Name: ");
  fgets(firstname, 100, stdin);
  printf("Enter Second Name: ");
  fgets(secondname,100,stdin);
  firstname[strlen(firstname)-1]= '\0';
  printf("fullname is %s %s", firstname, secondname);

  return 0;
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top