Author Topic: [C] do i need a *.h file for every *.c file?  (Read 2891 times)

0 Members and 1 Guest are viewing this topic.

Offline mikeselectricstuff

  • Super Contributor
  • ***
  • Posts: 12037
  • Country: gb
    • Mike's Electric Stuff
Re: [C] do i need a *.h file for every *.c file?
« Reply #25 on: January 21, 2019, 06:36:49 pm »

Instead of exposing a global variable, you write a function that returns the value and another function that sets the value.
Yes, hanging a global variable out in the wind is easier, in a Fortran kind of way, but it isn't better.
There is no such thing as "better", only more or less appropriate for a given situation.
The above would be terrible advice for a tiny microcontroller, or where speed was the most important factor.
Quote
Einstein was right: Code should be as simple as possible.  But no simpler!
Global variables are simplest... just sayin'
Youtube channel:Taking wierd stuff apart. Very apart.
Mike's Electric Stuff: High voltage, vintage electronics etc.
Day Job: Mostly LEDs
 
The following users thanked this post: GeorgeOfTheJungle

Online GeorgeOfTheJungle

  • Super Contributor
  • ***
  • Posts: 2095
  • Country: pl
Re: [C] do i need a *.h file for every *.c file?
« Reply #26 on: January 21, 2019, 06:45:12 pm »
Yeah, +1 mike, getters and setters to access globals in an "arduino"? Thanks, but no, thanks...
git diff *
 

Offline Simon

  • Global Moderator
  • *****
  • Posts: 13920
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [C] do i need a *.h file for every *.c file?
« Reply #27 on: January 21, 2019, 06:52:12 pm »
Including a c file will upset many IDE's that have already added the files to the project. But the files will be loaded after the main.c file so the header file is needed to give information on function prototypes.

I can't see a way out of global variables when an interrupt routine gives the variable (volatile variable) and another function uses that variable.
https://www.simonselectronics.co.uk/shop
Varied stock of test instruments and components including EEVblog gear and Wurth Elektronik Books.
Also, if you want to get ripped off: https://www.ebay.co.uk/usr/simons_electronics?_trksid=p2047675.l2559
 

Online GeorgeOfTheJungle

  • Super Contributor
  • ***
  • Posts: 2095
  • Country: pl
Re: [C] do i need a *.h file for every *.c file?
« Reply #28 on: January 21, 2019, 06:56:54 pm »
Including a c file will upset many IDE's that have already added the files to the project. But the files will be loaded after the main.c file so the header file is needed to give information on function prototypes.

You can put the prototype in the .c file just before the function that calls the forward function, it does NOT need to be in a separate .h file.
git diff *
 

Offline Simon

  • Global Moderator
  • *****
  • Posts: 13920
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [C] do i need a *.h file for every *.c file?
« Reply #29 on: January 21, 2019, 06:59:01 pm »
yes but that makes an awful mess.
https://www.simonselectronics.co.uk/shop
Varied stock of test instruments and components including EEVblog gear and Wurth Elektronik Books.
Also, if you want to get ripped off: https://www.ebay.co.uk/usr/simons_electronics?_trksid=p2047675.l2559
 

Offline Jeroen3

  • Super Contributor
  • ***
  • Posts: 3331
  • Country: nl
  • Embedded Engineer
    • jeroen3.nl
