What’s The Difference Between C – Now and Then

What’s The Difference Between C – Now and Then

Dennis Ritchie started developing the C programming language (Fig. 1) in 1969 at AT&T’s Bell Labs. In 1972, Dennis Ritchie and Brian Kernighan delivered the quintessential book, The C Programming Language, that defined C. C has progressed over the years although it has changed less than most other popular languages. The latest standard is C11.

Figure 1. The logo on cover of The C Programming Language by Dennis Ritchie and Brian Kernighan is well known to C programmers.

Table Of Contents

The C Programming Language

The C Programming Language book by Dennis Ritchie and Brian Kernighan, laid out the C language in 1972, also known as "K&R" C. There have been enumerable programming languages defined in the same manor but few have reached the adoption that C has achieved.

C was a step above assembler that was the norm for systems programming at the time while providing hardware-level capabilities with features such as pointers. This has turned out being an advantage as well as disadvantage for C. On the plus side, it mimics the underlying hardware. Unfortunately pointers also provide the mechanism for many problems such as dangling pointers and uninitialized pointers. Likewise, the lack of checking found in other languages like Ada or Java can lead to buffer overflows.

Most programmers will recognize the ever popular Hello World program.

#include 

void main () {
  printf (“Hello World”);
}

C included basic, preprocessor-based macro support with functional substitution. Inline functions were added in later C standards. C uses curly brackets to mark blocks of code including function definitions. It includes type checking but casts can be used to convert between data types. Data types included compound structures and arrays.

C was used as the system programming language for AT&T’s UNIX and eventually Linux as well as a host of other operating systems.

C89/C90, ANSI C

In 1983, the American National Standards Institute (ANSI) formed the X3J11 committee that developed the first ANSI C known as C89. It was essentially based on the standard UNIX C compiler of the time.

C89 incorporated more than K&R C adding features like function prototypes. It also supports void pointers that must be cast to access data, as well as international character set support. Function parameter declarations was improved although the K&R C version was supported as well for compatibility otherwise many applications would have to be rewritten to work with C89 compilers.

In 1990, International Organization for Standardization (ISO) created ISO/IEC 9899:1990, or C90, that matched ANSI’s C89. Most C compilers support C89/C90 at a minimum with extensions or support for other C standards like C99 as well. The ISO/IEC JTC1/SC22/WG14 working group now develops the C standards.

C99, ANSI C

The ISO/IEC 9899:1999 standard, also known as C99, was released in 1999 and adopted in 2000. It added a number of new features including:

  • inline functions, preferred over macros because it allows type checking
  • variable-length arrays (VLA) where the array length is a runtime expression
  • restrict pointers
  • C++ style // one line comments
  • type-generic math functions
  • designated initializers for aggregate types including arrays
  • compound literals for structures and arrays
  • variable number of arguments for macros – also known as variadic macros
  • restrict qualification allows more aggressive code optimization
  • universal character set support for names

C99 also allows intermingling for declarations and code. This is similar to C++ where variable declarations can be almost anywhere it makes sense. The standard adds several data types such as long long int and there is now an explicit boolean data type. The complex data type means complex numbers can be defined without resorting to non-standard structures. Support for implementation-specific integer types is included. It is called optional extended integer types. Floating point support now includes IEEE 754-1985 or IEC 60559 floating point definitions that match hardware support found on most microprocessors.

C99 has new library functions like snprintf. It also adds new standard header files like stdbool.h, complex.h, tgmath.h, and inttypes.h for Boolean, complex number, math and new integer types.

C99 has yet to displace C89. Most compilers currently available still support C89.

Embedded C

Programmers have been using C for embedded applications for decades. This was often a challenge when using processors that included special addressing mechanisms, multibank memory and non-memory-based I/O operations. C support often required the use of non-standard compiler features such as special pointer types.

Many features, such as fixed point arithmetic, were common enough to warrant a standard like Embedded C. The ISO/IEC JTC1/SC22/WG14 working group provided these as extensions to C. They are defined independent of other standards and add features like I/O registers, fixed-point arithmetic, named address spaces along with name register storage classes, and added the iohw.h header file.

MISRA-C

C has become notorious for allowing programmers “to shoot themselves in the foot.” This essentially means that C’s power and flexibility can be easily misused to cause catastrophic bugs in a program. This includes bugs like dangling pointers.

MISRA (Motor Industry Software Reliability Association) defined MISRA C to address this problem. MISRA-C consists of a set of rules, most of which can be implemented using a static analysis tool. Static analysis tools for C and C++ (see What's The Difference Between Static Analysis of C and C++ Versus Java Programs) provide a way to detect these problems in general and MISRA C is a specific implementation.

MISRA-C was initially defined for automotive applications but it has been utilized for many safety and security related applications as well. It does not define a new version of C but rather limits some of the C features with the objective of making bug free programs easier while making bugs harder to create.  

The 1998 version, MISRA-C:1998, had 127 rules. There are 93 that are required and 34 that were advisory rules. Most rules can be implemented by a static analysis tool but some are more procedural oriented. Most compilers that support MISRA-C allow rules to be applied selectively. For example, pointers cannot be used if all rules are utilized. Array access can be used instead of pointers for most chores and today’s optimizing compilers do a better job of pointer usage behind the scenes than most programmers could do with explicit pointer code.

MISRA-C:2004 has 142 rules including 122 required and 20 advisory. MISRA-C:2012 contains 143 rules plus 16 directives. Directives are compliance rules. All the MISRA-C:2012 rules can all be implemented using static analysis tools compared to the prior versions that mixed directives in the list of rules.

The MISRA-C rules can be divided into a number of categories:

  • Best practice rules
  • Complexity limits
  • Compiler differences such as data type size differences
  • Error prone constructs such as pointers and dynamic memory allocation
  • Maintainability features such as comments and naming conventions

MISRA-C is often a subset of more robust static analysis and process management tools available to C programmers.

C11, ANSI C

C11 is the latest ANSI C specification, ISO/IEC 9899:2011. C11 looked to address the issues of C99 and to more closely match the C++ standard, C++11. It changes some C99 features required to optional. Some of the features include variable length arrays and complex numbers. This makes it easier for compiler vendors to meet C11’s required function set.

Of course, C11 does not just cut back. There are additions and most are greatly appreciated. They also map to C++11 features making interfacing C and C++ code easier. Unicode support is a major fix. C11 also provides compliant support for IEC 60559 floating-point arithmetic including IEC 60559 complex arithmetic. This floating point support is optional as is many of the new features.

A major optional feature is multithreading support. This is something that has always been given over to an underlying OS or libraries like POSIX. This made multithreaded applications less portable. The new interface provides a standard way to handle threads that can be mapped to multitasking OS or library support. The _Thread_local declaration provides a mechanism for defining variables that are global to a particular thread.

Anonymous structs and unions are handy for embedded applications. An anonymous union in a struct definition allows union elements to be accessed using the element name only. Non-anonymous unions would have a name and that name, along with the element name, would be required to access the element.

Another feature embedded developers will find useful are the alignas and alignof keywords. The alignas keyword is used as a prefix to indicate variable alignment. The alignof returns the alignment in bytes for a given type.

Buffer overflow bugs have always haunted C programmers and the standard string manipulation programmers are often where these problems arise. C11 adds bounds checking functions to the string library. The new function names match the old plus a trailing _s as in strcat_s and strncpy_s. These new functions usually have an extra size parameter that is used to limit the string operations so a missing null terminator will not allow the functions to overrun the end of a buffer.

The C11 standard is relatively new. The open source gcc compiler supports the C11 and C++11 standard.

 

Hide comments

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish