2014-09-11 3 views
0

Это вопрос из курса на языке С. Кто-то хочет вернуть значение из функции указателем. Он присваивает адрес указателю result_ptr и печатает значение этого указателя.Значение возвращаемого указателя неожиданно изменяется после вызова функции

Когда нет линия А, Е() работает отлично: Е() печатает 3.

Однако, когда другая функция добавления() вызывается перед Е(), что-то не так происходит: Е() печатает 5.

Если линия а закомментирована и линии B, другой PRINTF функция(), является раскомментирована: Е() печатает 0.

Что именно происходит?

int *addition(int a, int b) { 
    int d = a + b; 
    int *c = &d; 
    return c; 
} 

int main(int argc, const char * argv[]) 
{ 
    int *result_ptr = addition(1, 2); 
    addition(2, 3); // Line A 
//  printf("Another line\n"); // Line B 
    printf("result = %d \n", *result_ptr); 
    return 0; 
} 
+1

Подсказка: вы возвращаете указатель на локальную переменную –

ответ

2

Когда функция вызывается, аргументы (в обратном порядке), обратный адрес и EBP вызывающего абонента (в котором хранится где функция возвращает после выполнения) помещаются в стек. Вызывающий устанавливает кадр стека, который хранит его локальные переменные и сохраняет содержимое трех регистров, EBX, ESI и EDI, если они изменены. Когда функция завершает выполнение, кадр открывается, и верх стека возвращается к высоте, где он был до вызова вызываемого.

В этом примере int * c объявляет локальную переменную-указатель, которая хранится в стеке в кадре вызываемого абонента. Возврат этого указателя возвращает адрес в фрейме стека. Поскольку последовательные вызовы add() вызывают идентичные распределения пространства стека, содержимое одного и того же адреса будет записано дважды. Вот почему второй вызов функции будет записывать 5, переписывая 3 из первого вызова функции: они изменяют значение в той же ячейке памяти. Однако при вызове printf() пространство стека используется для совершенно другого фрейма стека. Тогда в том же месте сохраняется неопределенное значение.

Чтобы избежать этого, было бы лучше вернуть указатель на место в куче, а не в стеке. Имейте в виду, чтобы освободить указатель после выделения памяти, чтобы избежать утечки.

int *addition(int a, int b) { 
    int *c = (int *) malloc(sizeof(int)); 
    *c = a + b; 
    return c; 
} 

// in main 
int *result_ptr=addition(1,2); 
printf("value = %d \n",result_ptr); 
free(result_ptr); 
result_ptr=0; 

Ссылка: http://www.csee.umbc.edu/~chang/cs313.s02/stack.shtml

Я буду рад видеть более четкие или различные объяснения этого вопроса.

+0

Это хорошее объяснение. Тем не менее, я считаю, что было бы очень не очень ясно, что возврат указателя на локальную переменную всегда считается неопределенным поведением и его следует избегать. (Для отличного рассмотрения темы см. [Основной ответ на вопрос, который указан как «возможно дубликат») (http://stackoverflow.com/a/6445794/2171689).) –

Смежные вопросы