【问题标题】:Using strcat in C在 C 中使用 strcat
【发布时间】:2010-09-29 23:53:08
【问题描述】:

好的,所以我有以下代码,它在 C# 中将一个字符串附加到另一个字符串,请注意,这只是一个 示例,因此在 C# 中提供替代字符串连接方法并不是必需的,这只是为了简化示例。

string Data = "";
Data +="\n\nHTTP/1.1 " + Status_code;
Data += "\nContent-Type: " + Content_Type;
Data += "\nServer: PT06";
Data += "\nContent-Length: " + Content_Lengt;
Data += "\nDate: " + Date;
Data += "\n" + HTML;

现在我想在 C 中做同样的事情,我正在尝试按照以下方式进行操作

time_t rawtime;

time ( &rawtime );

char *message = "\n\nHTTP/1.1 ";
message = strcat(message, Status_code);
message = strcat(message, "\nContent-Type: ");
message = strcat(message, Content_Type);
message = strcat(message, "\nServer: PT06");
message = strcat(message, "\nContent-Length: ");
message = strcat(message, Content_Lengt);
message = strcat(message,  "\nDate: ");
message = strcat(message, ctime(&rawtime));
message = strcat(message, "\n");
message = strcat(message, HTML);

现在,这给了我一个段错误,我知道为什么,我访问并读取了我不应该访问的内存。但问题是,我该如何解决?我可以使用 string.h 并像在 C# 中那样做吗?

【问题讨论】:

  • 除此之外,所以不是“回复”——但 C# 代码效率也很低;要么使用单个连接(a + b + c + d + e,编译器读取为 string.Concat(a,b,c,d,e)),要么使用 StringBuilder。
  • 请注意,通过标记 C#,所有那些 C-but-not-C# 程序员都会错过您的问题,因为我认为 C# 是一个流行的 Ignored Tag。
  • @Paul - 确实;另外,你会得到很多 C# 但不是 C 的程序员(比如我),他们阅读了它,但除了猜测之外真的无能为力。
  • 我明白了,谢谢。但是,C# 代码不是问题。正如我在之前的评论中所说的那样,这只是一种简单的方式来展示我想在 C 中做什么。如果我使用了 StringBuilder,那么这个问题就不那么具有教学性了。
  • 我不同意,实际上没有使用 StringBuilder 将焦点转移到不是实际问题的事情上。

标签: c string


【解决方案1】:

message 指向您无法写入的 char const[],但这正是 strcat 正在写入的位置。你需要 malloc() 一个足够大的缓冲区。

【讨论】:

  • 我明白了,那我试试这个。但是你是说我不能在那里使用 const char[] 吗?嗯。
  • 确实没有。段错误是由写入为“”字面量分配的 const(只读)内存引起的。
  • 您能否提供一个代码示例,说明如何将上述内容更改为该代码示例?
【解决方案2】:

改变

char *message = "\n\nHTTP/1.1 ";

char message[1024];  
strcpy(message,"\n\nHTTP/1.1 ");

你应该没问题,总消息长度为 1023。

编辑:(根据 mjy 的评论)。以这种方式使用 strcat 是获得缓冲区溢出的好方法。您可以轻松编写一个小函数来检查缓冲区的大小和传入字符串添加的长度来克服这个问题,或者在动态缓冲区上使用 realloc。 IMO,程序员有责任检查使用它们的正确缓冲区大小,就像 sprintfs 和其他 C 字符串函数一样。出于性能原因,我假设 C 被用于 C++,因此 STL 不是一个选项。

编辑:根据 Filip 评论的要求,一个基于固定大小字符缓冲区的简单 strcat 实现:

char buffer[MAXSIZE] = "";

int mystrcat(char *addition)
{
   if (strlen(buffer) + strlen(addition) + sizeof(char)  >= MaxSize)
     return(FAILED);
   strcat(buffer,addition);
   return(OK);
}

使用动态分配:

char *buffer = NULL;

int mystrcat(char *addition)
{
   buffer = realloc(buffer, strlen(buffer) + strlen(addition) + sizeof(char));
   if (!buffer)
     return(FAIL);
   strcat(buffer, addition);
   return(OK);
}

在这种情况下,您必须在完成后手动释放缓冲区。 (由 C++ 等效项中的析构函数处理)

附录(人):

好的,既然你没有真正解释为什么你必须创建message[1024],就在这里。

使用char *x = "hello",实际字节('h','e','l','l','o',0)(最后为null)存储在一个区域中内存与变量分开(很可能是只读的)并且变量 x 被设置为指向它。在 null 之后,可能还有其他非常重要的东西。所以你根本不能追加。

使用char x[1024]; strcpy(x,"hello");,您首先分配完全专用于x 的1K om 内存。然后你把“hello”复制进去,最后仍然留出相当多的空间来附加更多的字符串。在追加超过允许的 1K 奇数之前,您不会遇到麻烦。

结束附录(Pax):

【讨论】:

  • 是的,这就是我们现在在 C++ 中使用 stl 字符串、MFC CStrings 或其他各种智能字符串的原因。
  • 但是这是 C 而不是 C++。这个例子并没有像我希望的那样运行。我需要将所有代码更改为 strcpy 还是仍然使用 strcat?仍然使用 strcat 会产生很多其他错误。
  • @Filip,这取决于上下文。预先分配的固定大小缓冲区比动态分配更快,并且由于资源问题而失败的可能性较小。您是追求内存和速度效率还是可靠的通用子程序。
  • 我只想要一个简单、可读的上述字符串连接。我有一个方法,它需要 4 个字符指针,这些指针应该连接成一个字符串,然后我也返回一个指针。所以速度、效率或其他什么都不重要,理解才是最重要的
  • 如果您可以确定总消息长度不会超过 1023 个字符(即您是否可以控制 Status_code 等?),则此答案很好。 strcat 很好; strcpy 会覆盖你的字符串而不是附加它。如果您不必这样做,它肯定比边界检查更具可读性。
