Возвращение char * в функцию - PullRequest
5 голосов
/ 14 марта 2010

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

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

Как мне правильно вернуть char *

Ответы [ 9 ]

18 голосов
/ 14 марта 2010

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

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" );

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

12 голосов
/ 14 марта 2010

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

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

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

4 голосов
/ 14 марта 2010
#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 голосов
/ 14 марта 2010

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

2 голосов
/ 14 марта 2010

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

2 голосов
/ 14 марта 2010
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  );
1 голос
/ 14 марта 2010

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

static char pie[100] = "INSERT INTO test (nazwa, liczba) VALUES ('nowy wpis', '";
0 голосов
/ 14 марта 2010

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

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 голосов
/ 14 марта 2010

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

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 (size)). Таким образом, вам не нужно беспокоиться о начале работы с буфером, который «достаточно большой». Единственным недостатком является то, что вы должны помнить об освобождении буфера, когда закончите с ним (я обычно не люблю разделять обязанности по управлению памятью между вызывающим и вызываемым абонентами, как это; если я думал об этом более 10 минут, буду придумывать что нибудь получше).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...