C Предупреждение: функция возвращает адрес локальной переменной - PullRequest
5 голосов
/ 01 августа 2011

Функция ниже принимает аргумент argv [0], который содержит путь вызова приложения, и заменяет последний бит, пока он не достигнет "/", с именем нового приложения, которое я хочу создать, которое находится в той же папке .

Кстати: я объявляю глобальную переменную argv, чтобы функция могла иметь к ней доступ, потому что я не хотел передавать информацию при каждом вызове функции.

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

Я знаю, что объявляю переменную и, как только функция вернется, она будет уничтожена.

Будучи начинающим программистом на C, я хотел знать, каким будет самый элегантный / самый простой способ решения этой проблемы?

Должен ли я передать указатель на функцию или malloc немного памяти?

char *returnFullPath()
{
    char pathToApp[strlen(argv[0])+1];
    strcpy(pathToApp, argv[0]);
    int path_length = strlen(argv[0]);

    while (pathToApp[path_length] != '/')
    {
        path_length--;
    }

    if (path_length > 2)
        pathToApp[path_length+1] = '\0';
    else
        pathToApp[0] = '\0';

    // length of getcwd + length of pathtoapp + 1 for zero plus 6 for "bidbot"
    char bidbotPath[strlen(getcwd(NULL,0)) + strlen(pathToApp) + 1 + 6];

    sprintf(bidbotPath, "%s/%sbidbot", getcwd(NULL,0), pathToApp);

    return bidbotPath;
}

Ответы [ 8 ]

45 голосов
/ 01 августа 2011

Некоторые другие ответы предполагают, что вы malloc что-то и вернуть это. Это плохая практика в том же смысле, что и в C ++, когда вы добавляете что-то в функцию, и вызывающая сторона должна удалить это (у кого есть право собственности?)

Существует причина, по которой многие API C имеют формат:

function(buf, length);

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

Не надо malloc и возвращайся. Это просто напрашивается на неприятности.

4 голосов
/ 01 августа 2011

Замените

char bidbotPath[strlen(getcwd(NULL,0)) + strlen(pathToApp) + 1 + 6];

на

char* bidbotPath = malloc(strlen(getcwd(NULL,0)) + strlen(pathToApp) + 1 + 6);

Таким образом, ваша переменная будет размещена в куче, а не в стеке, поэтому она не будет удалена после возврата функции.

3 голосов
/ 18 апреля 2014

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

Клиентская собственность

Можно было бы, как предложил Moo-Juice, добавить некоторые параметры к вашему вызову, делегируя ответственность за постоянство информации после вызова функции.

void returnFullPath(char* fullPath, int maxLength)

и прежде чем вы закончите, скопируйте свой результат в выходной параметр с вызовом strncpy (http://www.cplusplus.com/reference/cstring/strncpy/).

strncpy(fullPath, bidbotPath, maxLength);

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

Право собственности со стороны поставщика

Существует, однако, другой подход, также принятый для этого языка. И это тот, который используется, например, библиотекой stdio.h. Если вы хотите открыть файл, вы используете структуру FILE в качестве указателя. В этом случае stdio предоставляет нам как функции fopen, так и fclose, одну, которая распределяет ресурсы, и другую, которая их распределяет. Это использует концепцию, называемую абстрактный тип данных, которая ближе всего подходит к объекту, который мы когда-либо увидим в структурированном программировании. См. this для получения дополнительной информации о ADT. В этом случае полная ADT кажется абсурдным излишним для того, что вы делаете, но идет с идеей.

Для этого случая потребуются как функции, так и распределение и перераспределение.

char* getFullPath(); /* here is where you do malloc*/
void disposeFullPath(char* fullPath); /* and here, free */

Таким образом, вы можете распределить точное количество необходимой памяти


По поводу вашего вопроса я хотел бы сделать несколько комментариев.

  • Всякий раз, когда вы можете, старайтесь придерживаться стандарта ANSI. Это - это Википедия, но кажется точным.
  • Теперь, когда вы используете C, вы должны проверить правила стиля для языка. Проверьте это .
  • Используйте strrchar, чтобы найти последний символ '/' на пути: здесь вы идете
  • И последнее, но не менее важное: избегайте статических глобальных переменных, это не что иное, как головная боль
3 голосов
/ 01 августа 2011

Если возможно, всегда лучше указатель на функцию памяти, в которую можно записать возвращаемое значение. Я говорю так, потому что вы разрешаете своим клиентам (вызывающим функции) выбирать, где размещать память: в стеке или в куче, или, возможно, даже где-то более экзотическим.

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

1 голос
/ 01 августа 2011

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

Есть два способа решения этой проблемы.

1) используйте переменную static. статические локальные переменные не освобождаются при выходе из функции.

static char bidbotPath[....];

НО! он не будет работать с переменной длиной.

2) Использование malloc

char *bidbotPath = malloc(strlen(getcwd(NULL,0)) + strlen(pathToApp) + 1 + 6);

и вам следует звонить free(bidbotPath) после всех его использований.

0 голосов
/ 07 марта 2015

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

0 голосов
/ 01 августа 2011

Поскольку BidotPath объявлен в области видимости тела функции как обычная переменная стека, он исчезнет, ​​когда функция вернется. Хотя ваша программа может работать сейчас, это просто удача, и она может начать сбой позже, если другой код использует старую область стека раньше, чем ваш вызывающий.

Вы можете объявить bobotPath статическим, который хранит его, но предотвращает поточную безопасность функции. Вы можете сделать malloc надлежащей длины и вернуть его, чтобы сохранить поток функции безопасным, но вызывающей стороне потребуется освободить память, чтобы избежать утечек. Лучше всего предоставить массив символов и длину, в которую данные будут помещены в аргумент вашей функции. Подумайте snprintf () здесь. Внутри используйте strncpy () и аналогичные процедуры для копирования в цель, но помните, что strncat () может быть небезопасным для вас.

Кроме того, ваш код должен учитывать тот факт, что в argv [0] не может быть косой черты ... только имя исполняемого файла.

Не совсем то, что вам нужно, но вот код, который я использовал. Я оставляю это в качестве упражнения студенту, чтобы получить то, что вам нужно:

  cp = strrchr( argv[0], '/' );
  if ( cp )
     cp++;
  else
    cp = argv[0];
0 голосов
/ 01 августа 2011

Вы должны назначить bidbotPath переменную память динамически, используя malloc или calloc.Затем убедитесь, что код, вызывающий вашу функцию, фактически освобождает память, которую вы возвращаете.Это обычная практика и распространенная идиома для функций C, которые возвращают указатели на «сгенерированный» массив.

char * bidbotPath = (char*)malloc(strlen(getcwd(NULL,0)) + strlen(pathToApp) + 1 + 6);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...