profile for David C. Rankin at Stack Overflow, Q&A for professional and enthusiast programmers

C References


Tutorials

Of all the online tutorials, these tutorials are among the best for the areas they discuss.


Libraries

These libraries will make your life easier. They are complete with Makefiles and test programs showing the library use. Just untar the files into a test directory and issue 'make'.


Code Tools

The tools below are general utilities for code and web development.


Comments/Bugs

If you have a comment or suggestion or find a bug in the code, please feel free to send a report.

C Programming Language

“Why C?” answer: “Speed & Low-level Control”

That answer also answers the question of why C continues to be a programming language of choice today for performance critical applications including operating systems, desktops, supercomputing applications and embedded systems. In today's world you see the oft repeated cycle of fanfare and hype over the latest "language of the month". Some new languange offering some new programming paradigm, poly-morphism, multiple-inheretance, object-oriented approach, type-safety or memory bounds checking guaranteed to change the programming world. Much like a used-car salesman describes his “latest deal”. In the near half-century since its beginnings at Bell Labs, C has stood the test of time and continues to be the go-to language where performance and speed is critical[1] because no other language offers the hardware and memory level control that C provides.

Like any language, C has its strengths and weaknesses, but the C standard and included libraries make it as robust a language as any. While it may lack class implementation, that is as much a strength as it is a weakness. While classes and other language paradigms may promise to make programming seem easier in the beginning and may promise more reusable code, the price paid is often significant overhead and loss of performance. The exact same things can be accomplished in C, without unnecessary overhead or performance penalty.

C is an exact language. It combines the hardware-level control of assembly with the readability and syntax of a high-level programming language. You have to know the machine as well as the language. You are programming at the system-level without unnecessary abstraction between you and the hardware.

The primary pitfall new C programmers make is simply failing to slow-down and consider what you are asking the code to do. While it may take a little more effort to learn C, the benefits are substantial. That is why C continues to be the language of choice by the leading minds in software development.[2]

What this page is intended to do is to organize and provide some of the elegant C programming methods, code and benefits C has to offer that you will not find in the libc manuals or man pages. Most of the examples and code are snippets I have collected over the past couple of decades to solve various problems along the way. Attribution is given to the original authors/source when it is available, but much of that information has been lost to the passage of time (and disc drives).

Organizing - Under Construction

Rather than leave nothing here while I find time to complete the page, let me post the library of functions I created to deal with conversions to/from binary representations and the binary string formatting functions I have created. Also included in the binstr library are bitwise operation that are far more efficient that those 1/2 page functions it takes to accomplish the same thing. The files contained are built into a shared-library for easy inclusion in your current projects. The libraries are both x86 and x86_64 compatible. I make no representations as to their bug-free status, but I would point out that I use them daily. There may be a corner-case or two that pop up, so use them at your own risk, ya da, ya da, da... In the event a corner-case is hit, care has been taken so the functions have sane returns in the case of error. The download link for the library is at the top-right of the page under the Libraries heading. The header file, lib_binstr.h, is shown below: (source code highlight by GNU source-highlight, a very good code highlighter)

#ifndef _lib_binstr_
#define _lib_binstr_ 1

/* __func__ is not defined in pre C99 so provide fallback */
#if __STDC_VERSION__ < 199901L
# if __GNUC__ >= 2
#  define __func__ __FUNCTION__
# else
#  define __func__ "<unknown>"
# endif
#endif

#if defined(__LP64__) || defined(_LP64)
# define BUILD_64   1
#endif

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>  /* for uintX_t definitions */
#include <string.h>  /* for memcpy              */
#include <limits.h>  /* for CHAR_BIT            */

/* CHAR_BIT */
#ifndef CHAR_BIT
#define CHAR_BIT  8
#endif  /* CHAR_BIT */

/* 23 bits of float fractional data */
#define I2F_FRAC_BITS       23
#define I2F_MASK ((1 << I2F_FRAC_BITS) - 1)

/* return binary representation of x - static return */
char *binstr_8  (uint8_t x);
char *binstr_16 (uint16_t x);
char *binstr_32 (uint32_t x);
char *binstr_64 (uint64_t x);

