I come from a java background (from my CS classes) and a semester of C++. I am just finishing up a OpenCV project for my Co-Op that\'s in pure C, so I\'m a bit late in aski
I honestly don't think that any number of answers on StackOverflow are going to teach you how to design and write well-structured C programs. You need to read a good book, and the obvious one to read is The C Programming Language by Kernighan & Ritchie.
You may want to have a good look at the source of The Linux Kernel..... BTW it is not the easiest piece of code to start with but since you are from a programming background, it may help... As an object-oriented programmer, you might particularly find error-handling in C an uphill task. May be this helps---> http://www.freetype.org/david/reliable-c.html
I have no professionnal experience on C (only on C++), so don't take my advices, tricks and tips too seriously, as they are "object-like-oriented".
Simulating basic object-like features can be done easily:
In the header, forward declare your type, typedef it, and declare the "methods". For example:
/* MyString.h */
#include <string.h>
/* Forward declaration */
struct StructMyString ;
/* Typedef of forward-declaration (note: Not possible in C++) */
typedef struct StructMyString MyString ;
MyString * MyString_new() ;
MyString * MyString_create(const char * p_pString) ;
void MyString_delete(MyString * p_pThis) ;
size_t MyString_length(const MyString * p_pThis) ;
MyString * MyString_copy(MyString * p_pThis, const MyString * p_pSource) ;
MyString * MyString_concat(MyString * p_pThis, const MyString * p_pSource) ;
const char * MyString_get_c_string(const MyString * p_pThis) ;
MyString * MyString_copy_c_string(MyString * p_pThis, const char * p_pSource) ;
MyString * MyString_concat_c_string(MyString * p_pThis, const char * p_pSource) ;
You'll see each functions is prefixed. I choose the name of the "struct" to make sure there won't be collision with another code.
You'll see, too, that I used "p_pThis" to keep with the OO-like idea.
In the source file, define your type, and define the functions:
/* MyString.c */
#include "MyString.h"
#include <string.h>
#include <stdlib.h>
struct StructMyString
{
char * m_pString ;
size_t m_iSize ;
} ;
MyString * MyString_new()
{
MyString * pMyString = malloc(sizeof(MyString)) ;
pMyString->m_iSize = 0 ;
pMyString->m_pString = malloc((pMyString->m_iSize + 1) * sizeof(char)) ;
pMyString->m_pString[0] = 0 ;
return pMyString ;
}
/* etc. */
If you want "private" functions (or private global variables), declare them static in the C source. This way, they won't be visible outside:
static void doSomethingPrivate()
{
/* etc. */
}
static int g_iMyPrivateCounter = 0 ;
If you want inheritance, then you're almost screwed. If you believed everything in C was global, including variable, then you should get more experience in C before even trying to think how inheritance could be simulated.
For example, multiple returns is risky. For example:
void doSomething(int i)
{
void * p = malloc(25) ;
if(i > 0)
{
/* this will leak memory ! */
return ;
}
free(p) ;
}
This include "static" variables (which are not static functions).
Global non-const variables are almost always a bad idea (i.e. see C API strtok for an example of crappy function), and if producing multithread safe code, they are a pain to handle.
Choose a "namespace" for your functions, and for your defines. This could be:
#define GROOVY_LIB_x_MY_CONST_INT 42
void GroovyLib_dosomething() ;
Defines can't be avoided in C, but they can have side effects!
#define MAX(a, b) (a > b) ? (a) : (b)
void doSomething()
{
int i = 0, j = 1, k ;
k = MAX(i, j) ; /* now, k == 1, i == 0 and j == 1 */
k = MAX(i, j++) ; /* now, k == 2, i == 0 and j == 3, NOT 2, and NOT 1 !!! */
}
Avoid declaring variables without initializing them:
int i = 42 ; /* now i = 42 */
int j ; /* now j can have any value */
double k ; /* now f can have any value, including invalid ones ! */
Uninitialized variables are causes of painful bugs.
The C API function list as described in the K&R is quite small. You'll read the whole list in 20 minutes. You must know those functions.
Rewrite the C API. For example, try to write your own version of the string.h functions, to see how it is done.
You can restrict visibility of file-scope variables and functions to their respective source files (although that doesn't prevent you from passing pointers to these objects around).
For example:
/** foo.c */
static void foo_helper() {...} /* foo_helper cannot be called by name
outside of foo.c */
static int local_state; /* local state is visible at file scope,
but is not exported to the linker */
systemd
systemd code quality tools
RIOT-OS
nginx
Git Source Guidelines
FFmpeg
curl
The Architecture of Open Source Applications C programs there include Git, Nginx, Open MPI, GPSD (C & Python), GDB, FreeRTOS, Berkeley DB, Bash, Asterisk,
VLC (lots of architecture info)
Any of the above projects are worth a read. Standard command-line tools are also available to read. The Linux kernel has a lot of design documents out there too.