macabeus
macabeus

Reputation: 4572

Error when pass "enum" in a function with variable arguments

I'm reading the book Head First C and I am the part about the variable arguments.

I wrote the following code:

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

enum drink {
    MUDSLIDE, FUZZY_NAVEL, MONKEY_GLAND, ZOMBIE
};

double price(enum drink d) {
    switch(d) {
    case MUDSLIDE:
        return 6.79;
    case FUZZY_NAVEL:
        return 5.31;
    case MONKEY_GLAND:
        return 4.82;
    case ZOMBIE:
        return 5.89;
    }
    return 0;
}

double calc(int args, ...) {
    double total = 0;

    va_list ap;
    va_start(ap, args);

    int i;
    for (i = 0; i < args; i++) {
        int currentDrink = va_arg(ap, int);
        total += price((drink) currentDrink);
    }

    va_end(ap);
    return total;
}

int main() {
    printf("Price is %.2f\n", calc(2, MONKEY_GLAND, MUDSLIDE));
    return 0;
}

The code compiles and works perfectly.

But... There are two different lines of my solution with the book.

My:

int currentDrink = va_arg(ap, int);
total += price((drink) currentDrink);

Book:

enum drink currentDrink = va_arg(ap, enum drink);
total += price(currentDrink);

I tried to use the solution proposed in the book, but the error during execution and reports a warning: 'drink' is promoted to 'int' when passed through '...'

The book is used gcc compiler on linux. I am using gcc on Windows.

Question: What is the reason I was unable to compile the code proposed in the book?

Edit There configured wrong. Was using a C++ compiler was thinking of using a C. But the question remains: why in C++ results in a warning and error in the execution?

Upvotes: 2

Views: 5927

Answers (1)

mafso
mafso

Reputation: 5543

Variadic arguments to functions are subject to a conversion called default argument promotions: Everything with a conversion rank smaller than that of int gets promoted to int before passed to a variadic function. And an enum has a smaller conversion rank (and may be represented as a short or something else in your case). So what your callee sees is an int and must fetch it by va_arg as such.

C99 7.15.1.1 p.2 (emphasis mine) about va_arg:

[…] If there is no actual next argument, or if type is not compatible with the type of the actual next argument (as promoted according to the default argument promotions), the behavior is undefined, except for the following cases:

  • one type is a signed integer type, the other type is the corresponding unsigned integer type, and the value is representable in both types;
  • [sth. about pointer types]

And 6.7.2.2, p.4:

Each enumerated type shall be compatible with char, a signed integer type, or an unsigned integer type. The choice of type is implementation-defined, […]

So, the va_arg(ap, enum drink) version has no defined bahavior (as far as the C standard is concerned). At least, if the compiler doesn't specify to always use int for enums. Hence the warning from gcc.

Some coding-guidelines say to avoid enum usage for types entirely, using int everywhere and only define enum constants:

enum {
    MUDSLIDE, FUZZY_NAVEL, MONKEY_GLAND, ZOMBIE
};

and

double price(int d);

HTH

Upvotes: 7

Related Questions