/* return binary representation of x - dynamically allocated return */
char *binstr_8d  (uint8_t x);
char *binstr_16d (uint16_t x);
char *binstr_32d (uint32_t x);
char *binstr_64d (uint64_t x);

/* Function removes leading 0's from binary string
   returns pointer to MSB, otherwise returns NULL */
char *bin_nopad (char *s);

/* format string 's' into 'szgrp' groups separated by 'sep' */
char *binstr_sep (char *s, const size_t szgrp, char *sep);

/* return binary representation of x, in groups of szgrp bits, using separator sep */
char *fmtbinstr_8  (uint8_t x, const size_t szgrp, char *sep);
char *fmtbinstr_16 (uint16_t x, const size_t szgrp, char *sep);
char *fmtbinstr_32 (uint32_t x, const size_t szgrp, char *sep);
char *fmtbinstr_64 (uint64_t x, const size_t szgrp, char *sep);

/* functions for conversion of numbers to IEEE 754 format */

/* get most significant bit MSB */
inline int getmsb (uint64_t x);
/* get least significant bit LSB */
inline int getlsb (uint64_t x);
/* get number of 1's in binary number (population - pop)*/
int getn1s (unsigned x);
/* count leading zeros passing value and sizeof (uintX_t) */
int nlz (uint64_t x, unsigned szost);
/* count trailing zeros (any storage) */
int ntz (uint64_t x);

/* change sign for given value, x => -x, -x => x */
int chs (uint64_t x);

/* rotate left and rotate right 32-bit functions */
inline uint32_t rotl    (uint32_t value, int shift);
inline uint32_t rotr    (uint32_t value, int shift);
/* special case rotate functions */
inline uint8_t  rotl_8  (uint8_t  x, int shift);
inline uint8_t  rotr_8  (uint8_t  x, int shift);
inline uint16_t rotl_16 (uint16_t x, int shift);
inline uint16_t rotr_16 (uint16_t x, int shift);
inline uint64_t rotl_64 (uint64_t x, int shift);
inline uint64_t rotr_64 (uint64_t x, int shift);

/* IEEE 754 functions */
uint32_t u2ieee (uint32_t x);
uint32_t i2ieee (int32_t x);
double ieee32dbl (int val);

/* swap macro & type functions */
#define swap(x,y) do \
   { unsigned char swap_temp[sizeof(x) == sizeof (y) ? (signed)sizeof (x) : -1]; \
     memcpy (swap_temp,&y,sizeof (x)); \
     memcpy (&y,&x,       sizeof (x)); \
     memcpy (&x,swap_temp,sizeof (x)); \
    } while (0)

inline void swapi (int *a, int *b);
inline void swapu (unsigned int *a, unsigned int *b);
inline void swapl (long *a, long *b);
inline void swapf (float *a, float *b);
inline void swapd (double *a, double *b);
inline void swapc (char *a, char *b);

/* return minimum integer storage size based on msb */
inline int
bit_fldsz (unsigned long val);

/* bit functions */
inline void bit_setc    (uint8_t *bf, int n);
inline void bit_set     (uint64_t *bf, int n);
inline void bit_clearc  (uint8_t *bf, int n);
inline void bit_clear   (uint64_t *bf, int n);
inline void bit_togglec (uint8_t *bf, int n);
inline void bit_toggle  (uint64_t *bf, int n);

/* (bit == 1) ? return 1 : 0, on error return -1 */
inline int bit_isset (unsigned long bf, int n);

/* (bit == 1) ? return bitvaule : 0, on error return -1,
   loop to retrieve all set bits from bitfield/bitarray */
inline int bit_value (unsigned long bf, int n);

#endif /* _lib_binstr_ */

Example output for the binstr library is shown below:

  Testing binstr_x (uintX_t x), getmsb (uint64_t x), getlsb (uint64_t)

 x :        216,  hex:       d8, (msb, lsb) :  7,  3  binary: 11011000
 x :      58251,  hex:     e38b, (msb, lsb) : 15,  0  binary: 1110001110001011
 x : 2047483648,  hex: 7a0a1f00, (msb, lsb) : 30,  8  binary: 01111010000010100001111100000000
 x : 9023372036854775808,  hex: 7d39750f44ec0000, (msb, lsb) : 62, 18