Re: [C] do i need a *.h file for every *.c file?
« Reply #30 on: January 21, 2019, 06:59:49 pm »
You can use dependency inversion. Both the ISR and the module using the data access this by getter setter of some other module. They both only need to know type the getter setter.
Changing anything in either module will not recompile the other module. (since there is no #include)

Although this is highly abstract for microcontroller C, once your project gains a certain size these concept might become interesting. And you might regret not having read about them before.
Eg: if your project is filling up half a megabyte (without images) the compilation time might start to take minutes or longer depending on the performance of your computer.
 

Online GeorgeOfTheJungle

  • Super Contributor
  • ***
  • Posts: 2095
  • Country: pl
Re: [C] do i need a *.h file for every *.c file?
« Reply #31 on: January 21, 2019, 07:17:30 pm »
yes but that makes an awful mess.

Well, you don't have to prototype every function for the sake of prototyping, so, unless you do lots of forward calls there's not going to be lots of them, only the few you need and always next to the functions that need it. But yeah, YMMV.
git diff *
 

Offline tggzzz

  • Super Contributor
  • ***
  • Posts: 10236
  • Country: gb
    • Having fun doing more, with less
Re: [C] do i need a *.h file for every *.c file?
« Reply #32 on: January 21, 2019, 07:25:17 pm »
Be aware that the concept of "dependency inversion" is blindingly obvious to a hardware engineer - you see it in every schematic.

In schematic terms it means that you have one schematic for the power supply module, another for the RF front end module, another for the front panel module, and one more for the signal processing module. Each schematic has its module's inputs and outputs clearly delineated as "ports" (or "pins" if the module is an ic!).

Then you add a "top-level" schematic consisting of a block for each of those "lower level" modules where the block shows the ports and nothing else, plus wires going between the lower level modules' ports.

Unsurprisingly, each "port" is equivalent to something that is exposed in a .h file.

It took the software world until the mid-90s to catch up with that concept, and of course they had to give it their own name to make it seem clever.
There are lies, damned lies, statistics - and ADC/DAC specs.
Glider pilot's aphorism: "there is no substitute for span". Retort: "There is a substitute: skill+imagination. But you can buy span".
Having fun doing more, with less
 

Offline Siwastaja

  • Super Contributor
  • ***
  • Posts: 2073
  • Country: fi
Re: [C] do i need a *.h file for every *.c file?
« Reply #33 on: January 21, 2019, 08:26:32 pm »
An example:

lcd_driver.h:

Code: [Select]
#pragma once

extern int variable_that_needs_to_be_so_quick_you_cant_use_a_getter_function;
void lcd_init();
void lcd_print_text(char* text);


lcd_driver.c:

Code: [Select]
#include "lcd.h"

int variable_that_needs_to_be_so_quick_you_cant_use_a_getter_function; // Actually defined only here
static int internal_variable;

static void internal_detail()
{
...
}

void lcd_init()
{
    internal_detail();
}

void lcd_print_text(char* text)
{
    ....
    internal_variable = 123;
}

main.c or whatever:
Code: [Select]
#include "lcd_driver.h"

void main()
{
    lcd_init();
    lcd_print_text("hello world");
    variable_that_needs_to_be_so_quick_you_cant_use_a_getter_function = 123; // Computer science people won't like this way, but you don't need to care.
}

Compilation, for example:
Code: [Select]
gcc -c lcd_driver.c -o lcd_driver.o
gcc -c main.c -o main.o
ld -o out.elf lcd_driver.o main.o
(-c means: do not link, only compile this module)


It took me about a decade to learn this. This isn't too well taught anywhere. This should be the first freaking C lesson ever, the 101. This is the way C implements private and public member variables and functions. Works very well and is understandable, you need to know two reserved words (static and extern) and how to use them.

Yes, sometimes you could use a single .h and multiple .c, if the interface (.h) is simple, but the implementation (.c) is complex but somehow logical to divide into several .c files. If you only do this to "reduce number of files", you are doing it wrong. If you have too many files and it confuses you, the chances are, your modules are too small, or the overall design is too complex to do what you want it to do.

Yes, sometimes you include a .c file from another .c file - one typical example would be an autogenerated dataset like a large lookup table, which is only needed by that specific implementation. By having it in a separate file, it's easier to autogenerate again, overwriting the old file; or choose between files by changing the include line only; or just simply to keep it out of sight. It could be named .whatever, but .c is a logical extension if it's valid C code, such as:

sin_lut.c:

Code: [Select]
// Autogenerated, do not modify
static const int8_t sin_lut[256] =
{0, 3, 6, 9, 12, ... };

Note that while this could be .h (it's just a file), it would be misleading since this actually defines variables. And, while you could compile it as a separate module and link together at the end, just including the .c to another .c may look simpler; the dependency is so clear and limited. If you did it through the linker, then you'd need to to declare the variable somewhere, preferably in another .h file, which would look stupid for such a trivially small task of including one constant table which would obviously be in the single .c file directly if it was smaller and human-generated.
« Last Edit: January 21, 2019, 08:28:31 pm by Siwastaja »
 
The following users thanked this post: newbrain

Offline rhb

  • Super Contributor
  • ***
  • Posts: 2677
  • Country: us
Re: [C] do i need a *.h file for every *.c file?
« Reply #34 on: January 21, 2019, 09:19:32 pm »

Yes, sometimes you include a .c file from another .c file - one typical example would be an autogenerated dataset like a large lookup table, which is only needed by that specific implementation. By having it in a separate file, it's easier to autogenerate again, overwriting the old file; or choose between files by changing the include line only; or just simply to keep it out of sight. It could be named .whatever, but .c is a logical extension if it's valid C code, such as:


That's a very important point.  There are cases where the only sensible implementation is to use a program that writes a larger piece of code.  lex and yacc are classic examples.
 

Online westfw

  • Super Contributor
  • ***
  • Posts: 3067
  • Country: us
Re: [C] do i need a *.h file for every *.c file?
« Reply #35 on: January 21, 2019, 11:18:35 pm »
I think that nominally, you need TWO .h files for each "set" of .c files.So if you have, say, a parsing library with separate C files for text, numbers, and keywords, and internal stuff, you might have the 4 .c files, plus a "parse_api.h" file that exposes the API to all the clients, and a "parse_internal.h" that contains all the other stuff needed for the library itself to compile (protoctypes of internal functions, or perhaps only includes of the parse_api.h, if you're exposing everything.)
Arduino turned on the switches that cause unused code to be omitted from the binary (even if they're in the same .C file as used code) about a decade ago.  If your compiler still doesn't have a similar option, perhaps it's time to consider a new compiler.   (that said, I bought a network protocol library a couple decades ago that put every function in a separate .c file, and had nearly as many .h files.   While it was a bit of a mess to look at, it worked REALLY WELL.  (and: if your editor can't easily jump between files to find a different function, perhaps it's time to get a new editor.)
 

Offline legacy

  • Super Contributor
  • ***
  • Posts: 4346
  • Country: ch
Re: [C] do i need a *.h file for every *.c file?
« Reply #36 on: January 21, 2019, 11:44:59 pm »
Arduino turned on the switches that cause unused code to be omitted from the binary (even if they're in the same .C file as used code) about a decade ago.  If your compiler still doesn't have a similar option, perhaps it's time to consider a new compiler.

this is done not by the compiler but rather by the Wiring &C infrastructure.
 

Online NorthGuy

  • Super Contributor
  • ***
  • Posts: 1834
  • Country: ca
Re: [C] do i need a *.h file for every *.c file?
« Reply #37 on: January 21, 2019, 11:52:55 pm »
.h file it's just a file being included in the compilation at the #include - exactly as if you copied and pasted. This is all there is to it (setting aside IDEs, syntax highlighters and such). It doesn't have to be .h, can have any extension you wish.

Thus, every time you want to include the same text into two (or more) different files, you use the .h file. Or, if you want to generate a text externally and then include into the file being compiled, you use the .h file as well.
 

Offline rhb

  • Super Contributor
  • ***
  • Posts: 2677
  • Country: us
Re: [C] do i need a *.h file for every *.c file?
« Reply #38 on: January 22, 2019, 02:17:36 am »
.h file it's just a file being included in the compilation at the #include - exactly as if you copied and pasted. This is all there is to it (setting aside IDEs, syntax highlighters and such). It doesn't have to be .h, can have any extension you wish.

Thus, every time you want to include the same text into two (or more) different files, you use the .h file. Or, if you want to generate a text externally and then include into the file being compiled, you use the .h file as well.

This is *really* important!  I use the C preprocessor to include named COMMON in FORTRAN to guarantee that *all* named COMMON blocks are the same.  It's one of the first things I do if I build a dusty deck.  I have *never*  encountered a large code that did not have errors in named COMMON blocks.

It is essential that you understand both the preprocessor rules in the language standard *and* whatever the implementation you are using actually does.  The preprocessor is commonly a separate program, but that it not required.

Imake was written with the assumption that the C preprocessor could be used to modify files for different systems.  In the end, imake(1) had to include its own C preprocessor.  I always thought the snake on the cover of the O'Reilly imake book especially appropriate.  I accomplished the same thing using Gnu make at a fraction of the complexity level. The makefiles included a single file with a name derived from uname(1).  Everything else was just very basic make syntax.
 

Online westfw

  • Super Contributor
  • ***
  • Posts: 3067
  • Country: us
Re: [C] do i need a *.h file for every *.c file?
« Reply #39 on: January 22, 2019, 02:36:48 am »
Quote
Quote
Arduino turned on the switches that cause unused code to be omitted from the binary (even if they're in the same .C file as used code) about a decade ago.
this is done not by the compiler but rather by the Wiring &C infrastructure.
Nope.  Standard gcc compiler switches.-ffunction-sections -fdata-sections  (Compiler)-gc-sections (linker)https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.htmlHuh.  The documentation implies that this can be detrimental to program size, since it apparently defeats intra-file optimization.  That hasn't been my experience (for example, functions are still inlined when they are tiny.)  Perhaps additional link-level optimizations (-flto, -relax) fix it up...

 
The following users thanked this post: newbrain

Offline andyturk

  • Frequent Contributor
  • **
  • Posts: 892
  • Country: us
Re: [C] do i need a *.h file for every *.c file?
« Reply #40 on: January 22, 2019, 03:45:46 am »
Simon,

Ditch your IDE. It's not helping you learn how this stuff works.

These are really basic questions and if you were to spend a day writing batch scripts, makefiles, or whatever using only command line tools, you'd be able to master these concepts. As it is now, your understanding seems to stop at what the IDE does and that's simply not sufficient for professional work.

Back to the topic... once you've factored out some code into a (shared) header file, you have to make sure that when that when the header is modified, every .c that depends on it is recompiled. Do you know how to do that without relying on an IDE?
 
The following users thanked this post: grbk

Offline rhb

  • Super Contributor
  • ***
  • Posts: 2677
  • Country: us
Re: [C] do i need a *.h file for every *.c file?
« Reply #41 on: January 22, 2019, 04:11:21 am »
Simon,

Ditch your IDE. It's not helping you learn how this stuff works.

These are really basic questions and if you were to spend a day writing batch scripts, makefiles, or whatever using only command line tools, you'd be able to master these concepts. As it is now, your understanding seems to stop at what the IDE does and that's simply not sufficient for professional work.

Back to the topic... once you've factored out some code into a (shared) header file, you have to make sure that when that when the header is modified, every .c that depends on it is recompiled. Do you know how to do that without relying on an IDE?

This is *excellent* advice.  I wish I'd thought of it.  I do not, and will not use an IDE if I have a command line option.  There is no substitute for knowing *exactly* what is being done.  The fundamental premise of an IDE is to hide all that.  Starting out it makes it easier.  Long term it makes it a lot harder.

Gnu make has become a bit  feature rich, but it is consistent across all platforms  Not that many of the others still exist.  But 25 years ago, every OEM had a *just* slightly different make implementation.  I'm disturbed that they are all gone, but there is nothing to be done about it.  Once IBM committed to investing $1 billion in Linux it was all over but the fire sales.
 

Offline andyturk

  • Frequent Contributor
  • **
  • Posts: 892
  • Country: us
Re: [C] do i need a *.h file for every *.c file?
« Reply #42 on: January 22, 2019, 05:23:06 am »
Gnu make has become a bit  feature rich, but it is consistent across all platforms  [...]
And under the covers, GNU Make often does the heavy lifting for contemporary IDEs. Sure, it's weird and arcane, but it's also super stable and available on all major development platforms. The GNU Make Standard Library has lots of good functionality that can add robustness to a build system.
 

Offline tggzzz

  • Super Contributor
  • ***
  • Posts: 10236
  • Country: gb
    • Having fun doing more, with less
Re: [C] do i need a *.h file for every *.c file?
« Reply #43 on: January 22, 2019, 09:10:11 am »
Ah yes, makefiles. Haven't written one in decades.

Do they still have the "feature" that tabs have a different meaning to spaces?
There are lies, damned lies, statistics - and ADC/DAC specs.
Glider pilot's aphorism: "there is no substitute for span". Retort: "There is a substitute: skill+imagination. But you can buy span".
Having fun doing more, with less
 

Offline legacy

  • Super Contributor
  • ***
  • Posts: 4346
  • Country: ch
Re: [C] do i need a *.h file for every *.c file?
« Reply #44 on: January 22, 2019, 11:38:01 am »
gnu make =sys-devel/make-4.2.1-r4 (last one stable) is *NOT* exactly compatible with its predecessors about expanding certain macros, and this is a problem with Linux kernel <v2.6.23, when you have to *edit* the "Makefile" and replace all the affected lines

not a real problem, just 15 minutes of your time

the real problem is: gnu make is NOT exactly compatible with SGI MIPS/PRO make, as well as gnu grep is NOT compatible with Unix grep, as well as GNU Tar is not compatible with UNIX Tar

tar is what is really ... annoying, especially if you have used it for tarballing something on a tape drive with IRIX and years later you want to untarball it with Linux  :palm:

I have DDS4 tape drive, it's a common SCSI tape drive, with a common SCSI protocol, so Linux should use it successfully, and it does it, unfortunately, the problem is in the format of the tarball archive; a customer sent me backup tapes and ... I have to get a goddamn IRIX machine in order to read them.

Someone in GNU must have thought that it would have been "cool" improving the UNIX Tar into GNU Tar
  :palm:
« Last Edit: January 23, 2019, 11:10:00 am by legacy »
 

Offline glarsson

  • Frequent Contributor
  • **
  • Posts: 807
  • Country: se
Re: [C] do i need a *.h file for every *.c file?
« Reply #45 on: January 22, 2019, 11:54:58 am »
Someone in GNU must have thought that it would have been "cool" improving the UNIX Tar into GNU Tar
  :palm:
Remember that GNU stands for GNU's Not Unix.

We have standard POSIX make and Gnu make.
We have standard POSIX tar and Gnu tar.

GNU suffers from severe Not Invented Here syndrome.
 

Offline andyturk

  • Frequent Contributor
  • **
  • Posts: 892
  • Country: us
Re: [C] do i need a *.h file for every *.c file?
« Reply #46 on: January 22, 2019, 01:33:06 pm »
Ah yes, makefiles. Haven't written one in decades.

Do they still have the "feature" that tabs have a different meaning to spaces?
You betcha.

I cut the program some slack because that design decision was made in 1976. OTOH, it's unfortunate that the Python world failed to learn from the experience and believes "significant" whitespace is a good idea.  :palm:

PS. Managing tabs in makefiles is easy if you're using emacs.
 

Offline tggzzz

  • Super Contributor
  • ***
  • Posts: 10236
  • Country: gb
    • Having fun doing more, with less
Re: [C] do i need a *.h file for every *.c file?
« Reply #47 on: January 22, 2019, 02:23:18 pm »
Ah yes, makefiles. Haven't written one in decades.

Do they still have the "feature" that tabs have a different meaning to spaces?
You betcha.

I cut the program some slack because that design decision was made in 1976. OTOH, it's unfortunate that the Python world failed to learn from the experience and believes "significant" whitespace is a good idea.  :palm:

PS. Managing tabs in makefiles is easy if you're using emacs.

Just so.

The software world is unusually prone to repeating old historical mistakes for new "reasons" - they are condemned to repeat history because they don't know it. My least favourite at the moment is the trend to "flat" GUIs where you can't know whether the black tab/button is active or not. That was a pain in the 80s, solved by X11's Motif (from HP of all companies!). Now it it a pain again.
There are lies, damned lies, statistics - and ADC/DAC specs.
Glider pilot's aphorism: "there is no substitute for span". Retort: "There is a substitute: skill+imagination. But you can buy span".
Having fun doing more, with less
 

Offline rstofer

  • Super Contributor
  • ***
  • Posts: 6618
  • Country: us
Re: [C] do i need a *.h file for every *.c file?
« Reply #48 on: January 22, 2019, 04:21:29 pm »
Ah yes, makefiles. Haven't written one in decades.

Do they still have the "feature" that tabs have a different meaning to spaces?

AFAIK, yes!

One approach for larger files is to put large modules in subdirectories, suitably named.  It is then possible for the top level makefile to recurse through the subdirectories invoking smaller makefiles in each subdirectory.  This seems like the long way around but the makefiles in the subdirectories are usually just a copy and paste.

Check out the structure of the code at http://jcwren.com/arm/  The makefiles are worth a glance.

So, I put my UART code in a file and I need to tell the world which functions are available for use.  I create a .h file to form a contract between the caller and the callee.  I don't put function prototypes in the .h file unless I really want them to be callable.  In the .c file, I can declare internal functions as 'static'.  This also prevents name conflict with identically named functions in other .c files.  If I need a prototype within a .c file just due to forward referencing, I'll put it in the .c file  but not in the .h file.

I would put the initialization code, the interrupt handler and the UART get/put functions in the file but I probably wouldn't publish prototypes for the interrupt code.  The user doesn't need to know.  He also doesn't need to know that I use a circular queue.  All he needs to know is what I tell him in the .h file and that would be the bare minimum.

As to 'tiny' CPUs and global variables, well, if they're really tiny we are probably coding in assembly language and everything will be visible.

I'm firmly in favor of hiding as much of the implementation as possible.  If the user can't get at a function or variable, they can't misuse it.

Of course there are exceptions, there aren't any 'laws', just guidelines.

Global variables are required from time to time but they shouldn't be used just to avoid having to come up with a better program structure.

Then we get to the idea of dependencies.  You change a .c file and then change the .h to match.  How many files in the project need to be recompiled?  The IDE that builds a makefile knows very well how to check dependencies and is perfectly capable of compiling just the files that include the .h file.  User makefiles can also build dependencies and at least one of mine creates the dependencies and appends it to the top level makefile.

I have attached a makefile.  Note that the target 'all' depends on 'depend' which creates the list of dependencies at the bottom of the file.  This gets executed on every build and this is probably not the best way to do it.  But it works!

This makefile is used from a Linux command line, my preferred way to build projects.  I use gedit as the editor (because it is WYSIWYG) and make.

Unfortunately, makefiles can't be attached without adding a file extension.
« Last Edit: January 22, 2019, 04:28:08 pm by rstofer »
 

Offline andyturk

  • Frequent Contributor
  • **
  • Posts: 892
  • Country: us
Re: [C] do i need a *.h file for every *.c file?
« Reply #49 on: January 22, 2019, 10:02:44 pm »
I have attached a makefile.  [...]

Cool! It's always interesting to see how others do things.

If you add "-MD" to your CFLAGS, GCC will generate the dependency info for you, producing a .d file for each object file (.o) it produces. The .d files are actually GNU Make include files, which you can bring in easily with:

Code: [Select]
-include $(OBJS:%.o=%.d)

I tend to lean heavily on make and use a few "weird tricks" to expand its capability. One is burying other scripts within a Makefile and then piping those scripts into shell tools. Here's one that handles flashing via JLink:

Code: [Select]
########################
define FLASH_BARE_CMD
device $(SEGGER_DEVICE)
log $(JLINK_LOG)                          # log what we do
si SWD
speed 4000
h
loadbin $(EXECUTABLE:.elf=.bin) 0x0
r
g
exit
endef
#########################
export FLASH_BARE_CMD


jflash : $(EXECUTABLE:.elf=.bin)
@echo "$$FLASH_BARE_CMD" | $(JLINKEXE) $(SELECT_JLINK)

So "make jflash" will compile, link and then reprogram the mcu.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf