Question

With curiosity of the definition and scope of typedef I have written below C code in 2 .c files:

main.c

#include <stdio.h>

int main()
{
    int a = 5, b = 6;
    printf("a = %d, b = %d\n", a, b);
    swap(&a, &b);
    printf("a = %d, b = %d\n", a, b);
}

swap.c

typedef T;

void swap(T* a, T* b)
{
    T t = *a;
    *a = *b;
    *b = t;
}

To my surprise, the code files could be compiled with Visual Studio C compiler (cl.exe /Tc main.c swap.c)

And the program runs correctly! From my understanding, typedef requires 2 parameters, but why this code compiles and runs?

For further analysis, in the main function, I declared another 2 float variables and try also to swap both after swapping the 2 integers, but this time it fails to compile (with cl.exe). What amazing is the code could be compiled and run with Tiny C (tcc.exe main.c swap.c), so it works like a template method!

Was it helpful?

Solution

Typedef is actually a declaration (it creates aliases for existing types), and is in no way limited to two 'parameters'. See Typedef Declarations (C) and Fundamental typedef operand syntax.

If you write typedef T;, you are declaring T to be an unspecified type (called "no specified type" in the C89 spec). It's a little (and only a very little, but conceptually this might help you) like #define X defines X but the preprocessor will replace it with the empty string (i.e. remove X).

So, you are typedefing T to be unspecified, which makes the arguments to your swap function of unspecified type.

What you are seeing here is that in C89 (but not C99, where it results in undefined behaviour - contrast ANSI 3.5.2 with ISOC99 6.7.2), unspecified types default to int, which is why your method works for integers but not floats in Visual Studio (presumably it disallows implicit integer typing by default). GCC will compile it with floats, though, provided you aren't using -Werror, which you probably should be.

I strongly suggest turning on some warnings: -Wall in gcc will spit out the following, among other things

swap.c:1:9: warning: type defaults to ‘int’ in declaration of ‘T’ [-Wimplicit-int]

The reason it "works" on floats is because floats and ints are probably the same size (32 bits) on your machine. Try it with double. Or char. Or short.

swap is really like this:

void swap(int *a, int *b)
{
    int t = *a;
    *a = *b;
    *b = t;
}

with you calling

swap((int*)&a, (int*)&b);

Try it and compare the results for yourself.


Edit: I just tried it in tcc. -Wall does not alert you to the implicit int typing, sadly.

OTHER TIPS

In C90 (which is what MSVC follows as a baseline when compiling C code), one of the possible type specifiers is (C90 6.5.2 "Type specifiers" - emphasis added):

  • int, signed, signed int, or no type specifiers

so if no type specifier is provided in a declaration (including a typedef) then the type defaults to int. this is commonly known as an "implicit int" declaration. Note that C99 removed support for implicit int (by default GCC only issues a warning for this when compiling in C99 mode).

Your typedef:

typedef T;

is equivalent to:

typedef int T;

So your swap() defintion is equivalent to:

void swap(int* a, int* b)
{
    int t = *a;
    *a = *b;
    *b = t;
}

As it happens, when calling a function that hasn't been declared or prototyped (as occurs when you call swap() in main.c), the compiler will apply default argument promotions to arithmetic arguments and assume the function returns an int. Your call to swap() passes two arguments of type int*, so no promotion occurs (they're pointer arguments, not arithmetic). That happens to be exactly what the definition for swap() expects, so the function call works (and is well defined behavior).

Now, the calling code expects swap() to return an int since no declaration was seen, and your swap() function doesn't return anything (void). That is undefined behavior, but in this case there's no apparent problem (though it's still a bug in your code). However, if you change the definition of swap() so that it returns int:

int swap(int* a, int* b)
{
    int t = *a;
    *a = *b;
    *b = t;
}

the undefined behavior goes away, even though swap() doesn't seem to return anything. Since nothing is done with the result at the call site, C90 permits the function to return with an expression. C90 allows this in order to support pre-standard code where there was no such thing as a void type.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top