Search

The Online Encyclopedia and Dictionary

 
     
 

Encyclopedia

Dictionary

Quotes

   
 

C programming language


The C programming language is a low-level standardized programming language developed in the early 1970s by Ken Thompson and Dennis Ritchie for use on the UNIX operating system. It has since spread to many other operating systems, and is one of the most widely used programming languages. C is prized for its efficiency, and is the most popular programming language for writing system software, though it is also used for writing applications. It is also commonly used in computer science education, despite not being designed for novices.

Contents

Features

Overview

C is a relatively minimalist programming language that operates close to the hardware, and is more similar to assembly language than most other programming languages. Indeed, C is sometimes referred to as "portable assembly," reflecting its important difference from assembly languages: C code can be compiled for and run on almost any machine, more than any other language in existence, while assembly languages run on at most a few very specific models of machines. C is typically called a low level or medium level language, indicating how closely it operates with the hardware.

This is no accident; C was created with one important goal in mind: to make it easier to write large programs with fewer errors in the procedural programming paradigm, but without putting a burden on the writer of the C compiler, who is encumbered by complex language features. To this end, C has the following important features:

Some features that C lacks that are found in other languages include:

Although the list of useful features C lacks is long, this has not been important to its acceptance, because it allows new compilers to be written quickly for it on new platforms, and because it keeps the programmer in close control of what the program is doing. This is what often allows C code to run more efficiently than many other languages. Typically only hand-tuned assembly language code runs more quickly, since it has complete control of the machine, but advances in compilers along with new complexity in modern processors have quickly narrowed this gap.

One consequence of C's wide acceptance and efficiency is that the compilers, libraries, and interpreters of other higher-level languages are often implemented in C.

"Hello, World!" in C

The following simple application appeared in the first edition of K&R, and has become a standard introductory program in most textbooks on C. The program prints out "Hello, World!" to standard output (which is usually the screen, but might be a file or some other hardware device or perhaps even the bit bucket depending on how standard output is mapped at the time the program is executed).


main()
{
    printf("Hello, World!\n");
}

Although the above program will compile correctly under most modern compilers when invoked in a non-conforming mode, it now produces several warning messages when compiled with a compiler that conforms to the ANSI C standard. (Additionally, the code will not compile if the compiler strictly conforms to the C99 standard, as a return value of type int will no longer be inferred if the source code has not specified otherwise.) These messages can be eliminated with a few minor modifications to the original program:


#include <stdio.h>

int main(void)
{
    printf("Hello, World!\n");

    return 0;
}

The first line of the program is an #include preprocessing directive, which causes the compiler to substitute for that line the entire text of the file (or other entity) it refers to; in this case the standard header stdio.h will replace that line. The angle brackets indicate that the stdio.h header is to be found in whatever place is designated for the compiler to find standard headers.

The next (non-blank) line indicates that a function named "main" is being defined; the main() function is special in C programs, as it is the function that is first run when the program starts (for hosted implementations of C, and leaving aside "housekeeping" code). The curly brackets delimit the extent of the function. The int defines "main" as a function that returns, or evaluates to, an integral number; the void indicates that no arguments or data must be given to function main by its caller.

The next line "calls," or executes, a function named printf; the included header, stdio.h, contains information describing how the printf function is to be called. In this call, the printf function is passed a single argument, the constant string "Hello, World!\n"; the sequence \n is translated to a "newline" character, which when displayed causes a line break. printf returns a value, an int, but since it is not used it is discarded quietly.

The return statement tells the program to exit the current function (in this case main), returning the value zero to the function that called the current function. Since the current function is main, the caller is whatever started our program. Finally, the close curly bracket indicates the end of the function main.

Comment text

Note that text surrounded by /* and */ (comment text) is ignored by the compiler. C99-compliant compilers also allow comments to be introduced with //, indicating that the comment extends to the end of the current line.

Types

C has a type system similar to that of other Algol descendants such as Pascal. There are types for integers of various sizes, both signed and unsigned, floating-point numbers, characters, enumerated types (enums), and records (structs). In addition, C has a union type, which allows the programmer to create an object that can hold object of various types, but only one at a time.

C makes extensive use of pointers, a very simple type of reference that stores the address of a memory location. The pointer can be dereferenced, an operation which retrieves the object stored at the memory location the pointer contains, and the address can be manipulated with pointer arithmetic. At runtime, a pointer is usually a machine address like those manipulated in assembly, but at compile-time it has a complex type that indicates the type of the object it points to, allowing expressions including pointers to be type-checked. Pointers are used widely in C; the C string type is simply a pointer to an array of characters, and dynamic memory allocation, described below, is performed using pointers.

Pointers in C have a special reserved null value which indicates that they are not pointing to anything. This is useful in constructing many data structures, but causes undefined behavior if dereferenced. A pointer with the null value is called a null pointer. C pointers also have a special void pointer type, meant to indicate a pointer that points to an object of unknown type.

C also has language-level support for static, or fixed-size, arrays. The arrays can appear to have more than one dimension, although they are logically arrays of arrays (e.g., tbl[10][20] rather than tbl[10,20]) and physically laid out as one-dimensional arrays, with pointers to subarrays being computed. Dimensions are laid out in row-major order. Arrays are accessed using pointers and pointer arithmetic; the array name is treated in most contexts as a pointer to the beginning of the array. In many applications, having fixed-size arrays is unreasonable, and so dynamic memory allocation can be used to create dynamically-sized arrays (see Data storage below).

Because C is often used in low-level systems programming, there are cases where it's actually necessary to treat an integer as an address, a floating-point number as an integer, or one type of pointer as another. For these, C supplies casting, an operation that forces an explicit conversion of an object from one type to another, if this is possible. While sometimes necessary, the use of casts sacrifices some of the safety normally provided by the type system.

Data storage

One of the most important functions of a programming language is to provide facilities for managing memory and the objects that are stored in memory. C provides three distinct ways of allocating memory for objects:

  • Static memory allocation: space for the object is provided in the binary at compile-time; these objects have a lifetime as long as the binary which contains them exists
  • Automatic memory allocation : temporary objects can be stored on the stack, and this space is automatically freed and reusable after the block they are declared in is left
  • Dynamic memory allocation: blocks of memory of any desired size can be requested at run-time using the library functions malloc(), realloc(), and free() from a region of memory called the heap; these blocks are reused after free() is called on them

These three approaches are appropriate in different situations and have various tradeoffs. For example, static memory allocation has no allocation overhead, automatic allocation has a small amount of overhead during initialization, and dynamic memory allocation can potentially have a great deal of overhead for both allocation and deallocation. On the other hand, stack space is typically much more limited than either static memory or heap space, and only dynamic memory allocation allows allocation of objects whose size is only known at run-time. Most C programs make extensive use of all three.

Where possible, automatic or static allocation is usually preferred because the storage is managed by the compiler, freeing the programmer of the error-prone hassle of manually allocating and releasing storage. Unfortunately, many data structures can grow in size at runtime; since automatic and static allocations must have a fixed size at compile-time, there are many situations in which dynamic allocation must be used. Variable-sized arrays are a common example of this (see "malloc" for an example of dynamically allocated arrays).

C syntax

See main article: C syntax

Problems with C

A popular saying is that C makes it easy to shoot yourself in the foot. In other words, C permits many operations that are generally not desirable, and thus many simple errors made by a programmer are not detected by the compiler or even when they occur at runtime, leading to programs with unpredictable behavior. Part of the reason for this is to avoid compile and runtime checks that were costly when C was originally designed and to avoid the extra syntax that other languages require to disable such checks.

One problem is that automatically and dynamically allocated objects are not initialized; they initially have whatever value is present in the memory space they are assigned. This value is highly unpredictable, and can vary between two machines, two program runs, or even two calls to the same function. If the program attempts to use such an uninitialized value, the results are usually unpredictable. Most modern compilers detect and warn about this problem in some restricted cases.

