функция char * не отображается правильно в C - PullRequest
2 голосов
/ 18 марта 2009
#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, но он все еще не работает. Что не так с этой программой?

Ответы [ 7 ]

10 голосов
/ 18 марта 2009

Вы возвращаете указатель на локальную переменную («буфер»), это неверно, и я удивлен, что вы не получили предупреждение об этом.

При выходе из функции все локальные переменные перестают существовать (это называется выходом из области видимости), и их память будет использоваться для других целей. Вы возвращаете указатель на эту память, но нет никакой гарантии, что теперь там будет.

В этом случае кажется, что во время вызова 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. В результате это намного надежнее, и вы не можете случайно переполнить буфер.

4 голосов
/ 18 марта 2009

Ваш возвратный буфер в функции должен быть где-то кроме стека. Поскольку вы объявили автоматическую переменную как массив фиксированного размера, он находится в стеке. Вы возвращаете указатель на него, НО, что это пространство может быть «закрашено» последующими вызовами функций.

Или:

  • используйте статический буфер и поймите, что функция не является входящей
  • выделите буфер с помощью malloc () и не забудьте освободить его в последующем в вызывающей программе

Оба варианта имеют недостатки. У вас будет 17 ответов, прежде чем я смогу их перечислить: -)

4 голосов
/ 18 марта 2009

Оператор char buffer [80]; делает buffer размещенным в стеке csas. Замените его на звонок malloc (char *buffer = malloc (80)), и все будет в порядке. Вы должны будете самостоятельно освободить буфер позже.

3 голосов
/ 18 марта 2009

Поскольку буфер - это локальная переменная, которая испаряется при возврате функции - вы видите неопределенное поведение. Быстрое и грязное решение этой проблемы - сделать буфер статичным, чтобы он зависал после вызова функции - измените:

char buffer [80];

до:

static char buffer [80];
2 голосов
/ 18 марта 2009

Не используйте статический буфер, если вы не уверены, что никогда не будете использовать его в многопоточном коде и никогда не вызовете его дважды, прежде чем использовать первый ответ.

Malloc - вариант, но принуждение вызывающей стороны освободить память, выделенную вызываемой, может оставить открытые проблемы владения и исключает возможность использования чего угодно, кроме кучи памяти для буфера.

На мой взгляд, лучше всего принять модификацию предложения Эндрю Гранта, но также обойти длину буфера:

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 точно указывает объем пространства, на которое указывает буфер).

0 голосов
/ 18 марта 2009

Вы возвращаете «буфер», который расположен в стеке. Но как только функция возвращается, эта часть стека больше не действительна.

Либо выделите память для возвращаемой строки в куче, либо используйте std :: string в качестве возвращаемого значения.

0 голосов
/ 18 марта 2009

Вы вызываете czas (), но после возврата созданный czas буфер больше не существует.

...