Возврат символа* в функции

У меня есть функция:

char *zap(char *ar) {

    char pie[100] = "INSERT INTO test (nazwa, liczba) VALUES ('nowy wpis', '";
    char dru[] = "' )";
    strcat(pie, ar);
    strcat(pie, dru);
    return pie;
}

и в основном существует:

printf("%s", zap( argv[1] )  );

При компиляции я получаю предупреждение:

test.c: In function ‘zap’:
test.c:17: warning: function returns address of local variable

Как я должен возвратить символ* продерзко?

5
задан Devel 14 March 2010 в 13:18
поделиться

9 ответов

Лучше всего не возвращать его вообще - вместо этого передайте буфер вам хотите ввести в функцию в качестве параметра.

void zap(char * pie, const char *ar) {
    strcpy( pie, "INSERT INTO test (nazwa, liczba) VALUES ('nowy wpis', '");
    char dru[] = "' )";
    strcat(pie, ar);
    strcat(pie, dru);
}

Затем вызовите это так:

char pie[100];
zap( pie, "foo" );

Для пуленепробиваемости этой функции вам также необходимо передать длину буфера, а затем проверять ее каждый раз, когда вы собираетесь добавить новый элемент запроса.

18
ответ дан 18 December 2019 в 05:17
поделиться

Выделите память для pie с помощью malloc

4
ответ дан 18 December 2019 в 05:17
поделиться

Я делаю такие манипуляции, делая локальный буфер статической переменной, зависящей от потока:

const int max_pie_cnt = 100;
const char *zap(char *ar) {

    static __declspec(thread) char pie[max_pie_cnt]; // use TLS to store buffer
    strcpy(pie, "INSERT INTO test (nazwa, liczba) VALUES ('nowy wpis', '");
    char dru[] = "' )";
    strcat(pie, ar);
    strcat(pie, dru);
    return pie;
}

Мне очень любопытны комментарии экспертов.

Кстати, давайте на время забудем о проблеме переполнения буфера.

0
ответ дан 18 December 2019 в 05:17
поделиться
char pie[100];

void zap(char* pie, char *ar) {

    char pies[100] = "INSERT INTO test (nazwa, liczba) VALUES ('nowy wpis', '";
    char dru[] = "' )";
    strcpy(pie, pies);
    strcat(pie, ar);
    strcat(pie, dru);
}

zap(pie, argv[1]);
printf("%s", pie  );
2
ответ дан 18 December 2019 в 05:17
поделиться

Все опубликованные решения работают, но чтобы ответить на ваш вопрос, почему вы получаете предупреждение:

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

Если вы хотите выделить постоянную память в функции c, на которую вы можете ссылаться вне этой функции, вам нужно использовать malloc (или другие виды функций выделения памяти из кучи). Это выделит память для этой переменной на куче, и она будет постоянной до тех пор, пока память не будет освобождена с помощью функции free. Если вы не совсем понимаете, что такое стековая память и память кучи, вам стоит погуглить на эту тему, это сделает ваш опыт работы с Си гораздо более гладким.

12
ответ дан 18 December 2019 в 05:17
поделиться
#include <assert.h>
#include <stdio.h>

/**
 * Returns the buffer, just for convenience.
 */
char *generateSQL(char *buf, size_t bufsize, const char *ar) {
    int n;

    n = snprintf(buf, bufsize, "INSERT INTO test (nazwa, liczba) VALUES ('nowy wpis', '%s')", ar);
    /* FIXME: Properly escape the argument, just in case it contains an apostrophe. */
    assert(0 <= n && (unsigned) n < bufsize);
    return buf;
}

int main(int argc, char **argv)
{
    char buffer[4096];

    assert(1 < argc);
    printf("%s\n", generateSQL(buffer, sizeof(buffer), argv[1]));
    return 0;
}
4
ответ дан 18 December 2019 в 05:17
поделиться

Немного другой подход:

void zap(char **stmt, char *argument, size_t *stmtBufLen)
{
  char *fmt="INSERT INTO test(nazwa, liczba) VALUES ('nowy wpis', '%s')";
  /**
   * Is our current buffer size (stmtBufLen) big enough to hold the result string?
   */
  size_t newStmtLen = strlen(fmt) + strlen(argument) - 2;
  if (*stmtBufLen < newStmtLen)
  {
    /**
     * No.  Extend the buffer to accomodate the new statement length.
     */
    char *tmp = realloc(*stmt, newStmtLen + 1);
    if (tmp)
    {
      *stmt = tmp;
      *stmtLen = newStmtLen+1;
    }
    else
    {
      /**
       * For now, just write an error message to stderr; the statement
       * buffer and statement length are left unchanged.
       */
      fprintf(stderr, "realloc failed; stmt was not modified\n");
      return;
    }
  }
  /**
   * Write statement with argument to buffer.
   */
  sprintf(*stmt, fmt, argument);
}

int main(void)
{
  char *stmtBuffer = NULL;
  size_t stmtBufferLen = 0;
  ...
  zap(&stmtBuffer, "foo", &stmtBufferLen);
  ...
  zap(&stmtBuffer, "blurga", &stmtBufferLen);
  ...
  zap(&stmtBuffer, "AReallyLongArgumentName", &stmtBufferLen);
  ...
  zap(&stmtBuffer, "AnEvenLongerRidiculouslyLongArgumentName", &stmtBufferLen);
  ...
  free(stmtBuffer);
  return 0;
}

Эта версия использует динамическое выделение памяти для изменения размера буфера по мере необходимости, начиная с указателя буфера NULL (realloc (NULL, size ) == malloc (размер)). Таким образом, вам не нужно беспокоиться о том, чтобы начать с «достаточно большого» буфера. Единственный недостаток заключается в том, что вам нужно не забыть освободить буфер, когда вы закончите с ним (обычно мне не нравится разделять обязанности по управлению памятью между вызывающим и вызываемым абонентами; если бы я думал об этом более 10 минут, я бы сказал: буду придумать что-нибудь получше).

0
ответ дан 18 December 2019 в 05:17
поделиться

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

2
ответ дан 18 December 2019 в 05:17
поделиться

объявите свой массив символов статическим

static char pie[100] = "INSERT INTO test (nazwa, liczba) VALUES ('nowy wpis', '";
1
ответ дан 18 December 2019 в 05:17
поделиться
Другие вопросы по тегам:

Похожие вопросы: