C

Also known as: C programming language

On this page 7

C is a compiled, general-purpose, procedural programming language that provides direct access to memory through pointers and maps closely to the operations of real hardware, while remaining portable across processors. Among programming languages it is the classic systems language: operating system kernels, embedded firmware, device drivers, databases and the runtimes of many higher-level languages are written in C.

C was created by Dennis Ritchie at Bell Labs in the early 1970s to rewrite the Unix operating system, and it is standardized by ISO/IEC 9899, with revisions commonly called C89, C99, C11, C17 and C23. Its small core, predictable performance and universal compiler support explain why it remains in heavy use decades later.

How C programs are built and executed

A C program is translated ahead of time into native machine code in several stages. The preprocessor expands macros and includes header files, the compiler turns each source file into an object file, and the linker combines object files and libraries into an executable. There is no virtual machine and no interpreter at run time, which gives C its minimal startup cost and small footprint.

StageInputOutput
PreprocessingSource with directives such as include and defineExpanded translation unit
CompilationTranslation unitAssembly or object code
AssemblyAssembly codeObject file
LinkingObject files and librariesExecutable or shared library
Stages of the C build process

C also defines two environments. A hosted implementation provides the full standard library and starts at the main function, as on desktop and server operating systems. A freestanding implementation, typical for microcontrollers and kernels, may provide only a minimal subset, so the program runs directly on hardware without an operating system.

Core language features

C deliberately offers a small set of features close to the machine. Its data types correspond to hardware words, its operators to processor instructions, and its memory model to a flat sequence of addressable bytes. Everything else, including strings and dynamic arrays, is built from these primitives.

FeatureWhat it providesConsequence
PointersVariables holding memory addressesDirect memory access and efficient data structures
Manual memory managementmalloc and free for heap memoryFull control, but leaks and dangling pointers possible
Static, weak typingTypes checked at compile time, implicit conversions allowedFast code, some errors pass unnoticed
Structs and unionsComposite and overlapping data layoutsPrecise control over memory representation
Preprocessor macrosText substitution before compilationConfiguration and portability, but hard to debug
Small standard libraryInput-output, strings, math, memoryPortable but minimal functionality
Characteristic features of C

Strings in C are arrays of characters terminated by a zero byte, with no stored length. Functions that copy or concatenate them must trust that the destination is large enough, which is the historical root of many buffer overflow vulnerabilities.

C is often compared with the languages that grew from it or compete for the same systems work. The main differences concern abstraction level and memory safety.

LanguageRelation to CKey difference
C++Extends C, largely source-compatibleClasses, templates and automatic resource management
RustCompetes for systems programmingMemory safety enforced at compile time
GoInfluenced by C syntaxGarbage collection and built-in concurrency
C#C-style syntax onlyManaged runtime and garbage collection
AssemblyLower level than CProcessor-specific, no portability
C and neighboring systems languages

C’s influence extends far beyond these languages. Its calling convention and data layout serve as the common foreign function interface through which Python, Java and many others call native libraries, so a library exposing a C interface can be used from almost any language.

Where C is used and why

C dominates where predictable performance, small size and direct hardware access outweigh development convenience. Typical domains include operating system kernels, embedded systems with only kilobytes of memory, network stacks, database engines, language interpreters and performance-critical libraries for compression, cryptography and media codecs.

The language’s practical strength is availability. A C compiler exists for virtually every processor architecture, including small 8-bit microcontrollers, and the language’s stable application binary interface on each platform makes compiled libraries long-lived and easy to link.

The language evolves slowly and conservatively through ISO standard revisions, so code written decades ago usually still compiles. Projects choose a standard level explicitly with a compiler option, and embedded toolchains often lag behind the newest revision, which is why many firmware projects still target C99 or C11.

RevisionNotable additionsPractical relevance
C89 or C90First standardized language and libraryBaseline for very old toolchains
C99Line comments, fixed-width integer types, inline functionsWidely supported minimum in embedded work
C11Threads, atomics, static assertionsPortable concurrency support
C17Defect fixes without new featuresStable default in many compilers
C23Boolean keywords, nullptr, binary literalsModernizes syntax, adoption ongoing
Major C standard revisions

Pitfalls and safe programming practice

C’s power comes with undefined behavior: situations such as reading outside an array, using freed memory or overflowing a signed integer, for which the standard imposes no requirements. The program may appear to work, crash, or produce subtly wrong results, and optimizing compilers may transform such code in surprising ways.

  • Enable all compiler warnings and treat them as errors in new code.
  • Use sanitizers for addresses and undefined behavior during testing to catch memory errors early.
  • Check every allocation and bounds explicitly; prefer length-aware functions such as snprintf.
  • Follow a coding standard such as MISRA C or CERT C in safety-critical or security-sensitive projects.
  • Pair every allocation with a clear owner responsible for freeing it exactly once.

Put simply

C is a programming language that talks to the computer almost in its own terms. It gives programmers very precise control over memory and speed. That is why much of the software beneath everyday devices is written in it.

The trade-off is that C does very little to protect programmers from mistakes. Think of it as a sharp professional knife without a guard. In skilled hands it is fast and precise, but careless use can cause serious damage.

In practice

An engineering team builds firmware for a battery-powered environmental sensor based on a microcontroller with 64 KB of flash memory and 8 KB of RAM. They write the firmware in C using a freestanding toolchain, with no operating system, and access the sensor and radio registers directly through pointers to fixed memory addresses.

The team allocates all buffers statically to avoid heap fragmentation, follows a subset of MISRA C rules and runs static analysis on every commit. The finished firmware occupies about 40 KB and keeps the device in a deep sleep mode most of the time, giving a battery life of several years on two AA cells, which would be hard to achieve with a language that needs a large runtime.