Thread: Control over variables inside a header file

  1. #16
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    MK27...

    The better test is to look at the value of x in each .c file...

    Using your current code structure... create a function that prints the value of x from the two.c file and print it from your main...
    Everything you've shown us so far is actually working on the value of X in two.c... not the one in main.

    main.h
    Code:
    int X;
    
    int getX();
    void initX();
    main.c
    Code:
    #include <stdio.h>
    #include "main.h"
    
    int main(void) {
         int i;
         X = 1234;    
    
         initX();
    
         for (i = 0; i<10; i++) {
             printf("%d  %d\n", getX(), X);	
              }
    
         return 0;
    }
    two.c

    Code:
    #include "main.h"
    
    void initX() {
    	X = 5;
    }
    
    int getX() { 
        X++;
    	return X; 
    }
    Last edited by CommonTater; 06-23-2011 at 10:46 AM.

  2. #17
    Registered User
    Join Date
    Nov 2010
    Location
    Long Beach, CA
    Posts
    5,909
    @Ayreon: Here is a good explanation of many of the problems with globals, though I wager this thread has brought many of them to light for you already. Minimize their use whenever possible. You may have managed to use global variables in header files in the past by using the keyword 'static', which means each copy is visible only to the translation unit (.c file) it's in.

    Defining a variable in a header file and including that header in several .c files means each .c file ends up with a global copy of that variable that will be visible by the entire program when linked. This doesn't cause problems with compilation proper (phase two of building), since that only produces disjoint object files, one for each .c file, each one with their own copy and unaware of other object files with same-named variables.

    The third phase of building your program is linking, which brings all the .o files along with any libraries (or references to shared libs) together to make your executable. If there's several instances of a global variable called x, the linker can't be sure of which one is being referred to in something like x+1. If you're wondering what the first phase of building a C program is, it's pre-processing, which handles all the #includes, #ifdef, #define, etc.

    If you really need global variables for something (I highly doubt you need them for your program), I recommend looking into a singleton pattern when possible. Make the variable a static global in one .c file, so no other files can see it. Provide a set of functions in that .c for accessing and manipulating that variable, and only list the prototypes for those functions in the header.

    @tabstop,MK27,CommonTater:
    The compiler (gcc at least) will produce one object file for each .c file, but since you have it compile all .c files straight to an executable instead of doing compilation proper in one stage, then linking in another, no .o files are produced and left in the current directory. It uses temporary files. The easy way to check is to insert a linker error in each .c file, so that it pukes when linking the object files:

    main.h
    Code:
    int X;
    
    int getX();
    void initX();
    main.c
    Code:
    #include <stdio.h>
    #include "main.h"
    
    int main(void) {
        int i;
    
        initX();
    
        for (i = 0; i<10; i++) {
            printf("%d\n", getX());
        }
        foo();
    
        return 0;
    }
    two.c
    Code:
    #include "main.h"
    
    void initX() {
        X = 5;
    }
    
    int getX() {
        X++;
        foo();
        return X;
    }

    Now, when I try to compile:
    Code:
    $ gcc -Wall main.c two.c
    main.c: In function ‘main’:
    main.c:13: warning: implicit declaration of function ‘foo’
    two.c: In function ‘getX’:
    two.c:9: warning: implicit declaration of function ‘foo’
    /tmp/ccd5Wtpt.o: In function `main':
    main.c:(.text+0x3b): undefined reference to `foo'
    /tmp/ccWxKcMk.o: In function `getX':
    two.c:(.text+0x23): undefined reference to `foo'
    collect2: ld returned 1 exit status
    Two different, temporary object files, one per .c file. It would be too much work for the compiler to some how try and figure out exactly what should and shouldn't be shared between the .c files at the compilation proper stage, since that's really the linker's job anyhow. It's much easier to just compile them separately to a temporary location and let the linker link them when the time comes. It's all behind the scenes though, and no trace is left in your local directory.

  3. #18
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    Well, I learned something new today... MK27 was correct about how the X in the header becomes a single variable... The good news is that since I've been using extern in my headers... I've never run into problems because of it.

    My misunderstanding seems to have come from the idea that since variables at page scope in a .c are not visible to other .c files... the ones in the header (included at compile time) would not be either...

    What can I say? Nobody knows everything...
    Last edited by CommonTater; 06-23-2011 at 11:41 AM.

  4. #19
    Registered User
    Join Date
    Mar 2009
    Posts
    344
    And even easier way to see what's going on with gcc is to add -save-temps to the command line. The result, as you'd expect, is two .o files (along with two .i and .s files).

    Note that this trick with defining a variable in a header file only works if you don't initialize it there. Change the line in the header to "int X = 5" and you'll get a linker error about multiple definitions for X. The fact that it is that touchy is a good hint you shouldn't use it in new code - it's probably just an artifact left around to be compatible with older code which relied on this. As others have said, put the definition in a .c file and an extern in a .h file for the rest of the files which need to access it.

  5. #20
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by CommonTater View Post
    MK27...
    The better test is to look at the value of x in each .c file...

    Everything you've shown us so far is actually working on the value of X in two.c... not the one in main.
    Yeah, my 2nd example was just an inversion of the first, I was in a bit of a rush to get out of here earlier.

    However, what I've said is true, although I realize now it's unorthodox and probably not good advice. Meaning I am just trying to prove myself correct out of ego As I said, I don't do this anymore -- I was assuming it's something a lot of beginners do, probably I am wrong about that. So I won't bring it up on the board again.

    Here's a version where we increment X alternately in main.c and two.c:

    main.h
    Code:
    int X;
    
    int getX();
    void initX();
    main.c
    Code:
    #include <stdio.h>
    #include "main.h"
    
    int main(void) {
    	int i;
    
    	initX();
    
    	for (i = 0; i<10; i++) {
    		printf("%d\n", getX());
    		X++;	// increment X in main.c
    	}
    
    	return 0;
    }
    two.c
    Code:
    #include "main.h"
    
    void initX() {
    	X = 5;
    }
    
    int getX() { 
    	X++;	// increment x in two.c
    	return X;    
    }
    Now, if the "normal" rules applied here, this would be incrementing two different variables. But when compiled this way:

    gcc main.c two.c

    As I said before, that is not what happens:

    [root~/C/tmp] ./a.out
    6
    8
    10
    12
    14
    16
    18
    20
    22
    24

    Proving there is only one X in the executable -- X in main.c refers to the same variable as X in two.c.

    Quote Originally Posted by KCfromNC View Post
    And even easier way to see what's going on with gcc is to add -save-temps to the command line. The result, as you'd expect, is two .o files (along with two .i and .s files).
    Yes, but the behaviour WRT X is what it is.

    Note that this trick with defining a variable in a header file only works if you don't initialize it there.
    Also true, but I wasn't suggesting it be initialized in an .h file.
    Last edited by MK27; 06-23-2011 at 02:15 PM.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  6. #21
    Astrophysics student Ayreon's Avatar
    Join Date
    Mar 2009
    Location
    Netherlands
    Posts
    79
    Quote Originally Posted by anduril462 View Post
    If you really need global variables for something (I highly doubt you need them for your program), I recommend looking into a singleton pattern when possible. Make the variable a static global in one .c file, so no other files can see it. Provide a set of functions in that .c for accessing and manipulating that variable, and only list the prototypes for those functions in the header.
    Wouldn't that mean that every time a function in one of the other .c files uses one of the variables, that it needs to call a function that creates this variable? That's exactly what I am trying to avoid. These variables are constant from the moment they are created, it makes no sense to reacalculate them every single time they are needed in a function.

    Bytheway I tried the #ifndef stuff but I still get this annoying error telling me I already defined phys_MO, I even used a seperate #ifndef for this variable. Why is it still giving me this error? This is my current constants.h:
    Code:
    #ifndef CONSTANTS_H
    #define CONSTANTS_H
    
    #ifndef VAR_PHYS_MO
    #define VAR_PHYS_MO
    extern double phys_MO = 4.94296E-6 ;
    #endif //VAR_PHYS_MO
    
    extern double c_array[6] ;
    
    extern double EOB_M, nu;
    extern double v_MECO, v_pole ;
    extern double omega0;
    
    #endif //CONSTANTS_H
    It shouldn't matter how many times I include this header file right? It shouldn't even be nessecary to use #ifndef anymore around the phys_MO variable. My main.c looks like so:

    Code:
    #include "constants.h"
    #include "constant_calculations.h"
    #include "initial_value_calculator.h"
    
    double EOB_M, nu, v_MECO, v_pole, c_array[6], omega0, phys_MO ;
    
    int main(){ etc...
    Nothing to see here, move along...

  7. #22
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by Ayreon
    Wouldn't that mean that every time a function in one of the other .c files uses one of the variables, that it needs to call a function that creates this variable? That's exactly what I am trying to avoid. These variables are constant from the moment they are created, it makes no sense to reacalculate them every single time they are needed in a function.
    No, it creates once then returns the value thereafter.
    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

  8. #23
    Astrophysics student Ayreon's Avatar
    Join Date
    Mar 2009
    Location
    Netherlands
    Posts
    79
    Quote Originally Posted by laserlight View Post
    No, it creates once then returns the value thereafter.
    Then where does this value go, so that all other files can see it?
    Nothing to see here, move along...

  9. #24
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by Ayreon
    Then where does this value go, so that all other files can see it?
    Well...
    Quote Originally Posted by anduril462
    Make the variable a static global in one .c file, so no other files can see it. Provide a set of functions in that .c for accessing and manipulating that variable, and only list the prototypes for those functions in the header.
    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

  10. #25
    Astrophysics student Ayreon's Avatar
    Join Date
    Mar 2009
    Location
    Netherlands
    Posts
    79
    So for example I would need a function that creates M, which is m1+m2, and also returns it. But then every time I call this function in stead of calling a global variable M, it would have to calculate M again. So this .c file would look like:

    Code:
    static M ;
    
    double m1=1,m2=1 ;
    
    double create_M(double m1,double m2){
      return m1+m2 ;
    }
    And that's where I get confused, I am not using M at all, so I still don't understand what you mean.
    Nothing to see here, move along...

  11. #26
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    The basic idea can be expressed along these lines:
    Code:
    static double M;
    
    double some_expensive_function(void);
    
    double get_M(void) {
        if (M == 0) {
            M = some_expensive_function();
        }
        return M;
    }
    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

  12. #27
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    Wait, does OP need a function to create the variable? I thought we were doing
    header:
    Code:
    extern double phys_MO;
    one C file:
    Code:
    double phys_MO = 4.29496E-6;
    (OP: Note how the header should look.)

    (EDIT: Oh, I see, we're trying to delete globals. Right.)

  13. #28
    Astrophysics student Ayreon's Avatar
    Join Date
    Mar 2009
    Location
    Netherlands
    Posts
    79
    @Laserlight: I tried this and it seems to work. I just got in trouble now with one of the variables which is an array.
    @tabstop: Tried this too, but must have had problems somewhere else for it did not work either.

    In the mean time I've spoken to someone and he suggested to use a structure. From the sound of what he told me, this is going to be the best approach and also avoids use of globals and what not.

    My intention was to "tidy up" my code, trying to avoid functions with a huge load of arguments, and preventing certain variables from being calculated over and over again. The structure method seems to be able to reduce the list of arguments in a function to just this structure itself and I only have to change the members of the structure once, all the way at the beginning of the program.
    So that's what I am trying to implement now.
    Nothing to see here, move along...

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. creating variables inside an if statement
    By nik in forum C++ Programming
    Replies: 3
    Last Post: 03-21-2011, 03:41 PM
  2. Replies: 30
    Last Post: 06-19-2006, 12:35 AM
  3. Replies: 5
    Last Post: 10-23-2005, 08:33 PM
  4. variables inside class help
    By Rune Hunter in forum C++ Programming
    Replies: 12
    Last Post: 10-02-2005, 09:15 AM
  5. Variables in a header file
    By Crossbow in forum C++ Programming
    Replies: 4
    Last Post: 03-29-2002, 11:54 AM

Tags for this Thread