binary: 0111110100111001011101010000111101000100111011000000000000000000

Testing fmtbinstr_x (uintX_t x, size_t szgrp, char *sep)

 x :        216,  hex:       d8,   fmtbinstr_8  (a, 4, "-") : 1101-1000
 x :      58251,  hex:     e38b,   fmtbinstr_16 (b, 4, "-") : 1110-0011-1000-1011
 x : 2047483648,  hex: 7a0a1f00,   fmtbinstr_32 (c, 4, "-") : 0111-1010-0000-1010-0001-1111-0000-0000
 x : 9023372036854775808,  hex: 7d39750f44ec0000,
fmtbinstr_64 (d, 4, "-"): 0111-1101-0011-1001-0111-0101-0000-1111-0100-0100-1110-1100-0000-0000-0000-0000

Test of size group (szgrp : 1-8) with fmtbinstr_16 (511, x, "-")

 fmtbinstr_16 (511, 1, "-"): 0-0-0-0-0-0-0-1-1-1-1-1-1-1-1-1
 fmtbinstr_16 (511, 2, "-"): 00-00-00-01-11-11-11-11
 fmtbinstr_16 (511, 3, "-"): 0-000-000-111-111-111
 fmtbinstr_16 (511, 4, "-"): 0000-0001-1111-1111
 fmtbinstr_16 (511, 5, "-"): 0-00000-01111-11111
 fmtbinstr_16 (511, 6, "-"): 0000-000111-111111
 fmtbinstr_16 (511, 7, "-"): 00-0000011-1111111
 fmtbinstr_16 (511, 8, "-"): 00000001-11111111

Testing  bin_nopad (char *str) to strip leading 0's

 x : 338, hex: 152
 binary: 0000000101010010  (char *bsp)
 no pad:        101010010

Testing binstr_sep (char *str, size_t szgrp, char *sep)

 binstr_sep (bsp, 4, " "): 0000 0001 0101 0010
 binstr_sep (bsp, 4, "-"): 0000-0001-0101-0010
 binstr_sep (bsp, 2, ":"): 00:00:00:01:01:01:00:10

