2

I am unable to understand the weird behaviour of the program. It throws a warning but the output is surprising. I have 3 files, file1.c, file2c, file1.h

file1.c :

#include "file1.h"

extern void func(void);

 int main(void)
{
func();
printf("%d\n",var);
return 0;
}

file2.c:

int var;

 void func(void)
{
var = 1;
}

and file1.h is :

#include<stdio.h>

extern double var;

When I compile and run, it shows a warning as expected but on printing prints 1. But how is it possible when I am changing the var declared in file2.c

4
  • What output are you expecting?
    – icktoofay
    Commented Apr 1, 2012 at 5:16
  • It should print garbage values inn't it. i haven't modified the value of extern var
    – Jatin
    Commented Apr 1, 2012 at 5:18
  • @Jatin in the func() you changed the var. isn't it?
    – llj098
    Commented Apr 1, 2012 at 5:22
  • Maybe stating the obvious, but extern void func(void); should be in file1.h and file2.c should include file1.h Commented Apr 1, 2012 at 16:08

2 Answers 2

3

You seem to have undefined behavior -- but one that is probably innocuous on many machines much of the time.

Since your extern declaration says var is a double, when it's actually an int, attempting to access it as a double causes undefined behavior.

In reality, however, in a typical case, a double will be eight bytes, where an int is four bytes. This means when you pass var to printf, 8 bytes of data will be pushed onto the stack (but starting from the four bytes that var actually occupies).

printf will then attempt to convert the first four bytes of that (which does correspond to the bytes of var) as an int, which does match the actual type of var. This is why the behavior will frequently be innocuous.

At the same time, if your compiler was doing bounds checking, your attempt at loading 8 bytes of a four-byte variable could easily lead to some sort of "out of bounds" exception, so your program wouldn't work right. It's just your misfortune that most C compilers don't do any bounds-checking.

It's also possible that (probably on a big-endian machine) instead of the bytes corresponding to var happening to be in the right place on the stack for printf to access them as an int, that the other four bytes that would make up a double will end up in that place, and it would print garbage.

Yet another possibility would be that the bit pattern of the 8 bytes would happen to correspond to some floating point value that would cause an exception as soon as you tried to access it as a floating point number. Given the (small) percentage of bit patterns that correspond to things like signaling NaN's, chances of that arising are fairly small though (and even the right bit pattern might not trigger anything, if the compiler just pushed 8 bytes on the stack and left it at that).

0
2

Just setting the type on an extern definition doesn't change the value stored in memory - it only changes how the value is interpreted. So a value of 0x0001 is what is stored in memory no matter how try to interpret it.

Once you've called func() you've stored the value 0x0001 in the location where a is stored. Then in main when a is passed to printf the value stored at location a plus the next 4 bytes (assuming 32bits) is pushed onto the stack. That is the value 0x0001xxxx is pushed onto the stack. But the %d treats the value passed in as an int and will read the first 4 bytes on the stack, which is 0x0001 so it will print 1.

Try changing your code as follows:

file1.c :

#include "file1.h"

extern void func(void);

int main(void)
{
    func();
    printf("%d %d\n",var);
    return 0;
}

file2.c:

int var;
int var2;

void func(void)
{
    var = 1;
    var2 = 2;
}

and file1.h is :

#include<stdio.h>

extern double var;

You will probably get the output 1 2 (although this depends on how the compiler stores the variables, but it's likely it will store them in adjacent locations)

4
  • 1
    Was that printf supposed to be printf("%d %d\n, var, var2); ? Commented Apr 1, 2012 at 16:06
  • @WilliamMorris - that would be exactly the same output as print("%d %d", var);, "1 2". With 2 %d's printf reads 2 32 bit values off of the stack and you're call push 2 64 bit values. printf will never read the values pushed on the stack by var2. The last parameter to printf is a variable argument list - C (at least in x86) handles that by reading directly off of the stack, it doesn't get a count of how many arguments were passed to it.
    – shf301
    Commented Apr 1, 2012 at 17:34
  • I was just puzzled by the %2\n format string. gcc says warning: unknown conversion type character 0xa in format for such a string - not surprising as it thinks the \n is a format specifier (or whatever). Commented Apr 1, 2012 at 20:07
  • @WilliamMorris - sorry didn't notice my typo - I thought you were asking a different question.
    – shf301
    Commented Apr 1, 2012 at 21:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.