Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Splitting programs into multiple .c files 2

Status
Not open for further replies.

WebDrake

Programmer
Sep 29, 2005
106
0
0
PL
Hello,

Suppose I wanted to split my code into separate modules, as is quite common for large applications. I'm talking about the sort of thing where one compiles,

gcc file1.c file2.c file3.c -o bigfile

What sort of effect does this have on performance? Does it improve or harm things? I'm concerned with this because, for example, one has to include libraries separately for each .c file---but one can of course include within each the minimum necessary.

Also, what's the standard practice for exchanging function declarations between the different modules? Does one create a header file for each along the lines of, file1dec.h, file2dec.h, etc., which can be included or excluded as necessary, or ... ? I would imagine one of the benefits of modular programming is that of all the functions in each module, one has to make other modules only aware of one or two, while the rest can remain of interest only to the module in which they are contained.

I've never really had to do this before because all my programs (number-crunching science stuff) tend to be short enough that I don't need modules; though I often "outsource" some functions to separate header files.

Any thoughts?

Cheers,

-- Joe
 
I can only speak from writing on a Unix plaform but from my experience it has no impact on the final binary. It does however improve readability dramatically. It also means that a 'one line amendment' doesn't mean that everything needs to be recompiled. This can be a significant time saver in big programs. Everyone has their own style and I'm not saying I'm right, merely that my preference is for:-
[ol]
[li] One module per routine - I'll stretch this if there's a family. This means that if I want to borrow a routine for another program I don't carry any junk over with it.[/li]
[li]Each module is named after that routine i.e. main.c, read_file.c. This makes it easy to identify which bit does what.[/li]
[li]One header file to rule them all and in the shadows... (sorry I couldn't resist the Tolkien!) If all the routines are declared in myprog.h then you don't have to worry about which need to be declaired where.[/li]
[li]NO GLOBAL VARIABLES - sorry - hobby horse of mine.[/li]
[/ol]
Just my 2p worth

Columb Healy
 
Thanks for the thoughts. :)

OK, I'll bite. Why no global variables? ;-) As things stand I tend to find them useful since in the sort of stuff I do it tends to be a benefit to pass the minimum amount of data to functions; but I can imagine that in a large-scale work they could be a pain in the arse to keep track of, for little practical benefit.

 
Simple answer is that a module should be self contained and should need nothing from external code.
If you need to pass large amounts of data in then define a structure for it in the header file and take a reference (or pointer) to that structure as a parameter.
By defining it in the header, any other program can include the header and create the structure of data that your module needs to function.


Trojan.
 
OK, that's certainly something I can understand. What, however, about stuff like a seed for a random number generator? (e.g. a function I like, ran2 from Numerical Recipes in C, requires to have passed to it each time a long int, which lets it identify where it is in the sequence of pseudo-random numbers. Would there be a way to deal with this? e.g. to have the random seed contained within the module and remembered from successive calls to the module?

 
Yes, you create a "static" within the function. It must be initialised but then it holds the last value from call to call.


Trojan.
 
OK, great. So a nice random number module might be something like,


Code:
static long int seed = 0;

void raninit(long int *n) {
    seed = (*n);
}


double ranfunc(void) {
    /* does something with seed
       and generates a random
       number between 0 and 1 */
}
 
Just got in on Monday AM. Thanks for the star.
To clarify my biggest arguemnt against global variables. How do you know which functions affect which variables? This can be a major factor in debugging. If you see the code
Code:
foo ( &variable );
you can be pretty sure that foo is going to do something to variable, you wouldn't pass it otherwise, but if variable is global then does
Code:
foo();
affect it or not? Only looking at foo.c can you check. I don't want to spend my time checking the code of every module looking to find out which one has the bug which changed variable in unexpected ways.

Columb Healy
 
I have to say I disagree with the whole "no globals ever" argument here.
Whilst I agree with columb's last post, there are times when globals are very handy - and IMO acceptable.

I would say the best rule of thumb is "globals should be read-only". For example, if you have an application that needs to constantly refer to some data (ie a config file's contents or something) in many parts of code, it can become a serious pain to have to pass around a struct or pointer between functions when you know that the object is read-only - better to have a static global I would say.

Of course, I would agree that globals in general should not be modified by functions (for the reasons stated by columb above).

--------------------------------------------------
Free Java/J2EE Database Connection Pooling Software
 
sedj,

The original post was about breaking up a program and using modules.
I think the rule of thumb here is that any globals in main code space should be kept there. And globals in a module should be kept there. You should never[/] use mainspace globals in a module or vice versa. If you do, you completely remove any possibility of code re-use.
As I pointed out earlier, any communication of data between main code space and module functions can be done with one or more data structures and these data structures can and should be defined in a header file by the same name as the module itself so that any code wishing to re-use the module need only include the same header to be able to use the module.

It should go without saying that the use of globals is generally frowned upon and so should be kept to a minumum.


Trojan.
 
Surely there is a compromise one can make along the following lines: that one can set up static pointers in individual modules which can be initialized to point to the same thing. That way all the modules will have access to certain data but the module is still portable and does not require outside intervention.
 
So long as the static pointer is not accessed outside the module then fine.


Trojan.
 
I think all applications are different, and should be treated as so.

There are some common hard rules, such as do not use functions that modify globals ---- in MOST cases.

However, there are often times when globals make sense, and a misguided sense of object-orientation or 'pure modularity' just flies in the face of common sense.

As a programmer, you know what is right at the time for your programme - and you should trust your instincts for what is right for your programme. Anyone who preaches hard and fast rules with no exceptions is living in a dreamworld.

Examples :

1) You should attempt to process XML that does not pass the initial XML parser validation.
IMO - false. Our largest customer sometimes passes incorrectly formed 'XML' to us. I modify it using substring methods before parsing it. We are just not in a place where we can tell our customer that their IT is <insert_rude_word_here/>.