【解决方案3】:

从使用更安全的strncat函数开始。一般来说,如果字符串的大小大于特定大小,则始终使用不会溢出的更安全的 'n' 函数。

在 C 中,您需要自己处理字符串大小。因此,您需要知道生成的字符串有多大并适应它。如果您知道左侧所有字符串的大小,您应该创建一个足够大的缓冲区来保存生成的字符串。

【讨论】:

    【解决方案4】:

    如前所述,您必须写入足够大的缓冲区。不幸的是,这样做需要做很多额外的工作。大多数处理字符串的 C 应用程序都使用可动态调整大小的字符串缓冲区来进行连接。

    glib 包含一个实现,glib strings,我建议将它用于任何大量使用字符串的应用程序。它使管理内存更容易,并防止缓冲区溢出。

    【讨论】:

      【解决方案5】:

      以经典 C 风格执行此操作的安全方法是:

       char *strconcat(char *s1, char *s2)
       {
          size_t old_size;
          char *t;
      
          old_size = strlen(s1);
      
          /* cannot use realloc() on initial const char* */
          t = malloc(old_size + strlen(s2) + 1);
          strcpy(t, s1);
          strcpy(t + old_size, s2);
          return t;
        }
      
        ...
      
        char *message = "\n\nHTTP/1.1 ";
        message = strconcat (message, Status_code);
        message = strconcat (message, "\nContent-Type: ");
      

      现在你可以说很多关于它的坏话:它效率低下,它会破坏你的记忆,它很丑陋......但它或多或少是任何具有字符串连接运算符和 C 类型(零终止)字符串的语言可以(除了这些语言中的大多数将内置垃圾收集)。

      【讨论】:

      • -1:这是一个巨大的内存泄漏。您需要释放 s1 指针当且仅当它不是由您首先分配的(如果您有一个 const char * 起始值,这会使代码更加糟糕)。
      • 那最好是静态函数。
      • 是的,它会泄漏内存,使用 realloc() 并复制初始的 const char* 可以轻松避免这种情况。所有简单的 C 解决方案都是内存效率低下的,例如,常量字符串都是重复的(即使使用 realloc)。此外,您始终需要明确释放最终结果。
      • 这些事情超出了原始答案的范围。如果 OP 也想要一个内存高效的版本,那么答案会更长。我的重点是简短且等效于不使用不安全静态缓冲区或非 libc 库的 C# 代码。
      【解决方案6】:

      我想知道为什么还没有人从stdio.h 中提到snprintf()。这是输出多个值的 C 方式,您甚至不必事先将原语转换为字符串。

      以下示例使用堆栈分配的固定大小的缓冲区。否则,您必须 malloc() 缓冲区(并存储其大小),这样就可以在溢出时 realloc()...

      char buffer[1024];
      int len = snprintf(buffer, sizeof(buffer), "%s %i", "a string", 5);
      if(len < 0 || len >= sizeof(buffer))
      {
          // buffer too small or error
      }
      

      编辑:您也可以考虑使用asprintf() 函数。它是一个广泛可用的 GNU 扩展,也是 TR 24731-2 的一部分(这意味着它可能会成为下一个 C 标准)。上面的例子是

      char * buffer;
      if(asprintf(&buffer, "%s %i", "a string", 5) < 0)
      {
          // (allocation?) error
      }
      

      使用完缓冲区后记得free()

      【讨论】:

      • 似乎 asprintf 进入了 C11,或者至少 GCC -std=c11 认为它做到了......?无论如何,这是最有帮助的答案,谢谢!
      【解决方案7】:

      还没有看到关于 strlcpy、strlcat 函数的任何提及,它类似于“n”函数,除了也考虑了尾随 0。两者都采用第三个参数指示输出缓冲区的最大长度,并在字符串.h。

      示例:

      char blah[]="blah";
      char buffer[1024];
      strlcpy(buffer,"herro!!!",sizeof(buffer));
      strlcat(buffer,blah,sizeof(buffer));
      printf("%s\n",buffer);
      

      将输出“herro!!!blah”

      char blah[]="blah";
      char buffer[10];
      strlcpy(buffer,"herro!!!",sizeof(buffer));
      strlcat(buffer,blah,sizeof(buffer));
      printf("%s\n",buffer);
      

      由于缓冲区[]的大小有限,将输出“herro!!!b”,没有段错误。 ^^

      唯一的问题不是所有平台似乎都将它包含在它们的 libc 中(例如 linux ._.),大多数 BSD 变体似乎都有它。

      在这种情况下,可以在此处找到这两个函数的代码并轻松添加:strlcpystrlcatthe rest of string.h

      【讨论】:

      • strl* 没有那么广泛可用,并且最终变得不那么便携。至少 strn* 是标准的 afaik。
      猜你喜欢
      • 2017-03-15
      • 2019-01-05
      • 1970-01-01
      • 2021-08-24
      • 1970-01-01
      • 2013-10-16
      • 2019-03-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多