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
- 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
- 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++