Jak połączyć const / dosłowne ciągi w C?

Pracuję w C i muszę konkatenować kilka rzeczy.

Teraz mam to:

message = strcat("TEXT ", var);

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

Teraz, jeśli masz doświadczenie w C jestem pewien, że zdajesz sobie sprawę, że to daje błąd segmentacji, gdy próbujesz go uruchomić. Jak to obejść?

Author: Peter Mortensen, 2008-11-21

17 answers

W C, "ciągi" są po prostu zwykłymi tablicami char. Dlatego nie można bezpośrednio łączyć ich z innymi "ciągami".

Możesz użyć funkcji strcat, która dołącza łańcuch wskazywany przez src do końca łańcucha wskazywanego przez dest:

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

Oto przykład z cplusplus.com:

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

Dla pierwszego parametru należy podać sam bufor docelowy. Bufor docelowy musi być buforem tablicy znaków. Np.: char buffer[1024];

Upewnij się, że pierwszy parametr ma wystarczająco dużo miejsca, aby przechowywać to, co próbujesz skopiować. Jeśli dostępne dla Ciebie, bezpieczniej jest używać funkcji takich jak: strcpy_s i strcat_s, gdzie jawnie musisz określić rozmiar bufora docelowego.

Notatka: literał Łańcuchowy nie może być używany jako bufor, ponieważ jest stałą. Dlatego zawsze musisz przydzielić tablicę znaków dla bufora.

Wartość zwracana strcat może być po prostu ignorowany, zwraca tylko ten sam wskaźnik, który został przekazany jako pierwszy argument. Jest tam dla wygody i pozwala na łączenie wywołań w jedną linię kodu:

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

Więc twój problem może być rozwiązany w następujący sposób:

char *foo = "foo";
char *bar = "bar";
char str[80];
strcpy(str, "TEXT ");
strcat(str, foo);
strcat(str, bar);
 303
Author: Brian R. Bondy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-16 17:31:02

Unikaj używania strcat w kodzie C. Najczystszym i, co najważniejsze, najbezpieczniejszym sposobem jest użycie snprintf:

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

Niektórzy komentatorzy podnosili problem, że liczba argumentów może nie pasować do ciągu formatowania i Kod będzie nadal kompilowany, ale większość kompilatorów już wystawia ostrzeżenie, jeśli tak jest.

 211
Author: Alex B,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-10 00:24:32

Ludzie, użyj str n cpy (), str N cat (), lub sNprintf ().
przekroczenie przestrzeni buforowej zniszczy wszystko, co nastąpi w pamięci!
(i pamiętaj, aby pozostawić spację dla końcowego znaku null '\0'!)

 21
Author: Mr.Ree,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-07 20:39:40

Również malloc i realloc są przydatne, jeśli nie wiesz z wyprzedzeniem, ile łańcuchów jest konkatenowanych.

#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);
}
 17
Author: Reed Hedges,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-21 15:29:06

Łańcuchy mogą być również łączone podczas kompilacji.

#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 */ 
                ;
 11
Author: dbagnara,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-19 08:22:13

Nie zapomnij zainicjalizować bufora wyjściowego. Pierwszy argument strcat musi być zakończonym znakiem null z wystarczającą ilością miejsca na wynikowy łańcuch:

char out[1024] = ""; // must be initialized
strcat( out, null_terminated_string ); 
// null_terminated_string has less than 1023 chars
 5
Author: David Rodríguez - dribeas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-21 13:41:10

Pierwszy argument strcat() musi posiadać wystarczającą ilość miejsca dla skonkatenowanego ciągu znaków. Więc przydziel bufor z wystarczającą ilością miejsca, aby otrzymać wynik.

char bigEnough[64] = "";

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

/* and so on */

Strcat() połączy drugi argument z pierwszym argumentem i zapisze wynik w pierwszym argumencie, zwracany znak * jest po prostu tym pierwszym argumentem i tylko dla Twojej wygody.

Nie otrzymujesz nowo przydzielonego ciągu znaków z pierwszym i drugim argumentem konkatenowanym, co chyba oczekiwałeś na podstawie kodu.

 4
Author: Pieter,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-21 13:14:58

Jak ludzie zauważyli, obsługa strun znacznie się poprawiła. Możesz więc nauczyć się, jak używać biblioteki ciągów C++ zamiast ciągów w stylu C. Jednak tutaj jest rozwiązanie w czystym 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;
}

Nie jestem pewien, czy jest to poprawne / bezpieczne, ale w tej chwili nie mogłem znaleźć lepszego sposobu, aby to zrobić w ANSI C.

 4
Author: Nils,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-07 20:51:47

Najlepszym sposobem, aby to zrobić bez ograniczonego rozmiaru bufora jest użycie asprintf ()

char* concat(const char* str1, const char* str2)
{
    char* result;
    asprintf(&result, "%s%s", str1, str2);
    return result;
}
 3
Author: Nico Cvitak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-13 20:23:08