2) One of our dev. teams will not use emebedded java code in their JSPs. When their application went live, it all failed (it worked in dev, test and uat). I suggested they put in a quick'n'dirty hack to check some values. They said they could not do it because "Our design model, MVC2, does not allow embedded java in out JSPs - its bad practice". Well, after 3 hours, I was asked by management to see what was going on, added some hacks, diagnosed the problem, and we were rolling.

I'm just saying that sometimes it makes sense to go against what the books and the seers say. I am not willing to reload a static config file in each module/class/object/model each time just because its a "clean design" - I'll use a global.

You just have to use common sense.


--------------------------------------------------
Free Java/J2EE Database Connection Pooling Software
 
sedj,
That was a bit bitchy!
Rules are there for a reason. It's not impossible to break the rules and there are situations where it's appropriate (as in your second example) but blindly assuming that you are such a great programmer that you can break any rules you like whenever you like encourages poor code.
It's a bad habit waiting to happen.
Your example 1 is a good example.
If the code fails validation and you cannot reject it then fixing it for the client is fine. Using that as a justification to write poor code is not. You could easily write a pre-processor to clean it up. Also, by doing that you can have 2 versions of the XML. The before and after cleansing. That adds to tracability.

We are here offering advice to others. I can't agree with you suggesting that ignoring rules in general is ok. I think that these rules are here particularly for the weaker guys and therefore we should encourage the use of them, not reject them.



Trojan.
 
TrojanWarBlade :

Not sure how my post was bitchy ... but there you go. I did not mean it to be 'bitchy' - we're all friends here as far as I'm concerned among the regular posters ...

At no point in this thread have I said that 'ignoring rules' is OK in general. Quite the contrary, I am just pointing out that there are cases when breaking the 'rules' makes sense.

I agree that most of the time, following the rule that globals are generally a bad idea, is usually fine - but sometimes it is not the best practice. Sometimes, (like my example '2' - which did happen !), following rules prescribed by someone else is inefficient.

Whether someone is a newbie or not has no bearing on the issue - its about common sense, not about how good a programmer you are.

--------------------------------------------------
Free Java/J2EE Database Connection Pooling Software
 
So long as the static pointer is not accessed outside the module then fine.
What I had in mind was a module setup along the following lines. Suppose I have several different modules that use a similar kind of data, let's say a couple of arrays (but of course in practice it might be more than that). It's a pain in the ass to keep passing the variables all the time because I'm calling these functions many, many times. So what I would propose is that each module have a form kind of like this:

Code:
static double *x, *y;

modsetup(double *XX, double *YY) {
    x = XX;
    y = YY;
}

modfuncA() {
   /* Does stuff with x and y. */
}

modfuncB() {
   /* Ditto. */
}

Then, when I run my main function I can do something like,

Code:
int main(void) {
    double *x, double *y;

    x = create_x();
    y = create_y();

    mod1setup(x,y);
    mod2setup(x,y);
    mod3setup(x,y);
   /* etc. ... */

   /* So, I do a load of stuff that involves the module
      functions without ever having to pass x and y, and
      finally, */

   free(x); free(y);
   return(0);
}

Of course, here the modules share common values of the pointers x and y but the data contained within can be changed---and *that's the idea*. So it takes advantage of the fact that a global variable doesn't need to be passed, but the code stays modular.

I think this is somewhat breaking with the idea you have of modules not modifying other modules' local variables; but in the case of some of the code I'm writing, that's actually useful.
 
As I said, your point 2 is perfectly valid and I would support you all the way on that one. It's a typical "jobsworth" situation where no-one has the common sense to realise that debugging is not prodcution code and the debug code lasts minutes or hours.

The reason I was a littel offended is this:
Anyone who preaches hard and fast rules with no exceptions is living in a dreamworld.
That came across to me as though you are suggesting that I am living in a dreamworld. I hope I misinterpreted that.

I don't doubt for one second that your point 2 actually happened. I've seen such things myself.

I disagree with your last point. IMHO newbies need more guidance. When you learn to drive you are taught how to drive properly with a set of rules. When you become much more experienced you and more capable of judging when and where it is appropriate to bend those rules.



Trojan.
 
Trojan, its often hard to convey points in a text post when you have no idea what kind of person is on the other end - of course I didn't mean you personally were living in a dreamworld (unless Kent does not actually exist ?! -it is a long way from Devon).

I guess we'll have to agree to disagree on this one, though I do take your point about guiding newbies along certain rules - perhaps this is why I'd never make a good mentor. I guess you never saw Yoda saying 'do whatever you think feels right' :p

Cheers

--------------------------------------------------
Free Java/J2EE Database Connection Pooling Software
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top