Skip to content

What C and C++ Actually Are

A file ending in .c or .cpp does not run by itself. It is input to an implementation: a compiler and associated tools that interpret one edition of a standardized language, combine the program with libraries, and produce code for a particular target. That sentence names several layers that integrated development environments often compress into a Build button.

Learning to separate those layers is the first practical native-programming skill.

C and C++ are separate standardized languages

C and C++ share ancestry and a large amount of surface syntax, but neither should be taught as merely a mode of the other. ISO working groups publish separate standards. The standards describe abstract machines: rules for translating and executing programs, required library facilities, and boundaries where implementations have choices.

A small program can look nearly identical:

c
#include <stdio.h>

int main(void) {
    puts("hello from C");
    return 0;
}
cpp
#include <iostream>

int main() {
    std::cout << "hello from C++\n";
}

The resemblance does not imply identical semantics. C permits implicit conversion from void * to other object-pointer types; C++ does not. C++ has references, function and operator overloading, templates, exceptions, classes with deterministic destruction, and a much larger standard library. C has compound literals and designated-initializer capabilities whose details do not simply transfer to every C++ version. Some source accepted by both languages means different things.

In ordinary engineering, choose the language deliberately. Use a C compiler mode for C source and a C++ compiler mode for C++ source. The course uses “C/C++” only when discussing genuinely shared ecosystem concerns such as object files, linkers, or debugger concepts.

A standard is not a compiler

“C17” and “C++20” name editions of language standards. GCC, Clang, and MSVC are implementations. A compiler selects a language mode through a flag or project setting:

sh
cc -std=c17 report.c -o report
c++ -std=c++20 report.cpp -o report

The generic commands cc and c++ often point to a platform's preferred compiler drivers. They are convenient in portable instructions, but they do not promise the same implementation on every computer.

An implementation may:

  • support only part of a recent standard;
  • provide extensions beyond the standard;
  • choose implementation-defined properties such as the signedness of plain char;
  • use a particular object-file format and application binary interface;
  • ship a particular standard-library implementation.

Consequently, “my compiler accepts it” is weaker than “the selected standard specifies it.” Strict conformance flags and multiple compilers help reveal accidental dependencies on extensions, but careful reasoning is still required.

The standard library is part of the language contract

Headers such as <stdio.h> and <vector> describe standardized library facilities. Implementations supply compiled runtime components where needed. The compiler and standard library are related but separable pieces.

On common C++ platforms, GCC is usually paired with libstdc++, upstream Clang may use libstdc++ or libc++, Apple Clang normally uses Apple's libc++, and MSVC uses the Microsoft C++ Standard Library. Selecting clang++ does not by itself tell you which C++ standard library is in use.

Language-feature support and library-feature support can arrive at different times. A compiler might parse a new syntax feature while the installed standard library lacks the corresponding component, so both support tables affect whether a program builds.

Operating-system APIs are another layer

printf is an ISO C library facility. fork is a POSIX API. CreateFileW is a Windows API. All may be callable from C or C++, but only the first belongs to an ISO language-library contract.

c
#include <stdio.h>   /* ISO C */
#include <unistd.h>  /* POSIX, not ISO C */

Separating these layers lets you ask a useful portability question. A program using fork may be valid C and compile perfectly on a Unix-like system while requiring architectural changes on Windows. Changing the -std flag cannot manufacture an absent operating-system facility.

Third-party libraries form another layer above this. SQLite exposes a C API; Boost offers many C++ libraries; neither becomes part of the language merely because it is widely used.

Hosted and freestanding implementations serve different worlds

Most desktop and server programs use a hosted implementation. It provides the full required standard library and begins execution through the conventional main function.

A freestanding implementation may target a microcontroller, kernel, bootloader, or similarly constrained environment. Only a smaller set of facilities is guaranteed, and program startup is implementation-defined. Embedded toolchains often add vendor headers, startup objects, linker scripts, and hardware-specific libraries.

A freestanding implementation makes fewer assumptions about the environment while still compiling C or C++. A microcontroller C++ project can use templates and deterministic destruction while intentionally avoiding facilities that require an operating system or dynamic allocation.

Source compatibility is not binary compatibility

Two compilers can accept the same source but produce objects that cannot safely be linked. Binary compatibility depends on an application binary interface: calling conventions, type layout, alignment, symbol naming, exception machinery, runtime-library choices, and object-file format.

C interfaces are commonly used across language boundaries because their binary surface is comparatively simple and can be named without C++ overload mangling:

cpp
extern "C" int report_total(const int* values, size_t count);

extern "C" requests C language linkage in C++; it does not turn the function body into C or guarantee a universal ABI. Both sides must still agree on types, ownership, compiler/runtime compatibility, and target architecture.

A classification habit prevents random fixes

When encountering an unfamiliar construct or failure, ask which layer owns it:

  1. Is it C syntax, C++ syntax, or shared syntax with different rules?
  2. Which standard version introduced it?
  3. Is it a standard-library facility?
  4. Is it a compiler extension or project convention?
  5. Is it supplied by the OS, SDK, or a third-party package?
  6. Is the problem in compilation, linking, loading, or execution?

A missing declaration suggests a source/header or language-mode problem. An undefined symbol usually points toward linking. A missing shared library is a loader or deployment problem. A crash after startup is runtime behavior. Treating all four as “the compiler is broken” leads to configuration churn.

Optional prompts

Explain: Clang accepts a file using a POSIX function. Does that make the function part of C17?

Answer: No. Clang implements the language, while platform headers and libraries expose POSIX. Successful compilation shows that this implementation and environment supplied the declaration; it does not change the ISO C contract.

Predict: Can a compiler support C++20 language concepts but lack a C++20 library component?

Answer: Yes. Front-end language support and standard-library implementation are distinct. The selected compiler, library, and installed versions all affect availability.

Debug: A C++ declaration is exported for a C caller without extern "C", and linking cannot find the name. Which boundary is most suspicious?

Answer: Language linkage and the ABI boundary. C++ normally encodes type information into symbol names for overloading. A carefully designed C-facing declaration should use C language linkage and C-compatible types.

Further reference