Значение в объединении, вложенном в структуру, не обновляется

#c #struct #unions

Вопрос:

Я пишу реализацию стека на C, которая позволяет хранить данные любого типа. У меня есть это до сих пор:

 // stack.h
enum ELEMENT_TYPE {
    ELEMENT_CHAR,
    ELEMENT_INT,
    ELEMENT_DOUBLE,
    ELEMENT_FLOAT
};

typedef struct {
    enum ELEMENT_TYPE type;
    union {
        char val_c;
        int val_i;
        double val_d;
        float val_f;
    };
} Stack_Element;

typedef struct {
    unsigned top;
    unsigned capacity;
    Stack_Element* elements;
} Stack;
 
 Stack* stack_malloc(unsigned capacity) {
    Stack* stack = (Stack*)malloc(sizeof(Stack));
    stack->top = 0;
    stack->capacity = capacity;
    stack->elements = (Stack_Element*)malloc(sizeof(stack->elements) * capacity);
    return stack;
}

void stack_push(Stack* stack, enum ELEMENT_TYPE type, ...) {
    if (isFull(stack)) {
        return;
    }

    va_list ap;
    va_start(ap, type);

    switch(type) {
        case ELEMENT_CHAR:
            printf("Pushing char: %cn", (char) va_arg(ap, int));
            stack->elements[stack->top].val_c = (char) va_arg(ap, int); // issue
            printf("After pushing: %cn", stack->elements[stack->top].val_c);
            break;
        case ELEMENT_INT:
            printf("Pushing int: %dn", va_arg(ap, int));
            stack->elements[stack->top].val_i = va_arg(ap, int); // issue
            printf("After pushing: %dn", stack->elements[stack->top].val_i);
            break;
        case ELEMENT_DOUBLE:
            printf("Pushing double: %fn", va_arg(ap, double));
            stack->elements[stack->top].val_d = va_arg(ap, double); // issue
            printf("After pushing: %fn", stack->elements[stack->top].val_d);
            break;
        case ELEMENT_FLOAT:
            printf("Pushing float: %fn", (float) va_arg(ap, double));
            stack->elements[stack->top].val_f = (float) va_arg(ap, double); // issue
            printf("After pushing: %fn", stack->elements[stack->top].val_f);
            break;
    }

    stack->elements[stack->top].type = type;
    stack->top  ;
    va_end(ap);
}
 

Проблема, с которой я столкнулся, находится в очереди stack->elements[stack->top].val_? . В первом printf , где я печатаю Pushing X: %x , значение правильное, поэтому у меня будет что-то вроде Pushing int: 123 .

Но тогда выводом для второго printf является After pushing: 291176586 , или другое мусорное значение. Я сбит с толку, почему это происходит. Любая помощь будет признательна.

Комментарии:

1. malloc(sizeof(stack->elements) * capacity) это выделение неправильного размера. Чтобы избежать такого рода ошибок, используйте шаблон p = malloc(sizeof *p * N)

2. Изменено на malloc(sizeof *stack->elements * capacity) , но это не работает. Также пробовал sizeof stack->elements * capacity

3. @Sami Use stack->elements = malloc( capacity * sizeof *stack->elements);

4. Используйте правильную версию , не «пробуйте» — C не является языком проб и ошибок

5. @4386427 в чем разница? sizeof имеет более высокий приоритет, чем умножение.

Ответ №1:

Вы используете va_arg неправильно. На главной странице написано:

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

но вы получаете доступ к нему дважды. Измените свой код на:

         char tmp = (char) va_arg(ap, int);  // Save in tmp variable
        printf("Pushing char: %cn", tmp);
        stack->elements[stack->top].val_c = tmp;
        printf("After pushing: %cn", stack->elements[stack->top].val_c);
        break;
 

Кроме того, вы malloc ошибаетесь, как было указано в комментариях.

 stack->elements = (Stack_Element*)malloc(sizeof(stack->elements) * capacity);
 

должно быть

 stack->elements = malloc(sizeof(*stack->elements) * capacity);
                                ^
                                notice
 

Тем не менее, я предпочитаю порядок

 stack->elements = malloc(capacity * sizeof *stack->elements);
 

но он делает то же самое.

Комментарии:

1. Хороший улов! Может va_fetch быть, это было бы более точное название для va_arg .

Ответ №2:

 stack->elements = (Stack_Element*)malloc(sizeof(stack->elements) * capacity);
 

malloc неверно, stack->elements это указатель, длина которого равна 4.
должно быть

 stack->elements = (Stack_Element*)malloc(sizeof(Stack_Element) * capacity);
 

Комментарии:

1. Это ничего не добавляет к другому ответу. Также: это указатель, длина которого равна 4 . Это зависит от моей платформы (64 бита) — 8.