【问题标题】:Concatenating strings without buffers连接没有缓冲区的字符串
【发布时间】:2018-08-16 12:24:03
【问题描述】:

有没有办法在不预先分配缓冲区的情况下连接字符串?

考虑以下几点:

int main()
{
    char buf1[] = "world!";
    char buf2[100] = "hello ";
    char * p = "hello ";
    // printf("%s", strcat(p, buf1)); // UB
    printf("%s", strcat(buf2, buf1)); // correct way to use strcat
    return 0;
}

如果我有一个指针作为字符串的前缀,我必须先将它复制到缓冲区,然后再复制到strcat() 吗?是否有任何隐含的功能?

我想到的唯一选项是strcat() 和sprintf(),它们都需要缓冲区。

  • 对于一个需要一个字符串的函数,我需要它,并且我希望有一些接近 Python str1 + str2 语法的东西

【问题讨论】:

  • 如果您需要存储连接的字符串以供以后处理,您将需要一个缓冲区。如果您只想打印串联的字符串"%s%s", p, buf1 应该这样做。
  • 介于两者之间...我需要将它作为一个字符串传递给非printf 函数
  • ...那么你需要一个中间缓冲区。
  • 请记住,C 中没有像 Python、C++ 或其他语言那样的“字符串”类型
  • Python 很可能开始为该行生成大量代码,很可能使用字符串类和堆分配。那么您是在问如何最有效地做到这一点(在速度或内存方面?),或者如何以一种使源代码看起来很漂亮但机器代码看起来很糟糕的方式做到这一点?

标签: c string strcat


【解决方案1】:

嗯,它不是标准的 C 语言所以不是很便携,但是在 GNU libc 系统上你可以使用asprintf()。

int main(void)
{
    const char buf1[] = "world!";
    const char * const p = "hello ";
    char *s;
    if (asprintf(&s, "%s%s", p, buf1) > 0 && s != NULL)
    {
      puts(s);
      free(s);
    }
    return 0;
}

在兼容的系统上编译和运行时,会打印出来

hello world!

【讨论】:

    【解决方案2】:

    如果你真的想使用 C,那么你需要使用缓冲区。在 C++ 或 Python 中,您可以使用为您隐藏工作的构造函数和运算符,但在 C 中您必须自己做事。如果你想让你的代码更简单,你可以创建一个小辅助函数,但要注意内存泄漏和线程:

    #include "stdlib.h"
    #include "string.h"
    #include "stdio.h"
    
    const char * strconcat(const char *p1, const char *p2)
    {
        static __thread char buffer[2048];
        snprintf(buffer, 2048, "%s%s", p1, p2);
        return buffer;
    }
    
    int main(int argc, char** argv)
    {
        const char *p1 = "hello ";
        const char *p2 = "world!";
        printf("%s\n", strconcat(p1, p2));
    }
    

    只要生成的字符串不超过 2048 个字符,此示例就可以工作;否则生成的字符串将被截断。

    这当然只是您可以创建的辅助函数的一个示例。

    请注意,上面的示例返回您无法编辑的const char *。如果你想进一步编辑字符串,你需要创建一个分配缓冲区的辅助函数,但是你需要管理你的内存!

    【讨论】:

    • 这对于实际应用程序来说不是一个好主意,因为每组字符串只能调用一次该函数。如果我要引入 p3 和 p4 并将它们也连接起来,那么第一个字符串将会改变。
    • 示例:const char* s1 = strconcat("hello ", "world"); const char* s2 = strconcat("not buggy ", "at all"); puts(s1);
    【解决方案3】:

    在运行时执行此操作的最有效方法是调用 malloc 并为新字符串分配空间,然后复制那里的所有内容。这很可能是 Python 在背后做的事情——尽管它会有一个字符串类型。

    在 ADT/类之外执行此操作的缺点是您必须手动进行清理。

    然而,使用 malloc + copy 比任何 *printf 都快得多,它是线程安全的,并且可以再次重复使用相同的函数。示例:

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    
    char* strdog (const char* s1, const char* s2)
    {
      size_t size1 = strlen(s1);
      size_t size2 = strlen(s2);
      char* result = malloc(size1 + size2 + 1);
      if(result == NULL) 
      {
        exit(EXIT_FAILURE);
      }
    
      memcpy(result, s1, size1);
      memcpy(result+size1, s2, size2);
      result[size1 + size2] = '\0';
      return result;
    }
    
    inline void cleandog (char* s)
    {
      free(s);
    }
    
    int main (void)
    {
      char s1[] = "hello ";
      char s2[] = "world";
    
      char* str = strdog(s1, s2);
      puts(str);
      cleandog(str);
    }
    

    但是请注意,C 支持编译时连接。所以如果你只使用字符串文字,你应该这样做:

    #define HELLO "hello "
    #define WORLD "world"
    
    ...
    
    const char* str = HELLO WORLD;
    puts(str);
    

    这当然是它们的高级版本,但仅适用于编译时字符串文字。

    【讨论】:

    • 好答案,这是我所说的替代方案之一,但这需要良好的内务管理......
    猜你喜欢
    • 2010-09-14
    • 2020-05-07
    • 2010-11-25
    • 2021-12-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多