Thread: Variable access across multiple files

  1. #1
    Registered User
    Join Date
    Mar 2012
    Posts
    19

    Variable access across multiple files

    I was trying out programs based on extern and as i understand, this is helpful when accessing variables across multiple files having only one definition.
    But i tried a simple program as below without "extern" and thing seem to work when i expected it would fail during linking process

    Code:
    file5.c
    1 #include <stdio.h>
      2 #include "var.h"
      3 
      4 int main() {
      5 
      6     printf("\n File5.c a = %d", a);
      7     test();
      8     return 0;
      9 }
    Code:
    file6.c
     1 #include <stdio.h>
      2 #include "var.h"
      3 
      4 int test() {
      5     printf("\n File6.c a = %d",a);
      6 }
    Code:
    var.h
    int a;
    As i have included "var.h" in all header files without extern, "int a" would be included in both the .c file and during linking, compiler should have thrown a warning or error message but it compiles file without any issue. Shouldn't var.h have the following "extern int a"?



  2. #2
    - - - - - - - - oogabooga's Avatar
    Join Date
    Jan 2008
    Posts
    2,808
    You have coded it so that you have two separate "int a" variables, one in each file. To see this, try having test() set a to 999 and see if main() sees this change. It won't, proving they are separate variables.

    extern does not define a variable. It just declares it's name and type and implies that it will be available during the linking process from another object file. So changing your "int a" to "extern int a" will not work either, because you will not have defined a anywhwere, just declared its existence. Try it. The linker will complain.

    Extern works like this:
    Code:
    /* main.c */
    #include <stdio.h>
    
    int my_extern;  /* defined in one file without extern */
    
    int main(void) {
        my_extern = 1;
        printf("Before: %d\n", my_extern);
        add_one();
        printf("After: %d\n", my_extern);
        return 0;
    }
    
    /* other.c */
    #include <stdio.h>
    
    extern int my_extern; /* declared in other files with extern */
    
    int add_one(void) {
        my_extern++;
    }
    The cost of software maintenance increases with the square of the programmer's creativity. - Robert D. Bliss

  3. #3
    Registered User
    Join Date
    Mar 2012
    Posts
    19
    When we include var.h, compiler would replace with "int a" in each file and as the scope is not static, wouldn't compiler crib when linking as it finds multiple reference to "int a"?

    I modify the program as follows

    Code:
    file5.c
    1 #include <stdio.h>
      2 
      3 int a;
      4 
      5 int main()
      6 {
      7     printf("\n a = %d", a);
      8     test();
      9 }
    Code:
    file6.c
     1 #include <stdio.h>
      2 
      3 int a = 100;
      4 void test()
      5 {
      6     printf("\n test : a = %d", a);
      7     }
      8
    Even here, i see value of "int a" in both the files are same.

  4. #4
    Hurry Slowly vart's Avatar
    Join Date
    Oct 2006
    Location
    Rishon LeZion, Israel
    Posts
    6,788
    I do not understand why you want to use globals at all. Better learn how properly to pass parameters to your functions and drop whole globals business till much later
    All problems in computer science can be solved by another level of indirection,
    except for the problem of too many layers of indirection.
    – David J. Wheeler

  5. #5
    - - - - - - - - oogabooga's Avatar
    Join Date
    Jan 2008
    Posts
    2,808
    Quote Originally Posted by pkumarn View Post
    Even here, i see value of "int a" in both the files are same.
    Yeah, you're right.

    Apparently the compiler (gcc at least, even with full warnings) doesn't complain as long as you only initialize one of them. It basically assumes that the other one was meant to be declared extern. That seems wrong and I really don't know what to say about it.

    If, however, you assign both a's an initial value, it will complain.

    But the proper way to do this is to declare one of them as extern.

    And if you want them to be separate variables, you have to declare (at least one of) them to be static.
    Last edited by oogabooga; 09-07-2013 at 01:39 PM. Reason: fixed wording
    The cost of software maintenance increases with the square of the programmer's creativity. - Robert D. Bliss

  6. #6
    Registered User
    Join Date
    Apr 2013
    Posts
    1,658
    Variables declared outside of functions are global unless they are prefixed by static, in which case they are only global to the current source file. In the examples above, the two instances of the global "int a" in the two source files were handled by the linker. Some linkers could complain about multiple instances of a global variable in multiple object files. GCC might do this if there is a parameter to turn up warnings during the link step.

  7. #7
    Registered User
    Join Date
    Mar 2010
    Posts
    583
    Quote Originally Posted by oogabooga View Post
    Apparently the compiler (gcc at least, even with full warnings) doesn't complain as long as you only initialize one of them. It basically assumes that the other one was meant to be declared extern. That seems wrong and I really don't know what to say about it.
    I was surprised by this too, so I looked it up. Apparently it's a "tentative definition".

    Quote Originally Posted by C11 draft standard
    6.9.2 External object definitions

    1 If the declaration of an identifier for an object has file scope and an initializer, the
    declaration is an external definition for the identifier.

    2 A declaration of an identifier for an object that has file scope without an initializer, and
    without a storage-class specifier or with the storage-class specifier static, constitutes a
    tentative definition. If a translation unit contains one or more tentative definitions for an
    identifier, and the translation unit contains no external definition for that identifier, then
    the behavior is exactly as if the translation unit contains a file scope declaration of that
    identifier, with the composite type as of the end of the translation unit, with an initializer
    equal to 0.
    So it's not quite "extern a". It will allocate storage for a if there is no other definition of a. No amount of "-std=c90 -ansi -pedantic" could make GCC complain about this, so I don't think it's a recent change. I had no idea you could do this though - oogabooga's original explanation was how I thought it worked! You can repeat as many of these tentative definitions as you want.

    I compiled the source files separately from the link step to have a look at how this is handled. Each object has a definition of 'a', but instead of an index to a section, it has SHN_COMMON (see Symbol Table). Then the linker merge together all the definitions, with the non-SHN_COMMON one 'winning' if it is present.

    This is a completely C concept and doesn't exist in C++.

    My recommendation would be to declare a as 'extern' in the header file, and provide a definition somewhere else. If you plan to have lots of globals, create another source file just for their definitions, globs.c or similar. If there aren't many and they logically tie up to one of your existing source files, define it in there.

    My reasoning is really just consistency with contents of other header files. Header files are typically used to provide access to functions and data defined in other source files. I don't want to introduce a different situation here. Suppose I needed to initialise 'a'. I'd have to do so in a source file. Fine. Then suppose I accidentally comment out or remove that initialisation. At that point, I would not want the header to quietly recreate 'a' for me, I'd want a link error.

    I also wouldn't use this because it's illegal in C++, and I tend to use a bit of C++ in a lot of projects.

    It's legal and well defined, so use it by all means!


    edited to add. Just to rub it in a bit. This code compiles and results in one 'a':

    Code:
    #include <stdio.h>
    
    int a;
    int a;
    int a;
    int a;
    int a;
    int a;
    int a;
    int a;
    int a;
    
    int main(void)
    {
       printf("main: %d\n", a);
    }
    Last edited by smokeyangel; 09-07-2013 at 03:18 PM.

  8. #8
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by smokeyangel
    No amount of "-std=c90 -ansi -pedantic" could make GCC complain about this, so I don't think it's a recent change.
    Yes, the notion of a tentative definition has been around since C89/C90, maybe even earlier.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  9. #9
    Registered User MutantJohn's Avatar
    Join Date
    Feb 2013
    Posts
    2,665
    My recommendation would be to declare a as 'extern' in the header file, and provide a definition somewhere else. If you plan to have lots of globals, create another source file just for their definitions, globs.c or similar. If there aren't many and they logically tie up to one of your existing source files, define it in there.
    I highly recommend this as I do something similar and it works out great.

    I define in my structures.h file,
    Code:
    extern int knots;
    and define it in my main.c with
    Code:
    int knots = 1; // or is it just knots? I think you have to declare it as an int
    and no matter where I am in my code, just so long as the code has a .o file shared with main.o and has structures.h included, I can do what I will to knots.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Using 1 ofstream variable to write multiple output files
    By Destro09 in forum C++ Programming
    Replies: 7
    Last Post: 03-17-2012, 06:11 AM
  2. Trying to use argv to access multiple files
    By DonFord81 in forum C Programming
    Replies: 12
    Last Post: 04-03-2009, 09:32 AM
  3. Multiple Source Files, make files, scope, include
    By thetinman in forum C++ Programming
    Replies: 13
    Last Post: 11-05-2008, 11:37 PM
  4. Replies: 1
    Last Post: 05-01-2003, 02:52 PM
  5. How to Access a Function from Multiple Files?
    By Unregistered in forum C++ Programming
    Replies: 7
    Last Post: 06-08-2002, 06:24 PM

Tags for this Thread