1

I have defined a variable in one file and declared it in another file using the extern keyword. but i have declared it with different datatype..

file1.c  
    char i;
    main()  
    {   
        foo();  
    }

file2.c

void foo()  
{  
    extern int i;  
    i = 130;  
    printf("%d", i);  
}

In the above program, memory is allocated for 'i' in the main function as char datatype. And the answer after executing should be negative(-127). But it prints 130. Whatever the value assigned to 'i' in the foo() function is printed not only 130.

Raghu Srikanth Reddy
  • 2,703
  • 33
  • 29
  • 42

7 Answers7

6

"I have defined a variable in one file and declared it in another file using the extern keyword". That is just not true. char i and in main is not in any way related to int i in foo.

The variable you define as char i inside main has no linkage. It cannot be referred to by name from anywhere else in the program besides that main function. There's simply no way to do it in C. It is a local variable.

Your extern int i; declaration inside foo has absolutely no relation to that local char i; variable from main. It declares a completely different, independent variable i. The extern int i declaration in foo refers to a variable i with external linkage, i.e to a global variable i. You are still required to define that i somewhere. That i will have to be defined at file scope (since this is the only way to define a variable with external linkage). If you don't define it your program will not compile.

Entities with external linkage and entities with no linkage live in completely separate worlds. They know nothing about each other and never interact or interfere with each other.

As I said above, the code you posted in your question simply won't compile. The linker will tell you that you forgot to define the int i variable with external linkage that which you declared in used in foo. Yet, your post suggests that the code compiled successfully. This would simply mean that the code you posted is either inaccurate or incomplete. If the code is accurate, then somewhere else in your code you also defined a global variable int i. This is the global variable your foo works with, while the char i in main has absolutely nothing to do with anything.


What you have now, after your edit, is an invalid program again, albeit for a different reason. You defined a global variable char i in file1.c and then you attempted to link to it in file2.c. However, in file2.c you lied to the compiler by telling it that the global variable i has type int, while in reality it has type char.

The behavior of such program is undefined. It is illegal to use inconsistent types when declaring an entity with external linkage. If you declared a global variable of type char in one translation unit, you are not allowed to declare the same global variable with any other type in other translation units.

Some compilers might catch your error and refuse to compile the program. Most C compilers will not catch this error though. Instead the will produce a program that causes undefined behavior. This is what you got in your case. In your case that undefined behavior happened to manifest itself by printing 130 after you assigned 130 to your variable. Why you suddenly find it strange I don't know. Why you expect it to print -127 I don't know either.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
2

Analysis — What is wrong with the code

In your code:

file1.c

int main(void)  
{  
    char i;   // Warning: unused variable
    foo();  
}

file2.c

void foo(void)  
{  
    extern int i;  
    i = 130;  
    printf("%d", i);  
}

The variable i in main() is wholly unrelated to the i referred to in foo(). The variable i in main() is strictly local to main() and is not passed to foo(); foo() is unable to see that i.

On Mac OS X 10.7.5 with GCC 4.7.1, the files compile separately, but they can't be linked because there is no definition for variable i.

If we move the variable i outside of main(), then the program links:

file2.h

extern void foo(void);

file1.c

#include <stdio.h>
#include "file2.h"

char i = 'A';

int main(void)
{
    printf("i = %d (%c)\n", i, i);
    foo();
    printf("i = %d (%c)\n", i, i);
    return(0);
}

file2.c

#include "file2.h"
#include <stdio.h>

void foo(void)
{
    extern int i;
    i = 130;
    printf("i = %d (%c)\n", i, i);
}

This now links and runs, but it invokes undefined behaviour. The linker does not notice that the size of i should be sizeof(int) but is sizeof(char). The assignment in foo(), though, goes trampling out of bounds of the i that is allocated. It doesn't cause visible damage in this program, but that's pure luck.

When I run it on a little-endian machine (Intel x86/64), the output is:

i = 65 (A)
i = 130 (?)
i = -126 (?)

When I ran it on a big-endian machine (SPARC), I got a different result (which isn't very surprising; the behaviour is undefined, so any result is valid):

i = 65 (A)
i = 130 (?)
i = 0 ()

The most significant byte of the 4-byte integer was written over the only byte of the 1-byte character, hence the zero in the third line out output. Note too that it was lucky that the character was allocated an address that was a multiple of 4 bytes; that was not guaranteed and a misaligned address for an int would have caused a bus error (though experimentation suggests that it doesn't happen on SPARC even when i is forced onto an odd address; I'm not sure what is happening).

Synthesis — Correct Handling of extern

The correct way to handle this is to avoid writing external variable declarations such as extern int i; in any source file; such declarations should only appear in a header that is used everywhere the variable needs to be used.

file2.h

extern char i;
extern void foo(void);

With that change in place (and the corresponding extern int i; removed), then the output is self-consistent:

i = 65 (A)
i = -126 (?)
i = -126 (?)

Note the role of the header in keeping both file1.c and file2.c in line with each other. It ensures that the definition in file1.c matches the declaration in file2.h, and the use of file2.h in file2.c ensures that the declaration used there is correct, too. See also What are extern variables in C.

Community
  • 1
  • 1
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
1

The extern declaration inside foo has absolutely no relation to that local char variable.

As far as I know, extern can be put in front of a variable declaration. Perhaps this is a bit more confusing. Doing that tells the compiler you want to share the variable between different .c files. However, there has to be one .c file where the variable is declared without the extern in front of it.

Rahul Tripathi
  • 168,305
  • 31
  • 280
  • 331
0

Note: You're example wouldn't work anyway, as extern has no effect on local variables.

extern is a way to tell the compiler some variable you are using is implemented in another object file that you promise that you will link at link time. It is often used in library header files that involve global variables to tell the compiler that the variable is actually implemented in the library file.

Ex: file1.c:

int thing;
int bork(int val) {
  return thing += val
}

file2.c

extern int thing
int main() {
  thing + 2;
}
Linuxios
  • 34,849
  • 13
  • 91
  • 116
0

In the case when i is declared in one file as global variable then only it has external linkage.

Linker only looks for the symbol i which will be resolved at link time ,but types are not resolved by linker so I think it's Undefined Behavior when i is of different type and defind as global variable in one file and extern declaration is done in other file.

Omkant
  • 9,018
  • 8
  • 39
  • 59
-1

In file1.c, i is defined (and declared implicitly) globally.

char i;
main()  
{    
    foo();  
}

file2.c extern is used with a variable i, it’s only declared not defined.

extern char i; 
void foo()  
{  
   printf("%d", i);  
}
Sunil Bojanapally
  • 12,528
  • 4
  • 33
  • 46
-1

You are LYING to the compiler about the size of i. The compiler goes by what you have told it in foo, so you are getting the integer representation. Unfortunately, it won't really work reliably, because you have also written three bytes beyond your char i;

If you add char j = 11; and then print j in main after you call foo();, you'll notice that j is not 11 any more.

Lying to compilers is a bad idea. You get found out sooner or later!

Mats Petersson
  • 126,704
  • 14
  • 140
  • 227