views:

664

answers:

9

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 asking this question.

What are the design processes and coding standards for pure C?

I'm familiar with object oriented programming, design and best practices. I'm just a bit at a loss at a non-object oriented language like C. Every single variable and function appears to be global. It makes it feel like a real mess to me.

+7  A: 

You can make a function or object's scope local to its source file by declaring it "static". That helps a bit.

Otherwise, the typical idiom I see for avoiding namespace clashes is to put some kind of facility identifier onto the name. For instance, everything in foo.c might be named foo_*

T.E.D.
+11  A: 

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.

anon
+3  A: 

Work with other good C programmers. Have a code review with them. Not only let them look at your code, but you look at their code.

Robert
+1  A: 

You can do object oriented design in pure C. An easy approach is to think of a module as a class with public methods as ordinary functions that require the this parameter as an explicit first argument.

It helps if the class name is a prefix on the function name, and if all private functions and class data are declared static. You build constructors with malloc() to get the memory, and explicit initialization of the data fields.

A constructor for an object with entirely private data members can expose an opaque pointer (typed void * even, or as a pointer to an incomplete type if type safety is desired). If you want to have only public data members, then a pointer to a publicly defined struct works well.

This pattern is followed by a number of libraries. An initialization function returns a cookie that must be passed back to all library methods, and one method serves as a destructor.

Of course, there are other ways to design well in pure C, but if OO works for you, you don't have to abandon it completely.

RBerteig
Note that the only OO-thing you have with this design is encapsulation. Inheritance and polymorphism will need A LOT MORE of features, but then encapsulation is *always* a good thing, no matter the language.
paercebal
@paercebal, true. A fair amount of polymorphism can be done with function pointers, but you can't have overloading without something doing the name mangling. Inheritance can be modeled in a straightforward way as well. It's worth remembering that C++ was originally implemented as a preprocessor that wrote pure C, after all.
RBerteig
+1  A: 

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 */
John Bode
+1  A: 

Good news is that you can program in a semi-object oriented fashion in C. You can protect data, expose access functions, etc. It may not have all of the fanciness of C++, but from what I have seen of other people's C++ code, many people do not use the fanciness anyway. In other words, people write C code inside a class, where in C you'd write the same code without the class container.

First, read a book on C programming and style, K&R is fine. Second, I'd recommend checking out CERT Programming Standard. Even though this site is primarily focused on "Secure" coding standards, much of the content here is general code quality standards everyone should follow. Doing the things mentioned here will improved your quality, eliminate pesky bugs, and as a side effect, make your code more secure.

KFro
+4  A: 

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".

Almost Object C?

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.

Misc. Tips

Avoid multiple code-paths.

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) ;
}

Avoid non-const globals

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.

Avoid name collision

Choose a "namespace" for your functions, and for your defines. This could be:

#define GROOVY_LIB_x_MY_CONST_INT 42

void GoovyLib_dosomething() ;

Beware defines

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 !!! */
}

Initialize your variables

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.

Know all the C API

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.

Wanna some experience?

Rewrite the C API. For example, try to write your own version of the string.h functions, to see how it is done.

paercebal
+5  A: 

You may be interested in checking out the answers to a similar question I asked not too long ago. Moreover, if you're interested in C style guides, you may want to take a look at this page since it is a repository for C (and C++) style guides. If you're in the mood for a good laugh, please take a look at the NASA C Style Guide. In particular, take a look at the massive comment... you'll know which one I'm talking about. Don't write comments like this please.

I personally reccomend the Indian Hill C Style Guide with some modifications. Furthermore, you may want to purchase the book C Interfaces and Implementations if you're having trouble designing large-scale programs in C.

Anthony Cuozzo
+1 because of the links to web page containing multiple links to C standards.
paercebal
+1 for the recommendation of the Indian Hill C Style.
sheepsimulator
+1  A: 

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

puffadder