Висячий указатель в C - PullRequest
1 голос
/ 13 марта 2011

Я написал программу на C с висящим указателем.

#include<stdio.h>

int *func(void)
{
    int num;
    num = 100;
    return &num;
}

int func1(void)
{
    int x,y,z;
    scanf("%d %d",&y,&z);
    x=y+z;
    return x;
}

int main(void)
{
    int *a = func();
    int b;
    b = func1();
    printf("%d\n",*a);
    return 0;
}

Я получаю вывод как 100 , даже если указатель болтается.

Я сделал одно изменение в вышеуказанной функции func1(). Вместо того, чтобы брать значения y и z из стандартного ввода, как в приведенной выше программе, теперь я присваиваю значение во время компиляции.

Я переопределил func1() следующим образом:

int func1(void)
{
    int x,y,z;
    y=100;
    z=100;
    x=y+z;
    return x;
}

Теперь вывод равен 200 .

Может кто-нибудь объяснить мне причину вышеупомянутых двух выходов?

Ответы [ 7 ]

16 голосов
/ 13 марта 2011

Неопределенное поведение означает, что может произойти все, в том числе и то, что вы ожидаете. В этом случае ваши переменные стека не были перезаписаны.

void func3() {
  int a=0, b=1, c=2;
}

Если вы включите вызов func3() между func1 и printf, вы получите другой результат.

РЕДАКТИРОВАТЬ: Что на самом деле происходит на некоторых платформах.

int *func(void)
{  
    int num;  
    num = 100;  
    return &num;  
}

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

Когда вы вызываете функцию, адрес возврата помещается в стек (в позицию 10), а указатель стека увеличивается до 14 (да, очень упрощенно). Затем переменная num создается в стеке в позиции 14, а указатель стека увеличивается до 18.

Когда вы возвращаетесь, вы возвращаете указатель на адрес 14 - адрес возврата извлекается из стека, а указатель стека возвращается к 10.

void func2() {
    int y = 1;
}

Здесь происходит то же самое. Возвращаемый адрес помещается в позицию, y создается в позиции 14, вы присваиваете 1 для y (записывает в адрес 14), вы возвращаетесь и складываете указатель обратно в позицию 10.

Теперь ваш старый int * возвратился из func точек на адрес 14, и последним изменением этого адреса было присвоение локальной переменной func2. Таким образом, у вас есть свисающий указатель (ничто над положением 10 в стеке не является действительным), который указывает на оставшееся значение от вызова к func2

4 голосов
/ 13 марта 2011

Это из-за того, как распределяется память.

После вызова func и возврата висящего указателя часть стека, в которой хранилась num, все еще имеет значение 100 (что вы и видите позже). Мы можем прийти к такому выводу, основываясь на наблюдаемом поведении.

После изменения похоже, что происходит то, что вызов func1 перезаписывает область памяти, на которую указывает a, с результатом добавления внутрь func1 (пространство стека, ранее использовавшееся для func, равно используется теперь func1), поэтому вы видите 200.

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

2 голосов
/ 13 марта 2011

Это неопределенное поведение. Он может корректно работать на вашем компьютере прямо сейчас, через 20 минут, может зависнуть через час и т. Д. Как только другой объект займет то же место в стеке, что и num, вы будете обречены !

1 голос
/ 13 марта 2011

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

1 голос
/ 13 марта 2011

С висячими указателями результат программы не определен. Это зависит от того, как используются стек и регистры. С разными компиляторами, разными версиями компилятора и разными настройками оптимизации вы получите другое поведение.

1 голос
/ 13 марта 2011

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

В частности, области памяти используются повторно на шанс * в func1.Результат зависит от компоновки стека, оптимизации компилятора, архитектуры, соглашений о вызовах и механизмов безопасности стека.

0 голосов
/ 21 июня 2012

Пожалуйста, изучите функции из базового C. Ваша концепция ошибочна ... main должно быть

int main(void)  
{  
    int *a = func();  
    int b;

    b = func1();  
    printf("%d\n%d",*a,func1());  
    return 0;  
}

Это выдаст 100 200

...