【问题标题】:char * function not displaying correctly in Cchar * 函数在 C 中无法正确显示
【发布时间】:2009-03-18 20:01:14
【问题描述】:
#include <time.h>
#include <stdio.h>
#include <stdlib.h>

char *czas()
{
  time_t rawtime;
  struct tm * timeinfo;
  char buffer [80];
  time ( &rawtime );
  timeinfo = localtime ( &rawtime );
  strftime (buffer,80,"Now it's %I:%M%p.",timeinfo);
  return buffer;
}

int main()
{
printf("%s",czas());
system("PAUSE");
}

我不知道为什么,但这个程序的结果只是“按任意键(...)”。我也尝试将其打印为 %c ,但它仍然无法正常工作。这个程序有什么问题?

【问题讨论】:

  • 这是一个典型的错误——每个人都应该犯一次,然后他们就会了解“范围”。顺便说一句,由于“未定义”缓冲区可能仍在内存中,你很幸运它坏了。有时这样的东西不会被发现!

标签: c char


【解决方案1】:

您正在返回一个指向局部变量(“缓冲区”)的指针,这是无效的,我很惊讶您没有收到有关它的警告。

当一个函数退出时,所有局部变量都不再存在(称为超出范围),它们的内存将用于其他目的。您正在返回一个指向此内存的指针,但不能保证现在会在那里。

在这种情况下,似乎在 printf 调用时内存包含一个 0,它被视为空字符串。这实际上是相当幸运的,您很容易以打印垃圾或程序崩溃而告终。

要解决这个问题,您可以将一个缓冲区传递给 czas,或者让 czas 在堆上分配一个缓冲区,以便稍后释放。我会推荐前者,因为它与几乎所有库函数的工作方式一致。它还避免了调用者稍后必须释放指针的内存分配。

例如:

size_t czas(char* buffer, size_t buffer_size)
{
    time_t rawtime;
    struct tm * timeinfo;

    time ( &rawtime );
    timeinfo = localtime ( &rawtime );
    return strftime (buffer, buffer_size,"Now it's %I:%M%p.",timeinfo);
}

int main()
{
    char buffer [80];
    if (czas(buffer, 80))
    {
      printf("%s\n",buffer);
    }
    else
    {
      printf("Call to czas failed");
    }
    system("PAUSE");
}

更新:我没有注意到 strftime 采用了大小参数,我已经更新了代码以使用它并正确地从 strftime 传回结果。因此,这更加健壮,您不会意外溢出缓冲区。

【讨论】:

  • 另请注意,如果您选择解决可能太小的缓冲区,则检查 czas 函数内部的 sizeof(buffer) 是不够的。这只会给你一个字符指针的大小,而不是缓冲区的大小。您必须将缓冲区大小作为第二个参数传递。
【解决方案2】:

语句char buffer [80]; 使buffer 分配在csas 的堆栈中。将其替换为对 malloc (char *buffer = malloc (80)) 的调用,您应该会没事的。稍后您必须自己释放缓冲区。

【讨论】:

    【解决方案3】:

    函数中的返回缓冲区应该位于堆栈之外的某个位置。由于您已将自动变量声明为固定大小的数组,因此它位于堆栈上。您返回一个指向它的指针,但是,随后的函数调用可以“乱涂乱画”该空间。

    要么:

    • 使用静态缓冲区,实现函数不可重入
    • 用 malloc() 分配一个缓冲区,然后记得在调用者中 free() 它

    这两个选项都有缺点。在我列举它们之前,您将有 17 个答案:-)

    【讨论】:

      【解决方案4】:

      因为缓冲区是一个局部变量,当函数返回时会消失 - 您看到的是未定义的行为。一个快速而肮脏的解决方法是使缓冲区静态,以便它在函数调用后挂起 - 更改:

      char buffer [80];
      

      到:

      static char buffer [80];
      

      【讨论】:

      • 为什么 1000 个人看到一个人输入了相同的答案,却说同样的话?
      • 这不是一个好的解决方案。为了返回值而使某些东西成为静态不是人们应该采取的第一种方法。现在,这使得该变量在应用程序的整个生命周期中都存在。分配内存或使用 out-parameter 代替。
      • 嗯,对于第一对夫妇来说,他们的借口是他们可能在 Neil 还在打字的时候打字。但是三四分钟后才回答的人,我不确定他们有没有借口,尽管他们中的一些人花时间写了更多的解释。
      【解决方案5】:

      不要使用静态缓冲区,除非您确定永远不会在多线程代码中使用它,并且在使用第一个答案之前永远不会调用它两次。

      Malloc 是一种选择,但强制调用者释放被调用者分配的内存可能会留下未解决的所有权问题,并消除使用除堆内存之外的任何内容作为缓冲区的可能性。

      在我看来,最好的办法是修改 Andrew Grant 的建议,但也要传递缓冲区的长度:

      char *czas(char *buffer, size_t bufferLength)
      {
        time_t rawtime;
        struct tm * timeinfo;
        time ( &rawtime );
        timeinfo = localtime ( &rawtime );
        strftime (buffer, bufferLength, "Now it's %I:%M%p.",timeinfo);
        return buffer;
      }
      
      int main()
      {
         char buffer [80];
         printf("%s",czas(buffer, sizeof(buffer)));
         system("PAUSE");
      }
      

      或者

      #define TIME_BUFFER_LENGTH 80
      int main()
      {
         char *buffer = malloc(TIME_BUFFER_LENGTH);
         if (buffer)
             printf("%s",czas(buffer, TIME_BUFFER_LENGTH));
         free(buffer);
         system("PAUSE");
      }
      

      这样可以更轻松地跟踪潜在的内存泄漏和缓冲区溢出。您可以查看czas 并看到只要参数正确,该函数就不会溢出任何缓冲区或泄漏任何内存。接下来,你可以查看main的任一版本,看到没有内存泄漏,并且传递给czas的参数是正确的(bufferLength参数准确地指定了buffer指向的空间量。)

      【讨论】:

        【解决方案6】:

        您调用 czas() 但返回后,czas 创建的缓冲区不再存在。

        【讨论】:

          【解决方案7】:

          您正在返回分配在堆栈上的“缓冲区”。但是一旦函数返回,堆栈的那部分就不再有效。

          要么在堆上为返回的字符串分配内存,要么使用 std::string 作为返回值。

          【讨论】:

            猜你喜欢
            • 2021-01-10
            • 2020-09-24
            • 1970-01-01
            • 1970-01-01
            • 2022-01-20
            • 2019-05-10
            • 1970-01-01
            • 1970-01-01
            • 2021-05-12
            相关资源
            最近更新 更多