First things first, on reverse engineering. Given infinite time and resources, your code can always be reverse engineered. Having said that, your objective is to make it impractical for others to reverse engineer your code.
Now to answer your question:
Generating an executable binary from c code happens in "two major" steps. Compiling and Linking.
After compiling, your files.c become object files (machine code). They are not executable yet.
If you have two files: file1.c and file2.c, you will get file1.o and file2.o for example.
Now, the code in file1.c may be calling a function which exists in file2.o. At compilation stage, all what file1.c needs to know is the function prototype.
When the linker is invoked to generate the executable binary, it makes sure that the function called from file1.o exists somewhere, such as in file2.o.
How this affects you:
The header file should not be proprietary (but perhaps it is for legal reasons). The header file is mainly used to tell other .c files what functions and return values to expect (declaration, not implementation).
Now perhaps you have some proprietary function prototypes for whatever reason which you don't want to expose to the world. Say you want the world to start your code by calling the function
start_magic();
Then, what you do is:
- Provide a header file: magic.h to be included in the main.c
- header file will have the function: void start_magic();
- You then put your proprietary code in algo.c and algo.h
- algo.c will have start_magic() implementation
- algo.c will include proprietary algo.h
Now what you can do is compile (no linking) your algo.c file, and strip the debugging symbols to make it hard to reverse engineer. How this is done depends on the compiler you are using.
Now you can provide the object file and the header file to somebody who wants to call the function start_magic().
The implementer of main has to link the program using the object file you provided.
Example
Assume you have algo.c with your algorithms. Let us say algo.c has the function:
float sqrt(float x){
taylor_approx(x);
}
Suppose that sqrt function will be shared with supplier. However, sqrt function calls on proprietary function taylor_approx(x) to calculate the square root.
You can create an algo.h file to be sent to the users, which contains:
extern float sqrt(float x);
Then you can send your -stripped from debugging symbols- compiled object file, for example, algo.o, to the users and ask them to put algo.h in their main.c
Note that this is one way to do it.