Thread: Connecting and dealing with source files

  1. #1
    Registered User
    Join Date
    Aug 2009
    Posts
    24

    Connecting and dealing with source files

    I'm after a primer on how to deal with files. I want to learn things like: connecting .h with .c, how to call functions across different .c files, when/how often to include a file, whether a function can be declared in a completely different location to the definition as long as the files are connected somehow.

    I'm trying to dissect some prewritten code into multiple source files and I'm getting identifier errors.

  2. #2
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Let's start with some basic terms.

    Declaration:
    Basically, they are identifiers telling the compiler that things exist. Before calling a function or using a variable, the compiler must know of them*. Therefore, you need a declaration.

    Definition:
    The implementation of something. The code which contains the actual code of a function or a variable created in memory. Basically your algorithms.

    Now, let's move on to multiple source files.
    When you create functions (define them) in multiple source files, your code in other files cannot see them, because the compiler compiles each source file separately.
    So how do you call these functions then, if the compiler doesn't know about them? This is where declarations come in. When you declare the functions in a source file and put those in another source file, it will work. But there's a downside--you will need to do this in every source who will use these functions, so is there a better way?

    Yes, there is. Basically, we store these declarations in a common place, inside a common file called a header. This header is included (via #include) in each source file that uses those definitions that the declarations refer to.

    *) Strictly speaking, C does not require declarations of functions to call them, but it is still considered good practice since it will eliminate some mistakes.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  3. #3
    Registered User
    Join Date
    Aug 2009
    Posts
    24
    I can hardly believe it sounds so simple (wait, haven't tried it yet). Should every source file have its own header?

    Would it also be OK to declare the functions in the header file corresponding to the source file that the definition is in, then include that header in a general header which is further included in the other source files?

  4. #4
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    Quote Originally Posted by alanb View Post
    I can hardly believe it sounds so simple (wait, haven't tried it yet). Should every source file have its own header?
    Except your main source file (i.e., the one with main() in it), yes.
    Quote Originally Posted by alanb View Post
    Would it also be OK to declare the functions in the header file corresponding to the source file that the definition is in, then include that header in a general header which is further included in the other source files?
    ... Maybe. If I've got hold of what you're suggesting, then it's technically possible but often Frowned Upon -- the idea being you should not lie to people reading your code about what is required to make it work. If you only need functions from <special_unit_a.h>, then just use that instead of <entire_codebase.h>. If your code is such that you really expect people to need the entire suite, and not just bits and bobs, then making one massive header file is fine (although then you should ask yourself whether you really did a good job at splitting things up into individual headers).

  5. #5
    Registered User
    Join Date
    Aug 2009
    Posts
    24
    OK, I have some global variables (structs) that I want to share across two source files and I've moved their declarations to the header of one of these files instead of the main header.

    I've included the second header in the first file but I'm getting linker errors. It says the structs have been defined in the other file as well.

    Members are accessed in each file.

  6. #6
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    And, of course, big headers tend to slow down compiling (unless you're using precompiled headers).

    Quote Originally Posted by alanb View Post
    OK, I have some global variables (structs) that I want to share across two source files and I've moved their declarations to the header of one of these files instead of the main header.

    I've included the second header in the first file but I'm getting linker errors. It says the structs have been defined in the other file as well.

    Members are accessed in each file.
    This is due to the declaration/definition problem.
    If you define a variable in a header, then each source file will get a copy of that variable, and multiple global variables with the same name isn't allowed (plus they wouldn't really be global anymore, either).
    Remember my lection? You can define something somewhere and tell the compiler it exists via a declaration. And declarations goes into headers.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  7. #7
    Registered User
    Join Date
    Aug 2009
    Posts
    24
    I find that if I don't include the header file with the declarations into both source files (but just one), I get undeclared identifier errors. If I do, I get linker errors.

    These declarations are simply: Type and variable; They are long pointers and are used with the -> operator in each source file.
    Last edited by alanb; 08-27-2009 at 10:24 AM.

  8. #8
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    Quote Originally Posted by alanb View Post
    OK, I have some global variables (structs) that I want to share across two source files and I've moved their declarations to the header of one of these files instead of the main header.

    I've included the second header in the first file but I'm getting linker errors. It says the structs have been defined in the other file as well.

    Members are accessed in each file.
    So if this is a conscious design decision (i.e., you are doing this for a good reason, and not "I don't know any better"), then you will need to declare them in the header and define them in a source code file -- to declare a variable at file scope (which is what a global is) requires the word "extern".

  9. #9
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Oh yeah, sorry.
    To declare a variable (as opposed to define), you would have to prefix them with "extern".
    Type and Name will define a variable.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  10. #10
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    In more detail:

    Header files are simply copy-pasted to the point at which they are included by the preprocessor. So say your header file looks like this:
    Code:
    #ifndef INCLUSION_GUARD_H
    #define INCLUSION_GUARD_H
    
    int global_var;
    
    #endif
    If you include that header in multiple source files, the line "int global_var;" will appear multiple times. You'll be declaring the same global variable multiple times. Naturally, the linker gets confused when that happens.

    What you need to do is define the global variable in one source file only, and declare in the other source files that it occurs elsewhere by saying
    Code:
    extern int global_var;
    Of course, you could do just as I have said. But you'd have to type the "extern" declaration a lot, and there's an easier way: you can put the "extern" declaration right in the header file. C doesn't mind if you have code like this:
    Code:
    extern int global_var;
    /* ... */
    int global_var;
    It's like having a function prototype and then a function definition. You can do that. So you can include the header file in the implementation file, the file that actually say "int global_var;", without any problems.

    An example program:

    header.c
    Code:
    #include <stdio.h>
    #include "header.h"
    
    int global_var;
    
    void print_global_var(void) {
        printf("%d\n", global_var);
    }
    header.h
    Code:
    #ifndef INCLUSION_GUARD_H
    #define INCLUSION_GUARD_H
    
    extern int global_var;
    
    void print_global_var(void);
    
    #endif
    main.c
    Code:
    #include <stdio.h>
    #include "header.h"
    
    int main() {
        global_var = 42;
        print_global_var();
        return 0;
    }
    [edit] Wow, three posts while I was typing this up. Well, it still seems to be relevant. [/edit]
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  11. #11
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Quote Originally Posted by dwks View Post
    If you include that header in multiple source files, the line "int global_var;" will appear multiple times. You'll be declaring the same global variable multiple times. Naturally, the linker gets confused when that happens.
    Hang on. That's defining. It's important to separate these concepts, especially in these things.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  12. #12
    Registered User
    Join Date
    Aug 2009
    Posts
    24
    I made it,

    extern lpStruct var;
    lpStruct var;

    and included this in both source files. It gives: already defined in the second file. If I comment out the second line it gives 'unresolved external symbol'.

  13. #13
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    Hang on. That's defining. It's important to separate these concepts, especially in these things.
    Sigh. Yes. I tend to always use the word "declare" and never "define"; just a matter of inflexible, automatic habit.

    and included this in both source files. It gives: already defined in the second file. If I comment out the second line it gives 'unresolved external symbol'.
    You need the "extern" line in the header file, where it will appear in all files which include the header; and the non-extern line in one source file only. Otherwise, the same global variable will be defined (:P) multiple times.

    Incidentally, if you're using structures here, you'll want the structure definition (?) in the header file, so that you can state the "extern" line and have the compiler know what you're talking about.
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  14. #14
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Quote Originally Posted by dwks View Post
    Incidentally, if you're using structures here, you'll want the structure definition (?) in the header file, so that you can state the "extern" line and have the compiler know what you're talking about.
    This bugs me too. When we tell the compiler about how the struct is made up, we're not actually creating anything from it, but merely telling the compiler how it looks, so theoretically, to me, it should be a declaration. But then again, one could argue that a declaration would be only its name since it would tell the compiler that a struct of that name exists, and when we "populate" the struct's contents, it should be a definition, since it's the same as functions--the definition is where the actual contents of the function is.

    It could be seen both ways, but I still think they should be called declarations.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  15. #15
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    Well, this is a structure declaration.
    Code:
    struct something;
    That's not enough to declare instances of structures, of course, but it's good enough to allow function prototypes and pointers to the structure. So naturally the more complex and complete version
    Code:
    struct something {
        int else;
    };
    would be called a "definition".

    Of course, structures are different anyway. You're declaring (sigh, ignore my word choice here) a blueprint; you're creating a new type. You're saying, "this is what 'something's look like". It doesn't really fit into the same category as defining variables or functions.
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

Popular pages Recent additions subscribe to a feed