C's pointers are one primary source of danger; because they are unchecked, a pointer can be made to point to any object of any type, including code, and then written to, causing unpredictable effects. Although most pointers point to safe places, they can be moved to unsafe places using pointer arithmetic, the memory they point to may be deallocated and reused (dangling pointers), or they may be uninitialized (wild pointers). Another problem with pointers is that C freely allows conversion between any two pointer types. Other languages attempt to address these problems by using more restrictive reference types.

Although C has native support for static arrays, it does not verify that array indexes are valid (bounds checking). For example, one can write to the sixth element of an array with five elements, yielding unpredictable results. This is called a buffer overflow. This has been notorious as the source of a number of security problems in C-based programs.

Generally these security issues stem from overflows occurring when the program reads input into stack memory that is provided from an outside source. The intent of the malicious user is to cause stack corruption by taking advantage of improper bounds checking and injecting a series of machine instructions. Once injected the user need only cause the program to vector to those instructions by overwriting the current function's return address, which is relatively simple since most platforms store the return address on the stack above any local variables. After this corruption, the malicious instructions are invoked when the current function returns. Some platforms support the prevention of executing instructions on the stack; however, a programmer cannot rely on this functionality, as it may be possible to inject instructions into other segments of memory.

Another common problem in C is that heap memory cannot be reused until it is explicitly released by the programmer with free(). The result is that if the programmer accidentally forgets to free memory, but continues to allocate it, more and more memory will be consumed over time. This is called a memory leak. Conversely, it's possible to release memory too soon, and then continue to use it. Because the allocation system can reuse the memory at any time for unrelated reasons, this results in insidiously unpredictable behavior. These issues in particular are ameliorated in languages with automatic garbage collection.

Yet another common problem are variadic functions, which take a variable number of arguments. Unlike other prototyped C functions, checking the arguments of variadic functions at compile-time is not mandated by the standard. If the wrong type of data is passed, the effect is unpredictable, and often fatal. Variadic functions also handle null pointer constants in an unexpected way. For example, the printf family of functions supplied by the standard library, used to generate formatted text output, is notorious for its error-prone variadic interface, which relies on a format string to specify the number and type of trailing arguments. Type-checking of variadic functions from the standard library is a quality of implementation issue, however, and many modern compilers do in particular type-check printf calls, producing warnings if the argument list is inconsistent with the format string. It should be noted that not all printf calls can be checked statically (this is difficult as soon as the format string itself comes from somewhere hard to trace), and other variadic functions typically remain unchecked.

Tools have been created to help C programmers avoid many of these errors in many cases. Automated source code checking and auditing is fruitful in any language, and for C many such tools exist, such as Lint. A common practice is to use Lint to detect questionable code when a program is first written. Once a program passes Lint, it would then be compiled using the C compiler. There are also libraries for performing array bounds checking and a limited form of automatic garbage collection, but they are not a standard part of C.

History

Early developments

The initial development of C occurred at AT&T Bell Labs between 1969 and 1973; according to Ritchie, the most creative period occurred in 1972. It was named "C" because many of its features were derived from an earlier language called "B". Accounts differ regarding the origins of the name "B": Ken Thompson credits the BCPL programming language, but he had also created a language called Bon in honor of his wife Bonnie.

There are many legends as to the origin of C and its related operating system, Unix, including:

  • The development of C was the result of the programmer's desire to play an Asteroids-like game. They had been playing it on their company's mainframe, but being underpowered and having to support about 100 users, Thompson and Ritchie found they didn't have sufficient control over the spaceship to avoid collisions with the wandering space rocks. Thus, they decided to port the game to an idle PDP-7 in the office. But it didn't have an operating system (OS), so they set about writing one. Eventually they decided to port the operating system to the office's PDP-11, but this was onerous since all the code was in assembly language. They decided to use a higher-level portable language so the OS could be ported easily from one computer to another. They looked at using B, but it lacked functionality to take advantage of some of the PDP-11's advanced features. So they set about creating the new language, C.
  • The justification for obtaining the original computer that was used to develop Unix was to create a system to automate the filing of patents. The original version of Unix was developed in assembly language. Later, the C language was developed in order to rewrite the operating system.

