Thread: How to program properly

  1. #1
    Registered User
    Join Date
    Feb 2011
    Posts
    144

    How to program properly

    Hi all. I'm trying to learn C99. A few questions:

    1. When I write code that interacts with the operating system a fair bit, I find that the number of possible error conditions grows exponentially with each call. Every time I call a system function, I have to gracefully handle all possible errors. When people write code, do they typically write error-handling code for each function call at the time, or do they make it work assuming-no-errors and then go back and write error handling later?

    2. If my code has multiple .c source files and multiple .h files, and it has global variables, where are those globals typically defined? In a .c file or in a .h file? How are they typically made known to the other files? Using 'extern' in a .c file or in a .h file? Some of the talk of 'tentative definition' in K&R 2nd edition has confused me. What's that about?

    Richard (TIA)

  2. #2
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,659
    1. Write good error handling (or at the very least, good reporting) from the outset.
    In a large program, ignoring errors just means the error propagates to some other part of the program (probably written by someone else) and the error shows up as something completely weird. This (if not understood immediately) results in hours/days of debugging effort to recover from a situation that would have taken seconds to avoid.

    2. You should avoid global variables.
    But if you're going to have them, put them in the same file as main(), and have a "globals.h" which contains all the extern declarations.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  3. #3
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    Salem's advice is good as always so you should consider this as being "in addition to" his comments...

    1) Error handling is essential. Nobody's doing it for you in the background and it has to come from someplace.

    That said: There are certain functions that "never fail"; for example: in Windows the CreateWindow() api is extremely reliable so it is relatively safe to simply store it's return value (A window handle) and trust it. But there are other functions that are very likely to fail such as disk I/O thus you shold always check the return value and be ready to deal with any errors that result. My rule is: Write to avoid error conditions and trap those conditions you can't avoid.

    This turns into a balancing game... If you error checked every single last possibility your code would become so bloated and so slow as to be less than worthy... so a big part of the art is finding a reasonable balance.

    2) Yes, always avoid global variables whenever possible. However; in variance to Salem's advice about putting them in your main.c file... I generally define them in the source file where they're most relevent... For example: I often globalize my programs user settings. I will place the originals (non-extern ones) in the source file that edits loads and saves the settings, the extern version will then be placed in global.h along with a notation of where the originals are.

  4. #4
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Unless the error is something that's A) expected from time to time and B) easy to recover from, just report the error and terminate.

    Your program is not responsible for solving everything that could possibly go wrong on an entire system. Sort of like how you are not responsible for eliminating all hunger in Africa.

    Now, if you're getting errors because your program has a bug somewhere, then it's obviously your problem.
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  5. #5
    Third Eye Babkockdood's Avatar
    Join Date
    Apr 2010
    Posts
    352
    1. You should have some kind of expected output. Write an else statement for anything other than the expected output. If the else statement ends up being executed, then you know you're doing something wrong, or the user input something funky.

    2. I usually declare global variables in a header file. I never had to use the extern keyword. If you're doing a big project, you should write an initialisation function that assigns default values to global variables, then call it at the beginning of main.
    Quote Originally Posted by The Jargon File
    Microsoft Windows - A thirty-two bit extension and graphical shell to a sixteen-bit patch to an eight-bit operating system originally coded for a four-bit microprocessor which was written by a two-bit company that can't stand one bit of competition.

  6. #6
    Registered User
    Join Date
    Nov 2010
    Location
    Long Beach, CA
    Posts
    5,909
    Quote Originally Posted by Babkockdood View Post
    2. I usually declare global variables in a header file. I never had to use the extern keyword. If you're doing a big project, you should write an initialisation function that assigns default values to global variables, then call it at the beginning of main.
    You should not put variable definitions in header files. That can cause problems when you have a project that spans several .c files. Each .c file that includes the global header file gets it's own copy of that global variable. You will end up with several object files, each containing globals with the same name. When you try to put all of those together into one final executable/library, the linker wont know what to do with the 42 different globals all named foo. Put all global variable definitions in a .c file and use a .h file with the extern keyword to "share" these with other .c files.

  7. #7
    Third Eye Babkockdood's Avatar
    Join Date
    Apr 2010
    Posts
    352
    Quote Originally Posted by anduril462 View Post
    You should not put variable definitions in header files. That can cause problems when you have a project that spans several .c files. Each .c file that includes the global header file gets it's own copy of that global variable. You will end up with several object files, each containing globals with the same name. When you try to put all of those together into one final executable/library, the linker wont know what to do with the 42 different globals all named foo. Put all global variable definitions in a .c file and use a .h file with the extern keyword to "share" these with other .c files.
    I've been doing that for months, and it always works how I want it to. Maybe I just have a magic compiler.
    Quote Originally Posted by The Jargon File
    Microsoft Windows - A thirty-two bit extension and graphical shell to a sixteen-bit patch to an eight-bit operating system originally coded for a four-bit microprocessor which was written by a two-bit company that can't stand one bit of competition.

  8. #8
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    Quote Originally Posted by Babkockdood View Post
    I've been doing that for months, and it always works how I want it to. Maybe I just have a magic compiler.
    Yes, your way will work... so long as the actual variable definition is in your header **and no place else**... What you're doing is creating header files with actual data in them, not the best form, but it does work.

  9. #9
    Registered User
    Join Date
    Nov 2010
    Location
    Long Beach, CA
    Posts
    5,909
    Quote Originally Posted by Babkockdood View Post
    I've been doing that for months, and it always works how I want it to. Maybe I just have a magic compiler.
    I think "magic" is a euphemism for not behaving well. The following is my guess at what you're doing that "works", but it failed on my system (Linux/GCC):
    Code:
    // globals.h
    #ifndef globals_h__
    #define globals_h__
    
    int x = 3;  // no extern means foo.c and bar.c will both have their own x
    
    extern void bar(void);
    
    #endif  // globals_h__
    
    
    // foo.c
    #include <stdio.h>
    #include "globals.h"
    
    int main(void)
    {
        printf("main(): x: %d\n", x);
        x = 17;  // which x? there are two global x's, one in foo.c and one in bar.c
        printf("main(): x: %d\n", x);
        bar();
        printf("main(): x: %d\n", x);
    
        return 0;
    }
    
    
    // bar.c
    #include <stdio.h>
    #include "globals.h"
    
    void bar(void)
    {
        x = 42;  // which x? there are two global x's, one in foo.c and one in bar.c
        printf("bar(): x: %d\n", x);
    }
    Code:
    $ gcc -Wall -c foo.c  # no problem, only one x, in foo.c
    $ gcc -Wall -c bar.c  # no problem, only one x, in bar.c
    $ gcc -Wall foo.o bar.o  # now i try to combine them into one program
    bar.o:(.data+0x0): multiple definition of `x'
    foo.o:(.data+0x0): first defined here
    collect2: ld returned 1 exit status
    There are two x's, one in foo.c (from globals.h) and another in bar.c (also from globals.h). The x = 17 and x = 42 lines don't know which of the two x's to use. They're both global in scope, available to any c code using those object files.

    Can you provide a simple example that works for you? What compiler/linker are you using?

  10. #10
    Registered User
    Join Date
    Sep 2007
    Posts
    1,012
    Quote Originally Posted by Babkockdood View Post
    I've been doing that for months, and it always works how I want it to. Maybe I just have a magic compiler.
    Magic compiler no, but compiler/linker with Unix-like tendencies, yes.

    Putting, say, "int x" globally in multiple source files is undefined according to the standard: "If an identifier declared with external linkage is used in an expression ..., somewhere in the entire program there shall be exactly one external definition for the identifier; otherwise, there shall be no more than one." (C99 6.9).

    However, Unix systems (at least usually) have the notion of a "common symbol", which in C terms means a file scope declaration without static and without an initializer. If there are multiple common symbols, they're all merged into one object. Once you start giving initializers, though, all bets are off.

    It's a bad habit to get into because of the fact that it's not standard. Turbo C, for example, says "Error: _x defined in module a.c is duplicated in module b.c" when I try to pull the same trick with it.

  11. #11
    Third Eye Babkockdood's Avatar
    Join Date
    Apr 2010
    Posts
    352
    Alright, everyone. Here's a multiple source file program I wrote.

    prog.c
    Code:
    #include <stdio.h>
    #include "prog.h"
    
    int main() {
    	x = 42;
    	printf("%d\n", x);
    	foo();
    	printf("%d\n", x);
    	bar();
    	printf("%d\n", x);
    	return 0;
    }
    prog.h
    Code:
    #ifndef PROG_H
    #define PROG_H
    
    int x;
    void foo();
    void bar();
    
    #endif
    foo.c
    Code:
    #include "prog.h"
    
    void foo() {
    	x = 69;
    	return;
    }
    
    void bar() {
    	x = 1337;
    	return;
    }
    This is how I compiled it.
    Code:
    cc -c prog.c
    cc -c foo.c
    cc prog.o foo.o -o prog
    This is the output.
    Code:
    42
    69
    1337
    anduril462, I assigned a value to x at the beginning of main, not in the header file. I just declared it in the header file. I've been writing programs this way for months, I only get problems when I try to assign values to variables in header files.
    Quote Originally Posted by The Jargon File
    Microsoft Windows - A thirty-two bit extension and graphical shell to a sixteen-bit patch to an eight-bit operating system originally coded for a four-bit microprocessor which was written by a two-bit company that can't stand one bit of competition.

  12. #12
    ATH0 quzah's Avatar
    Join Date
    Oct 2001
    Posts
    14,826
    You don't define an instance of a variable in a header file. You might declare an extern variable there, but you wouldn't assign it there. If there is a variable everyone needs to be able to see, you make it extern, then, you actually define it in one single .c file, and set the value there:
    Code:
    /*foo.h*/
    extern int bar;
    Code:
    /*foo.c*/
    int  bar;
    ...
    Code:
    /*baz.c*/
    #include "foo.h"
    ...
    bar = 5;

    Quzah.
    Hope is the first step on the road to disappointment.

  13. #13
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,708
    Quote Originally Posted by Richardcavell View Post
    1. When I write code that interacts with the operating system a fair bit, I find that the number of possible error conditions grows exponentially with each call. Every time I call a system function, I have to gracefully handle all possible errors. When people write code, do they typically write error-handling code for each function call at the time, or do they make it work assuming-no-errors and then go back and write error handling later?
    Proper error handling is very much a matter of delegation. Fatal conditions should be checked at the lowest possible level (encapsulated within the program's file-reading functions, for instance), logged, and a result immediately returned to the environment. Malformed user input and other such recoverable errors can be dealt with using generalized routines. All other functions should simply assume that everything passing through has already been verified/validated/normalized - string-handling functions, for example, are not the place to be checking for null pointers! Thoughtful planning is really the key here.
    Last edited by Sebastiani; 02-21-2011 at 04:51 PM. Reason: typo, rw
    Code:
    #include <cmath>
    #include <complex>
    bool euler_flip(bool value)
    {
        return std::pow
        (
            std::complex<float>(std::exp(1.0)), 
            std::complex<float>(0, 1) 
            * std::complex<float>(std::atan(1.0)
            *(1 << (value + 2)))
        ).real() < 0;
    }

  14. #14
    Registered User
    Join Date
    Mar 2009
    Posts
    344
    Quote Originally Posted by Salem View Post
    1. Write good error handling (or at the very least, good reporting) from the outset.
    In a large program, ignoring errors just means the error propagates to some other part of the program (probably written by someone else) and the error shows up as something completely weird. This (if not understood immediately) results in hours/days of debugging effort to recover from a situation that would have taken seconds to avoid.
    Yep, and put it in early. When are you most likely to see various errors? When you're developing immature code. That means that the sooner you get the error checking in there, the quicker bugs will be contained to where they originate which should make it easier to track them down.

    But don't go crazy. Only worry about cases where you can actually correct a problem. Malloc fails? fclose doesn't work? printf() returns the wrong value? Well, in those cases you're boned no matter what you do - retrying isn't going to help so you have to cut your losses and hope for the best. But things like fopen() should always be checked - everyone gets the format of the path wrong or mistakes a \ for \\ so reporting "file not found" ASAP is a good thing.

    2. You should avoid global variables.
    But if you're going to have them, put them in the same file as main(), and have a "globals.h" which contains all the extern declarations.
    I disagree here. Define them in the .c file where they're most relevant, and include them as extern in that file's corresponding header. It's part of the external interface of a part of your program, just like a function call to search a data structure or log result to a file. Put it where extern's go - in that module's corresponding .h file. Having a catch-all globals.h which every file includes just makes it tougher to figure out what each of those globals are.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. c program help :>
    By n2134 in forum C Programming
    Replies: 9
    Last Post: 02-06-2010, 12:12 PM
  2. Hi, Quiz C program Assignment updated
    By Eman in forum C Programming
    Replies: 19
    Last Post: 11-22-2009, 04:50 PM
  3. c program that accepts and executes commands?
    By Cimposter in forum C Programming
    Replies: 3
    Last Post: 09-30-2009, 02:58 PM
  4. Dikumud
    By maxorator in forum C++ Programming
    Replies: 1
    Last Post: 10-01-2005, 06:39 AM
  5. I need some help with my program please.
    By agentxx04 in forum C Programming
    Replies: 9
    Last Post: 09-26-2004, 07:51 AM