Jest niezdefiniowanym zachowaniem próba modyfikacji literałów łańcuchowych, czyli coś w rodzaju:

strcat ("Hello, ", name);

Będzie próbował zrobić. Spróbuje przeciągnąć łańcuch name na koniec literał "Hello, ", który nie jest dobrze zdefiniowany.

Spróbuj czegoś takiego. Osiąga to, co wydaje się, że próbujesz zrobić:
char message[1000];
strcpy (message, "TEXT ");
strcat (message, var);

Tworzy to obszar bufora, który jest dozwolony do modyfikowania, a następnie kopiuje do niego zarówno literalny ciąg znaków, jak i inny tekst. Tylko uważaj z przepełnienia bufora. Jeśli kontrolujesz dane wejściowe (lub sprawdzasz je wcześniej), dobrze jest używać buforów o stałej długości, takich jak ja.

W przeciwnym razie należy stosować strategie łagodzenia, takie jak przydzielanie wystarczającej ilości pamięci ze sterty, aby upewnić się, że sobie z nią poradzisz. Innymi słowy, coś w stylu:

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.
 3
Author: paxdiablo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-22 02:27:08

Możesz napisać własną funkcję, która robi to samo co strcat() ale to niczego nie zmienia:

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

Jeśli oba ciągi znaków razem mają więcej niż 1000 znaków, wytnie ciąg o długości 1000 znaków. Możesz zmienić wartość MAX_STRING_LENGTH w zależności od swoich potrzeb.

 2
Author: Donald Duck,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-18 12:06:26

Zakładając, że masz znak [fixed_size] zamiast znaku*, możesz użyć jednego, kreatywnego makra, aby zrobić to wszystko na raz z <<cout<<like porządkiem ("raczej %s The disjointed %s\n", "than", "printf style format"). Jeśli pracujesz z systemami wbudowanymi, ta metoda pozwoli Ci również pominąć malloc i dużą rodzinę *printf funkcji takich jak snprintf() (dzięki temu dietlibc nie skarży się również na *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);
  • Uwaga 1, zazwyczaj nie używasz argv [0] w ten sposób-tylko przykład
  • Uwaga 2, możesz użyć dowolnej funkcji, która wyświetla znak*, w tym niestandardowych funkcji, takich jak itoa () do konwersji liczb całkowitych na typy łańcuchowe.
  • Uwaga 3, Jeśli używasz printf gdziekolwiek w swoim programie, nie ma powodu, aby nie używać snprintf (), ponieważ skompilowany kod byłby większy (ale inlined i znacznie szybszy)
 1
Author: technosaurus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-12-14 06:54:32
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;
}
 1
Author: Miljan Rakita,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-05 00:10:06

Próbujesz skopiować łańcuch znaków na adres, który jest przydzielany statycznie. Musisz wejść do bufora.

Konkretnie:

...snip...

Miejsce przeznaczenia

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

...snip...

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

Jest tu również przykład.

 1
Author: Todd,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-22 18:21:47

Jeśli masz doświadczenie w C, zauważysz, że łańcuchy znaków są tylko tablicami znaków, gdzie ostatni znak jest znakiem null.

Teraz jest to dość niewygodne, ponieważ musisz znaleźć ostatni znak, aby coś dodać. Zrobię to za Ciebie.

Więc strcat przeszukuje pierwszy argument pod kątem znaku null. Następnie zastąpi ją zawartością drugiego argumentu (aż skończy się na null).

A teraz przejdźmy przez twoje kod:

message = strcat("TEXT " + var);

Tutaj dodajesz coś do wskaźnika do tekstu "TEXT" (Typ "TEXT" to const char*. Wskaźnik.).

To zwykle nie zadziała. Również modyfikacja tablicy "tekst" nie będzie działać, ponieważ zwykle jest ona umieszczona w stałym segmencie.

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

To może działać lepiej, poza tym, że znowu próbujesz modyfikować statyczne teksty. strcat nie przydziela nowej pamięci dla wyniku.

Proponuję zrobić coś takiego zamiast:

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

Przeczytaj dokumentację sprintf, aby sprawdzić jej opcje.

A teraz ważny punkt:

Upewnij się, że bufor ma wystarczająco dużo miejsca, aby pomieścić tekst i znak null. Istnieje kilka funkcji, które mogą Ci pomóc, np. strncat i specjalne wersje printf, które przydzielają bufor dla Ciebie. Brak zapewnienia rozmiaru bufora doprowadzi do uszkodzenia pamięci i zdalnie wykorzystywalnych błędów.

 1
Author: Ralf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-22 18:24:16

To było moje rozwiązanie

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

Ale musisz określić, ile łańcuchów chcesz połączyć

char *str = strconcat(3, "testing ", "this ", "thing");
 0
Author: Naheel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-31 16:41:21

Spróbuj czegoś podobnego:

#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;
}
 -1
Author: jksante,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-22 18:25:22