Testing msb, lsb, n1s, nlz, ntz:

  value : 0000000111111000  (504)
  getmsb: 8
  getlsb: 3
  getn1s: 6  (population of 1's)
  nlz   : 7  (number of leading 0's)
  ntz   : 3  (number of trailing 0's)

Testing binstr_8 & binstr_8d - illustrate static buffer issue:

 88 & 87 = 80  (01010000)

 --------------- E R R O R ---------------
 |  multiple binstr_x calls in printf    |
 |  fail due to static char buffer.      |
 -----------------------------------------
 88  01011000  rightmost bit off: 01011000

 88  01011000
 87  01011000
  &  ---------
     01011000

 -----------------  OK  ------------------
 |  split into multiple printf calls     |
 -----------------------------------------
 88  01011000  rightmost bit off: 01010000

 88  01011000
 87  01010111
  &  ---------
     01010000

 -----------------  OK  ------------------
 |  multiple binstr_xd calls in printf   |
 -----------------------------------------
 88  01011000  rightmost bit off: 01010000

 88  01011000
 87  01010111
  &  ---------
     01010000
 -----------------------------------------

Testing Conversions to IEEE 754 Format

  val : 12345
  u2f : 1178657792
  hex : 0x4640e400
  bin : 01000110010000001110010000000000

  val : 12345
  i2f : 1178657792
  hex : 0x4640e400
  bin : 01000110010000001110010000000000

IEEE 754 -> double

  ieee 754  ( 1178657792 )  =  12345.000000

Original values for swap testing:

   char     int      float
  f:  a  | h:  1  | j:  3.4
  g:  b  | i:  2  | k:  5.6

 Values after swap macro:

   char     int      float
  f:  b  | h:  2  | j:  5.6
  g:  a  | i:  1  | k:  3.4

 Values after swap functions:

   char     int      float
  f:  a  | h:  1  | j:  3.4
  g:  b  | i:  2  | k:  5.6

Testing bitfield  58251 -> 1110001110001011

  bit_isset ( 0, 58251) : 1
  bit_isset ( 1, 58251) : 1
  bit_isset ( 2, 58251) : 0
  bit_isset ( 3, 58251) : 1
  bit_isset ( 4, 58251) : 0
  bit_isset ( 5, 58251) : 0
  bit_isset ( 6, 58251) : 0
  bit_isset ( 7, 58251) : 1
  bit_isset ( 8, 58251) : 1
  bit_isset ( 9, 58251) : 1
  bit_isset (10, 58251) : 0
  bit_isset (11, 58251) : 0
  bit_isset (12, 58251) : 0
  bit_isset (13, 58251) : 1
  bit_isset (14, 58251) : 1
  bit_isset (15, 58251) : 1

Testing bitfield values 58251 -> 1110001110001011

  bit_isset ( 0, 58251) : 1
  bit_isset ( 1, 58251) : 2
  bit_isset ( 2, 58251) : 0
  bit_isset ( 3, 58251) : 8
  bit_isset ( 4, 58251) : 0
  bit_isset ( 5, 58251) : 0
  bit_isset ( 6, 58251) : 0
  bit_isset ( 7, 58251) : 128
  bit_isset ( 8, 58251) : 256
  bit_isset ( 9, 58251) : 512
  bit_isset (10, 58251) : 0
  bit_isset (11, 58251) : 0
  bit_isset (12, 58251) : 0
  bit_isset (13, 58251) : 8192
  bit_isset (14, 58251) : 16384
  bit_isset (15, 58251) : 32768
  =====================(+)======
    Total Sum of Values : 58251

Testing bit functions, set, clear, toggle, etc...
  b : 58251     =>    1110001110001011
  bit_set (58255,2):  1110001110001111
  bit_set (58271,4):  1110001110011111
  bit_set (58303,5):  1110001110111111
  b : 58303     =>    1110001110111111

Testing circular-rotate left/right on 8 bit number

  122  01111010

 rotate-left:

  244  11110100    rotl_8 (122, 1)
  233  11101001    rotl_8 (122, 2)
  211  11010011    rotl_8 (122, 3)
  167  10100111    rotl_8 (122, 4)
   79  01001111    rotl_8 (122, 5)
  158  10011110    rotl_8 (122, 6)
   61  00111101    rotl_8 (122, 7)
  122  01111010    rotl_8 (122, 8)

 rotate-right:

   61  00111101    rotr_8 (122, 1)
  158  10011110    rotr_8 (122, 2)
   79  01001111    rotr_8 (122, 3)
  167  10100111    rotr_8 (122, 4)
  211  11010011    rotr_8 (122, 5)
  233  11101001    rotr_8 (122, 6)
  244  11110100    rotr_8 (122, 7)
  122  01111010    rotr_8 (122, 8)
  

Another test of highlight

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define INITWKR 25

typedef struct {
    char *name;             /* worker name      */
    int *time;              /* time array (min) */  /* optional     */
    unsigned entries;       /* entris in time   */
    unsigned size;          /* allocated size   */  /* realloc test */
    int total;              /* total time (min) */
} worker;

int main () {

    char *name = NULL;      /* name input       */
    int tio = 0;            /* time input       */
    int idx = 0;            /* workers index    */
    int idxmax = 0;         /* index max time   */
    int tdx = 0;            /* time index       */
    int tmax = 0;           /* max worker time  */
    char exist = 0;         /* wkr exists flag  */

    /* create INITWKR pointers to struct (initialize NULL) */
    worker **wkrs = calloc (INITWKR, sizeof (*wkrs));
    if (!wkrs) {
        fprintf (stderr, "error: virtual memory exhausted.\n");
        return 1;
    }

    printf ("\nPunch the clock, enter name & minutes worked ([enter] alone to end)\n");

    /* input loop, read name and minutes work for each shift */
    while (printf ("\n  name: ") && scanf ("%m[^\n]%*c", &name) >= 1 &&
            printf ("  time: ") && scanf ("%d%*c", &tio) >= 1 )
    {
        idx = 0;            /* reset loop variables     */
        exist = 0;

        while (wkrs[idx])   /* check each filled worker */
        {
            /* check if already has struct  */
            if (strcmp (wkrs[idx]->name, name) == 0) {
                exist = 1;
                break;
            }
            idx++;          /* points to next avail pointer */
        }
        /* if (idx >= INITWKR - 1) reallocate poiner array  */

        if (!exist) {
            /* add new worker / verify each allocation */
            if (!(wkrs[idx] = malloc (sizeof (**wkrs)))) {
                fprintf (stderr, "error: virtual memory exhausted.\n");
                return 1;
            }
            if (!(wkrs[idx]-> name = strdup (name))) {
                fprintf (stderr, "error: virtual memory exhausted.\n");
                return 1;
            }
             if (!(wkrs[idx]-> time = calloc (INITWKR, sizeof (int)))) {
                fprintf (stderr, "error: virtual memory exhausted.\n");
                return 1;
            }
            wkrs[idx]-> entries = 0;
            wkrs[idx]-> size = INITWKR;
            wkrs[idx]-> total = 0;
        }

        /* add time to worker */
        tdx = 0;
        while ((wkrs[idx]-> time)[tdx])
            tdx++;
        /* if (tdx >= wkrs[idx]-> size - 1)
               reallocate wkrs[idx]-> time, increment size */
        (wkrs[idx]-> time)[tdx] = tio;
        wkrs[idx]-> entries++;
        wkrs[idx]-> total += tio;
        if (wkrs[idx]-> total > tmax) {
            tmax = wkrs[idx]-> total;
            idxmax = idx;
        }

        if (name) free (name);  /* free memory allocate by scanf    */
        name = NULL;
    }

    printf ("\nWorker Time Summary:\n\n");

    idx = 0;
    while (wkrs[idx]) {     /* output worker name/time & max    */
        if (idx == idxmax)
            printf ("  Worker[%2d] : %-24s   time: %d  (max time)\n", idx, wkrs[idx]->name, wkrs[idx]->total);
        else
            printf ("  Worker[%2d] : %-24s   time: %d\n", idx, wkrs[idx]->name, wkrs[idx]->total);
        idx++;
    }
    printf ("\n");

    idx = 0;
    while (wkrs[idx]) {     /* free dynamically allocated mem   */
        if (wkrs[idx]->name) free (wkrs[idx]->name);
        if (wkrs[idx]->time) free (wkrs[idx]->time);
        if (wkrs[idx]) free (wkrs[idx++]);
    }
    free (wkrs);

    return 0;
}

// **example:**
//
//     $ ./bin/workers
//
//     Punch the clock, enter name & minutes worked ([enter] alone to end)
//
//       name: JD Clark
//       time: 38
//
//       name: Mike Wu
//       time: 34
//
//       name: JD Clark
//       time: 39
//
//       name: Mike Wu
//       time: 53
//
//       name: JD Clark
//       time: 64
//
//       name: Tim Taylor
//       time: 55
//
//       name:
//
//     Worker Time Summary:
//
//       Worker[ 0] : JD Clark                   time: 141  (max time)
//       Worker[ 1] : Mike Wu                    time: 87
//       Worker[ 2] : Tim Taylor                 time: 55

Still Under Construction


  1. The Gnome Desktop on Linux has been one of the two major desktop available for the past decade. It has relied almost exclusively on C for its desktop libraries (Gtk1, Gtk+2, Gtk+3 & Gtk4). You can find more about the Gnome GTK libraries here: GTK+ 3 Reference Manual
  2. Linus Torvalds, the founder of Linux, is a strong proponent of C. I think he explains his opinion on the comparison between C and C++ in an elegant manner in this semi-famous exchange during git development: Linus's Thoughts on C/C++

Developed in KDE3:

Quanta+ from KDE3 KDE3 now developed as Trinity Desktop