Поведение нечетного указателя NULL - PullRequest
1 голос
/ 24 ноября 2011

Я пытаюсь реализовать стек, используя связанный список.Мой конструктор стека createStack() создает пустой (фиктивный) Element и возвращает двойной указатель на этот элемент (начало стека).Мой метод push() проверяет наличие в стеке фиктивного элемента;если он это делает, он заполняет пустышку и возвращает, в противном случае он выделяет память для нового элемента и выполняет необходимые обновления указателя.

Проблема, с которой я столкнулся, заключается в том, что мой указатель *stack->next явно указывает на NULL (0x0), поскольку ондолжен, а затем через две строки он не равен NULL (0x17), но каким-то образом проходит тест NULL.Внутри вызова push он снова равен (0x17), но на этот раз он не проходит тест NULL, как и должно быть.

Итак, мой вопрос, что, черт возьми, происходит с этим указателем?Как / почему он изменился с (0x0) на (0x17), и если он равен (0x17), как прошел тест ==NULL ??

//main.c
int main () {

    struct Element **stack;

    stack = createStack();

    printf("stack: %p\n", stack );

    printf("*stack->next: %p\n", (*stack)->next );

    if ( (*stack)->next == NULL )
        printf("yes the pointer is null\n" );

    printf("*stack->next: %p\n", (*stack)->next );

    if ( (*stack)->next == NULL )
        printf("yes the pointer is null\n" );

    push ( stack, 1000 );

//stack.c

struct Element {
    int value;
    struct Element *next;
};

int push ( struct Element **stack, int el ) {

    if ( (*stack)->next == NULL) {
        // first element, fill dummy element and return
        printf("first value: %i !", el);
        (*stack)->value = el;
        return 1;
    }

    printf("the pointer is not null\n");

    struct Element *newElement = malloc( sizeof( struct Element ) );

    if ( !newElement )
        return -1;

    newElement->value = el;

    //add element to front of list
    newElement->next = *stack;

    //update pointer to new first element
    *stack = newElement;

    return 1;
}

struct Element** createStack() { 

    struct Element *dummy = malloc( sizeof( struct Element ) );

    if (dummy == NULL )
        printf("malloc failed...");

    dummy->value = 99;
    dummy->next = NULL;

    struct Element **stack;

    stack = &dummy;

    return stack;
}

Приведенный выше код дает следующий вывод:

stack: 0x7fff6c385ba8
*stack->next: 0x0
yes the pointer is null
*stack->next: 0x17
yes the pointer is null
the pointer is not null

Ответы [ 3 ]

3 голосов
/ 24 ноября 2011

Забудьте на мгновение, что вы работаете с указателями и указателями на указатели, и предположим, что ваша подпрограмма createStack() выглядела так:

int *createInt() {
    int dummy = 1;
    return &dummy;
}

Функция выделяет (временное) пространство в стеке для локальной переменной dummy, присваивает ей значение и затем возвращает указатель на него. Это именно то, что делает ваш createStack(), за исключением того, что ваш dummy оказывается более сложным типом данных.

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

0 голосов
/ 24 ноября 2011

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

struct Element** createStack() {

        struct Element *dummy = malloc( sizeof( struct Element ) );    
        ...
        struct Element **stack;    
        stack = &dummy;    
        return stack;
}

Вместо этого вы можете просто указать указатель на struct Element в main и вернуть указатель на вновь созданный узел из функции createStack:

struct Element *stack = createStack();

И передать адрес указателя stack в функцию ush:

push ( &stack, 1000 );
0 голосов
/ 24 ноября 2011

Переменная dummy в createStack() перестает существовать, когда эта функция возвращается, поэтому указатель, который вы возвращаете, указывает на переменную, которая больше не существует.

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

Вы можете исправить код, изменив createStack() для возврата значения struct Element *:

struct Element *createStack(void)
{  
    struct Element *dummy = malloc( sizeof( struct Element ) );

    if (dummy == NULL )
        printf("malloc failed...");
    else {
        dummy->value = 99;
        dummy->next = NULL;
    }

    return dummy;
}

и изменение main() для соответствия (push() может остаться без изменений):

int main ()
{
    struct Element *stack;

    stack = createStack();

    printf("stack: %p\n", stack );

    printf("stack->next: %p\n", stack->next );

    if ( stack->next == NULL )
        printf("yes the pointer is null\n" );

    printf("stack->next: %p\n", stack->next );

    if ( stack->next == NULL )
        printf("yes the pointer is null\n" );

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