Variadic functions and constants

How exactly do function variables handle numeric constants? for example consider the following code:

myfunc(5, 0, 1, 2, 3, 4);

      

The function looks like this:

void myfunc(int count, ...)
{
}

      

Now, in order to iterate over single arguments with va_arg

, I need to know their sizes, for example. int

, short

, char

, float

, Etc. But what size should I take for numeric constants as I am using in the above code?

Tests have shown that just assuming that int

seems to work fine for them, so the compiler seems to be pushing them like int

, although these constants can also be represented in one char

or short

each.

However, I am looking for an explanation of the behavior I see. What is the standard type in C for passing numeric constants to variadic functions? Is this well-defined or is it compiler specific? Is there a difference between 32-bit and 64-bit architecture?

Thank!

+1


source to share


2 answers


I like Jonathan Leffler's answer , but I thought I'd write some technical details for anyone intending to write a portable library or something that provides an API with variadic functions and therefore needs to go into the details.

Variadic parameters fall under advertisements by default (C11 draft N1570 PDF, section 6.5.2.2 Function calls, clause 6):

... Integer promotions are executed for each argument, and arguments that are of type float are doubled. These are called default promotions.

[If] .. post-promotion argument types are not compatible with post-promotion parameter types, the behavior is undefined, except in the following cases:

  • one promoted type is a signed integer type, the other promoted type is a corresponding unsigned integer type, and this value is represented in both types;

  • both types are pointers to qualified or unqualified versions of a character type or void

Floating point constants are of type double

, unless they are suffixed with f

or f

(as in 1.0f

), in which case they are of type float

.

In C99 and C11, integer constants have a type int

if they fit into one; long

(AKA long int

) if they match; long long

(AKA long long int

) otherwise. Since many compilers accept an integer constant without a size suffix, this is a human error or typo, it is good practice to always include the suffix if the integer constant is not of type int

.

Integer constants can also have letter suffixes to indicate their type:

  • u

    or u

    forunsigned int

  • l

    or l

    forlong int

  • lu

    or ul

    or lu

    or ul

    or lu

    or lu

    or ul

    or ul

    forunsigned long int

  • ll

    or ll

    or ll

    or ll

    forlong long int

  • llu

    or llu

    (or ULL

    or any of their uppercase or lowercase variants) forunsigned long long int



For integer promotion rules, see section 6.3.1.1.

To summarize the default promotion rules for C11 (there are some additions over C89 and C99, but no significant changes):

  • float

    rises to double

  • All integer types whose values ​​can be represented int

    are promoted to int

    . (This includes both unsigned and signed char

    and short

    , and bit fields of types _Bool

    , int

    and lesser unsigned int

    bit fields.)

  • All integer types whose values ​​can be represented unsigned int

    (but not int

    ) are promoted to unsigned int

    . (This includes bitfields unsigned int

    that cannot be represented int

    (from CHAR_BIT * sizeof (unsigned int)

    bits, in other words) and typedef'd aliases unsigned int

    , but that is, I guess.)

  • Integer types that are at least equal int

    do not change. This includes the types long

    / long int

    , long long

    / long long int

    and size_t

    , for example.

There is one "getcha" in the rules I would like to specify: "signed in unsigned ok, unsigned signed by iffy":

  • If the argument is promoted to a signed integer type, but the function gets the value using the corresponding unsigned integer, the function gets the correct value using modulo arithmetic.

    That is, negative values ​​will be as if they had been incremented by (1 + the maximum representable value in an unsigned integer type), making them positive.

  • If the argument is promoted to an unsigned integer type, but the function gets a value using the corresponding signed integer type, and the value is represented in both cases, the function gets the correct value. If the value is not displayed in both cases, the behavior is implementation-defined.

    In practice, almost all architectures do the opposite of the above, that is, the resulting integer value corresponds to the unsigned value subtracted (1 + the largest representable unsigned integer value). I've heard some weird ones might signal an integer overflow or something weird like that, but I've never gotten my mittens on machines like this.

man 3 printf (considering the man man pages project) is quite informative if you compare the rules above with the printf specifiers. The example function make_message()

at the end (C99, C11 or POSIX required for vsnprintf()

) should also be of interest .

+3


source


When you write 1

it is a constant int

. There is no other type that the compiler can use. If an unvariant prototype exists for a function that requires a different type, the compiler converts the integer 1

to the appropriate type, but is itself 1

a constant int

. So in your example, all 6 arguments int

.

You must know the types of the arguments one way or another before the function being called processes them. With a family of functions, printf()

the format string tells you what to expect; similar to the family of functions scanf()

.

Note that the default transformations are applied to arguments that match the ellipsis of the variational function. For example given:

char c = '\007';
short s = 0xB0hD;
float f = 3.1415927;

      

call:



int variadic_function(const char *, ...);

      

through:

int rc = variadic_function("c s f", c, s, f);

      

actually converts like c

, and s

in int

, and f

- double

.

+1


source







All Articles