2
#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 但它仍然无法正常工作。这个程序有什么问题?

4

7 回答 7

10

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

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

在这种情况下,似乎在 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 传回结果。因此,这更加健壮,您不会意外溢出缓冲区。

于 2009-03-18T20:07:55.090 回答
4

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

于 2009-03-18T20:03:58.843 回答
4

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

任何一个:

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

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

于 2009-03-18T20:06:17.437 回答
3

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

char buffer [80];

到:

static char buffer [80];
于 2009-03-18T20:03:29.097 回答
2

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

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 参数准确地指定了缓冲区指向的空间量。)

于 2009-03-18T20:22:27.223 回答
0

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

于 2009-03-18T20:04:14.133 回答
0

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

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

于 2009-03-18T20:04:15.693 回答