При возврате указателя на локальный массив, почему это всегда одно и то же значение? - PullRequest
0 голосов
/ 08 апреля 2010

Я программирую простой анализатор пакетов для проекта класса. Некоторое время я сталкивался с проблемой, когда источник и назначение пакета оказались одинаковыми. Например, источником и назначением кадра Ethernet всегда будет один и тот же MAC-адрес. Я заказал ether_ntoa(char *), потому что Windows, кажется, не имеет ethernet.h, как Linux. Фрагмент кода ниже:

char *ether_ntoa(u_char etheraddr[ETHER_ADDR_LEN])
{
    int i, j;
    char eout[32];

    for(i = 0, j = 0; i < 5; i++)
    {
        eout[j++] = etheraddr[i] >> 4;
        eout[j++] = etheraddr[i] & 0xF;
        eout[j++] = ':';
    }
    eout[j++] = etheraddr[i] >> 4;
    eout[j++] = etheraddr[i] & 0xF;
    eout[j++] = '\0';
    for(i = 0; i < 17; i++)
    {
        if(eout[i] < 10)
            eout[i] += 0x30;
        else if(eout[i] < 16)
            eout[i] += 0x57;
    }
    return(eout);
}

Я решил проблему с помощью malloc(), чтобы компилятор назначил память (т.е. вместо char eout[32] я использовал char * eout; eout = (char *) malloc (32);). Однако я подумал, что компилятор назначил разные области памяти, когда во время компиляции был определен размер массива char. Это неправильно?

Ответы [ 4 ]

3 голосов
/ 08 апреля 2010

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

ether_ntoa.c: In function ‘ether_ntoa’:
ether_ntoa.c:26: warning: function returns address of local variable

Обычный способ достичь желаемого результата - назначить вызывающего абонента , ответственного за распределение адресата. Например:

int ether_ntoa(unsigned char etheraddr[ETHER_ADDR_LEN], char *dest, size_t len)
{
    return snprintf(dest, len, "%02x:%02x:%02x:%02x:%02x:%02x",
        (unsigned)etheraddr[0],
        (unsigned)etheraddr[1],
        (unsigned)etheraddr[2],
        (unsigned)etheraddr[3],
        (unsigned)etheraddr[4],
        (unsigned)etheraddr[5]);
}

(обратите внимание также, что ваша процедура преобразования с кодированием вручную может быть заменена простым вызовом snprintf()). Вы бы назвали это так:

char eout[32];
ether_ntoa(etheraddr, eout, sizeof eout);
/* Converted address is now in eout */

Функция ether_ntoa() в Linux использует другой подход - она ​​объявляет буфер в функции как static. Если вы это сделаете, то ваш eout будет жить всю жизнь программы, поэтому вы можете вернуть указатель на него. Недостатком является то, что есть только один eout - каждый раз, когда вы звоните ether_ntoa, он будет перезаписывать предыдущий.

2 голосов
/ 08 апреля 2010

проблема с прежним способом в том, что концептуально некорректно вы не можете вернуть указатель на локальную переменную, локальные переменные bc живут в стеке у тебя два подхода 1) передать параметром буфер (eout) и его размер, а затем заполнить его внутри функции 2) выделить буфер (eout) внутри функции, и дать поручителю ответственность за освобождение буфера, как только он будет сделан с ним. С уважением

1 голос
/ 08 апреля 2010

Ваша проблема заключалась в том, что когда вы объявляете char eout[32] внутри функции, память, выделенная для этого массива, находится в стеке. После того, как ваша функция вернется, кадр стека выталкивается, и адрес памяти, ранее назначенный массиву, может быть переназначен другим переменным стека. Как вы уже поняли, правильный способ исправить это - malloc() пространство для массива, чтобы оно сохранялось после возврата функции.

0 голосов
/ 08 апреля 2010

Просто не забудьте free () ваш массив после того, как вы закончите, чтобы не было утечки памяти. Если вы используете C ++, вместо этого вы можете использовать boost :: shared_array , чтобы вам не приходилось вручную освобождать память. http://www.boost.org/doc/libs/1_42_0/libs/smart_ptr/shared_array.htm

...