By 1973, the C language had become powerful enough that most of the UNIX kernel, originally written in PDP-11/20 assembly language, was rewritten in C. This was one of the first operating system kernels implemented in a language other than assembly, earlier instances being the Multics system (written in PL/I) and TRIPOS (written in BCPL).

K&R C

In 1978, Ritchie and Brian Kernighan published the first edition of The C Programming Language. This book, known to C programmers as "K&R", served for many years as an informal specification of the language. The version of C that it describes is commonly referred to as "K&R C." (The second edition of the book covers the later ANSI C standard, described below.)

K&R introduced the following features to the language:

  • struct data types
  • long int data type
  • unsigned int data type
  • The =+ operator was changed to +=, and so forth (=+ was confusing the C compiler's lexical analyzer; for example, i =+ 10 compared with i = +10).

K&R C is often considered the most basic part of the language that is necessary for a C compiler to support. For many years, even after the introduction of ANSI C, it was considered the "lowest common denominator" that C programmers stuck to when maximum portability was desired, since not all compilers were updated to fully support ANSI C, and reasonably well-written K&R C code is also legal ANSI C.

In the years following the publication of K&R C, several "unofficial" features were added to the language, supported by compilers from AT&T and some other vendors. These included:

  • void functions and void * data type
  • functions returning struct or union types
  • struct field names in a separate name space for each struct type
  • assignment for struct data types
  • const qualifier to make an object read-only
  • a standard library incorporating most of the functionality implemented by various vendors
  • enumerations
  • the single-precision float type

ANSI C and ISO C

During the late 1970s, C began to replace BASIC as the leading microcomputer programming language. During the 1980s, it was adopted for use with the IBM PC, and its popularity began to increase significantly. At the same time, Bjarne Stroustrup and others at Bell Labs began work on adding object-oriented programming language constructs to C. The language they produced, called C++, is now the most common application programming language on the Microsoft Windows operating system; C remains more popular in the Unix world.

In 1983, the American National Standards Institute (ANSI) formed a committee, X3J11, to establish a standard specification of C. After a long and arduous process, the standard was completed in 1989 and ratified as ANSI X3.159-1989 "Programming Language C". This version of the language is often referred to as ANSI C. In 1990, the ANSI C standard (with a few minor modifications) was adopted by the International Organization for Standardization (ISO) as ISO/IEC 9899:1990.

One of the aims of the ANSI C standardization process was to produce a superset of K&R C, incorporating many of the unofficial features subsequently introduced. However, the standards committee also included several new features, such as function prototypes (borrowed from C++), and a more capable preprocessor.

ANSI C is now supported by almost all the widely used compilers. Most of the C code being written nowadays is based on ANSI C. Any program written only in standard C is guaranteed to perform correctly on any platform with a conforming C implementation. However, many programs have been written that will only compile on a certain platform, or with a certain compiler, due to (i) the use of non-standard libraries, e.g. for graphical displays, and (ii) some compilers not adhering to the ANSI C standard, or its successor, in their default mode.

C99

After the ANSI standardization process, the C language specification remained relatively static for some time, whereas C++ continued to evolve. (Normative Amendment 1 created a new version of the C language in 1995, but this version is rarely acknowledged.) However, the standard underwent revision in the late 1990s, leading to the publication of ISO 9899:1999 in 1999. This standard is commonly referred to as "C99". It was adopted as an ANSI standard in March 2000.

The new features in C99 include:

  • inline functions
  • freeing of restrictions on the location of variable declarations (as in C++)
  • addition of several new data types, including long long int (to reduce the pain of the looming 32-bit to 64-bit transition), an explicit boolean data type, and a complex type representing complex numbers
  • variable-length arrays
  • support for one-line comments beginning with //, borrowed from C++, and which many C compilers have been supporting as an extension
  • several new library functions, such as snprintf()
  • several new header files, such as stdint.h

Interest in supporting the new C99 features appears to be mixed. Whereas GCC and several other compilers now support most of the new features of C99, the compilers maintained by Microsoft and Borland do not, and these two companies do not seem to be interested in adding such support.

Relation to C++

The C++ programming language was originally derived from C. As C and C++ have evolved independently, there has been an unfortunate growth in the number of incompatibilities between the two languages. The latest revision of C, C99, created a number of conflicting features. The differences make it hard to write programs and libraries that are compiled and function correctly as either C or C++ code, and confuse those who program in both languages.

Bjarne Stroustrup, the creator of C++, has repeatedly suggested [1] http://www.research.att.com/~bs/sibling_rivalry.pdf that the incompatibilities between C and C++ should be reduced as far as possible in order to maximize interoperability between the two languages. Others have argued that since C and C++ are two different languages, compatibility between them is useful but not vital; according to this camp, efforts to reduce incompatibility should not hinder attempts to improve each language in isolation.

Today, the primary differences between the two languages are:

  • inlineinline functions are in the global scope in C++, and in the file (so-called "static") scope in C. In simple terms, this means that in C++, any definition of any inline function (but irrespective of C++ function overloading) must conform to C++'s "One Definition Rule " or ODR, requiring that either there be a single definition of any inline function or that all definitions be semantically equivalent; but that in C, the same inline function could be defined differently in different translation units (translation unit typically refers to a file).
  • The bool keyword in C99 is in its own header, <stdbool.h>. Previous Standards of C did not define a boolean type, and various (incompatible) methods were used to simulate a boolean type.
  • Character constants (enclosed in single quotes) have the size of an int in C and a char in C++. That is to say, in C, sizeof('a') == sizeof(int); in C++, sizeof('a') == sizeof(char). Nevertheless, even in C they will never exceed the values that a char can store, so (char)'a' is a safe conversion.

C has adopted some features that first appeared in C++. Among them are:

  • Prototype declarations for functions
  • Line comments, indicated by //; line comments end with a newline character
  • The inline keyword
  • Stronger typing including the addition of the void type and const qualifier and the removal of the "implicit int" return value

Common practices

With its extensive use, a number of common practices and conventions have evolved to help avoid errors in C programs. These are simultaneously a demonstration of the application of good software engineering principles to a language and an indication of the limitations of C. Although few are used universally, and some are controversial, each of these enjoys wide use.

Dynamic multidimensional arrays

Although one-dimensional arrays are easy to create dynamically using malloc, and fixed-size multidimensional arrays are easy to create using the built-in language feature, dynamic multidimensional arrays are trickier. There are a number of different ways to create them, each with different tradeoffs. The three most popular ways to create them are:

  • They can be allocated as a single block of memory, similar to static arrays. This requires the programmer to compute the index into it manually whenever it's used, but is more efficient.
  • They can be allocated as an "array of arrays", by first allocating an array of pointers, and then allocating subarrays and storing their addresses in the array of pointers. This approach has the advantage of a simple indexing syntax identical to that used by statically allocated arrays and the ability to make ragged arrays. However, it also uses more space and requires more levels of indirection to index into, and can have worse cache performance. It also requires many dynamic allocations, each of which can be expensive.

For more information, see the comp.lang.c FAQ, question 6.16 http://www.eskimo.com/~scs/C-faq/q6.16.html .

In some cases, the use of multi-dimensional arrays can best be addressed as an array of structures. Before user-defined data structures were available, a common technique was to define a multi-dimensional array, where each column contained different information about the row. This approach is also frequently used by beginner programmers. For example, columns of a two-dimensional character array might contain last name, first name, address, etc.

In cases like this, it is better to define a structure that contains the information that was stored in the columns, and then create an array of pointers to that structure. This is especially true when the number of data points for a given record might vary, such as the tracks on an album. In these cases, it is better to create a structure for the album that contains information about the album, along with a dynamic array for the list of songs on the album. Then an array of pointers to the album structure can be used to store the collection.

Constructors and destructors

In most object-oriented languages, objects cannot be created directly by a client that wishes to use them. Instead, the client must ask the class to build an instance of the object using a special routine called a constructor. Constructors are important because they allow an object to enforce invariants about its internal state throughout its lifetime. Destructors, called at the end of an object's lifetime, are important in systems where an object holds exclusive access to some resource, and we wish to ensure it releases these resources for use by other objects.

Since C is not an object-oriented language, it has no built-in support for constructors or destructors. It is not uncommon for clients to explicitly allocate and initialize records and other objects. However, this leads to a potential for errors, since operations on the object may fail or behave unpredictably if the object is not properly initialized. A better approach is to have a function that creates an instance of the object, possibly taking initialization parameters, as in this example:

  struct string {
      size_t size;
      char *data;
  };
  struct string *create_string(const char *initial) {
      assert (initial != NULL);
      struct string *new_string = malloc(sizeof(*new_string));
      if (new_string != NULL) {
          new_string->size = strlen(initial);
          new_string->data = strdup(initial);
      }
      return new_string;
  }

Similarly, if we leave it to the client to destroy objects correctly, they may fail to do so, causing resource leaks. It's better to have an explicit destructor which is always used, such as this one:

  void free_string(struct string *s) {
      assert (s != NULL);
      free(s->data);  /* free memory held by the structure */
      free(s);        /* free the structure itself */
  }

It is often useful to combine destructors with #Nulling freed pointers.

Sometimes it is useful to hide the definition of the object so that you can be sure the client doesn't allocate it manually. To do this, define the structure in the source file (or a private header file not available to users) instead of the header file, and simply put a forward declaration in the header file:

  struct string;
  struct string *create_string(const char *initial);
  void free_string(struct string *s);

Nulling freed pointers

As discussed earlier, after free() has been called on a pointer, it becomes a dangling pointer. Worse still, most modern platforms cannot detect when such a pointer is used before being reassigned.

One simple solution to this is to ensure that any pointer is set to a null pointer immediately after being freed:

  free(p);
  p = NULL;

Unlike dangling pointers, a hardware exception will arise on many modern architectures when a null pointer is dereferenced. Also, programs can include error checks for the null value, but not for a dangling pointer value. To ensure it is done at all locations, a macro can be used:

  #define FREE(p)   do { free(p); (p) = NULL; } while(0)

(To see why the macro is written this way, see #Macro conventions.) Also, when this technique is used, destructors should zero out the pointer that they are passed, and their argument must be passed by reference to allow this. For example, here's the destructor from #Constructors and destructors updated:

  void free_string(struct string **s) {
      assert(s != NULL  &&  *s != NULL);
      FREE((*s)->data);  /* free memory held by the structure */
      FREE(*s);          /* free the structure itself */
  }

Unfortunately, this idiom will not do anything to any other pointers that may be pointing to the freed memory. For this reason, some C experts regard this idiom as dangerous due to creating a false sense of security.

Macro conventions

Because preprocessor macros in C work using simple textual substitution, they are prone to a number of confusing errors that can be avoided by following a simple set of conventions:

  1. Place parentheses around macro arguments wherever possible. This ensures that, if they are expressions, the order of operations does not affect the behavior of the expression. For example:
    • Wrong: #define square(x) x*x
    • Better: #define square(x) (x)*(x)
  2. Place parentheses around the entire expression if it is a single expression. Again, this avoids changes in meaning due to the order of operations.
    • Wrong: #define square(x) (x)*(x)
    • Better: #define square(x) ((x)*(x))
  3. If your macro produces multiple statements, or declares variables, wrap it in a do { ... } while(0) loop, with no terminating semicolon. This allows the macro to be used like a single statement in any location, such as the body of an if statement, while still allowing a semicolon to be placed after the macro invocation without creating a null statement. Be careful that any new variables do not potentially mask portions of the macro's arguments.
    • Wrong: #define FREE(p) free(p); p = NULL;
    • Better: #define FREE(p) do { free(p); p = NULL; } while(0)
  4. Avoid using a macro argument twice or more inside a macro, if possible; this causes problems with macro arguments that contain side effects, such as assignments.
  5. If a macro may be replaced by a function in the future, consider naming it like a function.

See also

References

External links

Information

  • The Development of the C Language http://cm.bell-labs.com/cm/cs/who/dmr/chist.html by Dennis M. Ritchie
  • Programming in C: A Tutorial http://www.lysator.liu.se/c/bwk-tutor.html by Brian W. Kernighan
  • Lysator collection of C language resources http://www.lysator.liu.se/c/
  • Usenet forum: comp.lang.c news:comp.lang.c
  • comp.lang.c Answers to Frequently Asked Questions (FAQ List) http://www.faqs.org/faqs/C-faq/faq/index.html by Steve Summit
  • C Reference Card http://www.utsc.utoronto.ca/~nick/cscB70/ANSI_C.pdf (pdf)
  • Description from the developer's encyclopedia http://codepedia.com/1/BeginnersGuideToC

C99

  • Status of C99 features in GCC http://gcc.gnu.org/c99status.html
  • The FreeBSD C99 & POSIX® Conformance Project http://www.freebsd.org/projects/c99/
  • The Dinkum® C99 Library http://dinkumware.com/libc99.html
  • Tech Talk About C99 http://www.comeaucomputing.com/techtalk/c99/ — Comeau C99 FAQ
  • ISO/IEC 9899 http://www.nirvani.net/docs/ansi_c.pdf (pdf)
  • ISO/IEC n869 http://www.open-std.org/jtc1/sc22/wg14/www/docs/n869/ (final public draft)
  • ISO/IEC Working Group 14 http://www.open-std.org/jtc1/sc22/wg14/ (C99 committee)
  • Article "Open source development using C99 — Is your C code up to standard? http://www-106.ibm.com/developerworks/linux/library/l-c99.html?ca=dgr-lnxw07Usin
    gC99
    " by Peter Seebach
  • Are you Ready For C99? http://www.kuro5hin.org/?op=displaystory;sid=2001/2/23/194544/139
  • Article "The 'restrict' feature in C99 http://www.cbau.freeserve.co.uk/Compiler/RestrictPointers.html " by Christian Bau
  • Article "The New C: Declarations and Initializations http://cuj.com/documents/s=8191/cuj0104meyers/ " by Randy Meyers

Tools

  • Small C http://www.cpm.z80.de/small_c.html — a small, free implementation of the language originally published in Dr. Dobb's Journal of Computer Calisthenics & Orthodontia
  • TCC http://fabrice.bellard.free.fr/tcc/ , Tiny C Compiler, enables scripting in C
  • Splint http://splint.org/ , a free Lint derivative for static checking of C programs
  • Gimpel Software http://www.gimpel.com/ , producer of PC-Lint, the most popular commercial Lint derivative
  • A garbage collector for C http://www.hpl.hp.com/personal/Hans_Boehm/gc/ by Hans Boehm
  • Digital Mars C and C++ compilers (free download) http://www.digitalmars.com/download/freecompiler.html
  • Microsoft Visual C++ Toolkit 2003 (free download) http://msdn.microsoft.com/visualc/vctoolkit2003/
  • Dev-C++ http://www.bloodshed.net/ - a free C/C++ IDE including MinGW
  • DJGPP http://www.delorie.com/djgpp/ - a complete 32-bit C/C++ development system for Intel x86 PCs running DOS


Major programming languages (more)

Ada | ALGOL | APL | AWK | BASIC | C | C++ | C# | COBOL | Delphi | Eiffel | Fortran | Haskell | IDL | Java | JavaScript | Lisp | LOGO | ML | Objective-C | Pascal | Perl | PHP | PL/I | Prolog | Python | Ruby | SAS | Scheme | sh | Simula | Smalltalk | SQL | Visual Basic




Last updated: 02-02-2005 05:00:55
Last updated: 05-03-2005 02:30:17