代码之家  ›  专栏  ›  技术社区  ›  The.Anti.9

如何在C中连接const/literal字符串?

  •  285
  • The.Anti.9  · 技术社区  · 16 年前

    我在C中工作,我必须连接一些东西。

    现在我有了这个:

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

    现在,如果您有C方面的经验,我相信您会意识到,当您尝试运行它时,这会给您一个分段错误。我该怎么解决这个问题呢?

    17 回复  |  直到 7 年前
        1
  •  322
  •   Endre mr. fixit    10 年前

    在C语言中,“字符串”只是简单的 char 数组。因此,不能直接将它们与其他“字符串”连接起来。

    你可以使用 strcat 函数,它附加由 src 指向字符串的结尾 dest :

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

    这里是一个 example from cplusplus.com :

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

    对于第一个参数,需要提供目标缓冲区本身。目标缓冲区必须是字符数组缓冲区。例如。: char buffer[1024];

    确保 第一个参数有足够的空间来存储您试图复制到其中的内容。如果您可以使用,使用以下功能更安全: strcpy_s strcat_s 其中,您必须显式地指定目标缓冲区的大小。

    注释 :字符串文字不能用作缓冲区,因为它是常量。因此,您总是需要为缓冲区分配一个char数组。

    的返回值 字符串连接函数 可以忽略,它只返回与第一个参数相同的指针。为了方便起见,它允许您将调用链接到一行代码中:

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

    所以你的问题可以解决如下:

    char *foo = "foo";
    char *bar = "bar";
    char str[80];
    strcpy(str, "TEXT ");
    strcat(str, foo);
    strcat(str, bar);
    
        2
  •  221
  •   Alex B    11 年前

    避免使用 strcat 在C代码中。最干净,最重要的是,最安全的方法是使用 snprintf :

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

    一些注释者提出了一个问题,即参数的数量可能与格式字符串不匹配,代码仍将编译,但如果是这种情况,大多数编译器已经发出警告。

        3
  •  23
  •   Keith Thompson    13 年前

    各位,使用STR n () n (或) n Primff()。
    超过缓冲区空间将在内存中丢弃后面的任何内容!
    (请记住为后面的空字符\0'留出空间!)

        4
  •  17
  •   Reed Hedges    16 年前

    此外,如果您不知道提前连接了多少字符串,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);
    }
    
        5
  •  14
  •   dbagnara    9 年前

    字符串也可以在编译时连接起来。

    #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 */ 
                    ;
    
        6
  •  5
  •   David Rodríguez - dribeas    16 年前

    不要忘记初始化输出缓冲区。strcat的第一个参数必须是以空结尾的字符串,并为结果字符串分配足够的额外空间:

    char out[1024] = ""; // must be initialized
    strcat( out, null_terminated_string ); 
    // null_terminated_string has less than 1023 chars
    
        7
  •  4
  •   Pieter    16 年前

    strcat()的第一个参数需要能够为连接的字符串保留足够的空间。因此,分配一个有足够空间接收结果的缓冲区。

    char bigEnough[64] = "";
    
    strcat(bigEnough, "TEXT");
    strcat(bigEnough, foo);
    
    /* and so on */
    

    strcat()将第二个参数与第一个参数连接起来,并将结果存储在第一个参数中,返回的char*只是第一个参数,而且只是为了方便起见。

    您不会得到一个新分配的字符串,第一个参数和第二个参数连接在一起,我想您应该根据代码来获得这个字符串。

        8
  •  4
  •   Keith Thompson    13 年前

    正如人们指出的那样,弦的处理提高了很多。因此,您可能想学习如何使用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;
    }
    

    我不确定它是否正确/安全,但是现在我找不到一个更好的方法来在ANSIC中这样做。

        9
  •  4
  •   Nico Cvitak    11 年前

    在缓冲区大小不受限制的情况下,最好的方法是使用asprintf()。

    char* concat(const char* str1, const char* str2)
    {
        char* result;
        asprintf(&result, "%s%s", str1, str2);
        return result;
    }
    
        10
  •  3
  •   paxdiablo    9 年前

    试图修改字符串文字是未定义的行为,这类似于:

    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.
    
        11
  •  2
  •   Donald Duck user7392049    8 年前

    您可以编写自己的函数,该函数与 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 以满足您的需求。

        12
  •  1
  •   technosaurus    10 年前

    假设您有一个字符[固定的\大小]而不是一个字符*,那么您可以使用一个单独的、有创意的宏同时使用 <<cout<<like 排序(“而不是%s,不连接的%s\n”、“than”、“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,您可以使用任何输出char*的函数,包括像itoa()这样的非标准函数来将整数转换为字符串类型。
    • 注3:如果你已经在你的程序中的任何地方使用了printf,那么没有理由不使用snprintf(),因为编译后的代码会更大(但是内联的,速度明显更快)。
        13
  •  1
  •   Eli Sadoff    8 年前
    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;
    }
    
        14
  •  1
  •   Peter Mortensen icecrime    8 年前

    您试图将字符串复制到静态分配的地址中。你需要进入缓冲区。

    明确地:

    ……剪断…

    目的地

    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

    这里也有一个例子。

        15
  •  1
  •   Peter Mortensen icecrime    8 年前

    如果您有C方面的经验,您会注意到字符串只是最后一个字符为空字符的字符数组。

    现在这非常不方便,因为您必须找到最后一个字符才能附加一些内容。 strcat 会帮你的。

    所以strcat在第一个参数中搜索空字符。然后,它将用第二个参数的内容替换它(直到以空结尾)。

    现在让我们检查一下您的代码:

    message = strcat("TEXT " + var);
    

    在这里,您要向指向文本“text”的指针添加一些内容(“text”的类型是const char*)。一个指针。

    这通常行不通。另外,修改“文本”数组也不会起作用,因为它通常被放置在一个常量段中。

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

    这可能会更好,除非您再次尝试修改静态文本。strcat没有为结果分配新内存。

    我建议改为这样做:

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

    阅读以下文件: sprintf 检查它的选项。

    现在重要的一点是:

    确保缓冲区有足够的空间容纳文本和空字符。有几个功能可以帮助您,例如strncat和为您分配缓冲区的printf的特殊版本。 不确保缓冲区大小将导致内存损坏和可远程利用的错误。

        16
  •  0
  •   Naheel    8 年前

    这是我的解决方案

    #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");
    
        17
  •  -1
  •   Peter Mortensen icecrime    8 年前

    尝试类似的方法:

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