C programming / language comparison

C vs C++: Differences, Syntax and Which to Learn First

C and C++ are separate languages with separate standards and a large common subset. C++ adds classes with access levels and virtual functions and checks types more strictly, so valid C code can be invalid C++.

Published
ScopeISO C and ISO C++
Standards2
Reserved word class in Cnone
Cases of valid C failing in C++5
C: struct and functions (list.c)
#include <stdio.h>
#include <stdlib.h>

typedef struct {
    char *items;
    size_t count;
} List;

List *list_new(void) {
    List *l = malloc(sizeof *l);
    l->items = NULL;
    l->count = 0;
    return l;
}

void list_add(List *l, char c) {
    l->items = realloc(l->items, l->count + 1);
    l->items[l->count++] = c;
}

void list_print(const List *l) {
    for (size_t i = 0; i < l->count; i++) {
        printf("%s%zu:%c", i ? " " : "", i, l->items[i]);
    }
    printf("\n");
}

void list_free(List *l) {
    free(l->items);
    free(l);
}

int main(void) {
    List *l = list_new();
    list_add(l, 'a');
    list_add(l, 'b');
    list_add(l, 'c');
    list_print(l);
    list_free(l);
    return 0;
}
C++: class (list.cpp)
#include <cstdio>
#include <vector>

class List {
public:
    void add(char c) { items_.push_back(c); }
    void print() const {
        for (std::size_t i = 0; i < items_.size(); i++) {
            std::printf("%s%zu:%c", i ? " " : "", i, items_[i]);
        }
        std::printf("\n");
    }

private:
    std::vector<char> items_;
};

int main() {
    List l;
    l.add('a');
    l.add('b');
    l.add('c');
    l.print();
    return 0;
}
Both programs print 0:a 1:b 2:c. Built with cc -std=c17 -Wall -Wextra -pedantic and c++ -std=c++17 -Wall -Wextra -pedantic on Apple clang 12.0.0.

C and C++ are separate languages with separate standards and a large common subset. C++ is not a superset of C: valid C code can be invalid in C++. C++ adds classes with access levels and virtual functions and checks types more strictly, for instance when a pointer to void is converted.

The choice follows the goal. Editorial judgment: C suits work close to the machine and learning how memory is used, and C++ suits programs that grow large and need abstraction.

What is the difference between C and C++?

C is a procedural language. Programs are made of functions that work on data structures such as structs. C++ keeps that style available and adds classes, so a program can also be organized around types that bundle data with operations. A C struct holds data members only, because the standard does not allow a member of function type. A C++ class can carry member functions, access levels and virtual functions that derived classes override.

In practice this changes where the grouping of data and operations lives. In C every operation is a function written by the programmer, and naming habits tie the functions to their data. In C++ the grouping is part of the language, and the compiler knows which functions belong to which type.

Typing is the second visible difference. The rationale in Annex C of the C++ draft puts it in one sentence: C++ tries harder than C to enforce compile-time type safety. The most common consequence is the conversion of void *, covered in the section on syntax.

The two languages also borrowed from each other. According to the history of C on cppreference, C89 took const and function prototypes from C++, and C99 took inline functions and // comments.

AspectCC++
StandardISO CISO C++
StyleProcedural: functions and data structuresProcedural, class-based and other styles in one language
ClassesNone; a struct holds data members onlyThe class construct with access levels and virtual functions
Typingvoid * converts implicitly to object pointersNo implicit conversion; stricter checking

C vs C++ side by side: the same program in both languages

The program keeps a list of single characters. The C version defines a struct with a pointer and a count, plus four functions that take a pointer to that struct. The C++ version declares a class with the same behavior: add and print are member functions, and the storage is a standard vector, so allocation and cleanup disappear from the source.

The output stays the same. Built as C17 and as C++17 with -Wall -Wextra -pedantic on Apple clang 12.0.0, both programs print 0:a 1:b 2:c, and the two outputs compare equal byte for byte.

The C program has an explicit life cycle. list_new allocates the struct, list_add grows the character buffer with realloc, list_print walks the items, and list_free releases both allocations. The C++ version has no counterpart for list_new and list_free. A List exists once it is declared, and the vector inside it is released when the object goes out of scope. Which language writes more code depends on what the program needs, and the programs show only the shape of the difference.

What changes is where things sit. Data and operations share one declaration, the pointer parameter of every C function becomes the implicit object of a member call, and the private section keeps the vector out of reach. C reaches that last point with an opaque type instead.

Feeding the C program to a C++ compiler fails on its first allocation. Apple clang 12.0.0 reports:

list.c:10:11: error: cannot initialize a variable of type 'List *' with an rvalue of type 'void *'
    List *l = malloc(sizeof *l);
list.c:17:16: error: assigning to 'char *' from incompatible type 'void *'
    l->items = realloc(l->items, l->count + 1);

The cause is the conversion rule covered below. The fix is a cast on each allocation call, for example (List *)malloc(sizeof *l). With both casts added, the file compiles as C17 and as C++17 and prints the same line again.

C vs C++ syntax: which valid C code fails in C++?

Annex C of the C++ draft lists the differences from ISO C. Four of them turn valid C into an error in C++17, and each was confirmed with a compiler (Apple clang 12.0.0, -std=c11 against -std=c++17). The error texts are clang's; GCC words them differently. Subsection numbers change between drafts, so the cases are named by topic.

CaseCC++Fix
Conversion from void *int *p = malloc(4 * sizeof *p); is validError: cannot initialize a variable of type ‘int *’ with an rvalue of type ‘void *’Cast the result: (int *)malloc(...)
Struct as a scopestruct X { struct Y { int v; } inner; }; struct Y c; is validError: variable has incomplete type ‘struct Y’Declare Y outside X, or write struct X::Y c; in C++
Tentative definitionsint i; int i; at file scope is validError: redefinition of ‘i’Define once; use extern int i; for further declarations
Jump over an initializationgoto done; past int x = 5; is validError: cannot jump from this goto statement to its labelDeclare the variable before the jump, or inside an inner block

The reasons differ, and each tells something about the languages. C++ does not convert void * implicitly to an object pointer; C does. A struct is a scope in C++, so the inner name is reached as X::Y, while in C it is visible outside X. For the first case the cast is optional in C, so code that casts every allocation compiles in both languages. C treats repeated file-scope declarations without an initializer as tentative definitions, and C++ has no such notion. The jump rule protects against skipping an initialization, and C leaves it to the programmer.

A fifth entry in Annex C, mixing enumerations of different types or with floating-point values in arithmetic, depends on the version. The C++26 draft makes it ill-formed, cppreference lists it as deprecated since C++20, and a test with -std=c++17 compiled it without any message. Each entry above is a program that is valid C and not valid C++, which is why “C++ is a superset of C” does not hold.

How do structs and classes differ between C and C++?

In C++ the words class and struct are identical except for default access and default inheritance: a struct assumes public, a class assumes private. A C struct is more limited. It holds data members, it is not a scope, and it cannot contain a function. It can contain a pointer to a function, and that is how classes in C get methods.

FeatureC structC++ structC++ class
Data membersYesYesYes
Member functionsNo; pointers to functions onlyYesYes
Default accessNo access levelsPublicPrivate
ScopeNoYesYes

The practical effect shows in design. A C++ class can mark its data private and offer member functions as the only way in, and the compiler enforces the boundary. A C struct has no such boundary, so hiding members in C means an opaque type, where the header declares the struct and only one source file defines it. The guide to the C struct covers declaration, layout in memory and initialization in detail.

Which should I learn first, C or C++?

The answer depends on what the code is for.

  • To understand memory, pointers and how data sits in storage: C.
  • For firmware, drivers and other code close to the hardware: C.
  • For applications where abstraction matters, with many types that carry behavior: C++.
  • To work on an existing code base: the language of that code base.

Editorial judgment for someone with no particular goal: C first. It has fewer constructs, so a learner can see all of it, and what it teaches carries over, because structs, functions, pointers and the question of who owns memory appear in C++ too. Code written for C does not always compile as C++, as the cases above show, so the carry-over is of ideas more than of source files. The price of C first is that features C++ provides have to be written by hand. Classes in C show it: the pattern works, and every rule that C++ checks has to be kept by the author.

Starting with C++ is just as workable for someone who wants to build applications, and the order matters less than staying with one language until programs of real size are written. Style is not fixed by either language, and the guide to functional programming shows ideas that apply in both.

Do C and C++ share the same standards and compilers?

They do not share a standard. ISO C and ISO C++ are separate documents. The C draft used here, N3220, carries the title ISO/IEC 9899:2024, and the C++ language is defined in a working draft of its own. That separation is the reason Annex C of the C++ draft exists: it lists where the two disagree.

Compilers add a second layer, the language mode. The same file can pass in one mode and fail in another. On the machine used for these examples, cc and c++ start the compiler in C and in C++ mode, the option -x c++ forces C++ for a file ending in .c, and -std=c17 or -std=c++17 picks the revision. The list program is accepted with -std=c17 and rejected with -std=c++17 from identical source, so a program belongs to a language only together with the mode it is built in. The warning options -Wall -Wextra -pedantic used throughout ask the compiler to report more questionable code in either mode. They do not change which language is accepted.

FAQ

Frequently asked questions

01How do C and C++ differ in short?

They are separate languages with separate standards and a large common subset. C++ adds classes, access levels, virtual functions and stricter type checking. Valid C code can be invalid C++, so C++ is not a superset of C.

02Which is better for beginners, C or C++?

It depends on the goal, and the sources used here do not rank the two. As an editorial judgment, C is the better first language for anyone who wants to understand memory and machine-near programming, and C++ is a sound first choice when the aim is larger applications built from types with behavior.

03Which is harder, C or C++?

No source used here puts the two on one scale, so this is an editorial judgment rather than a finding. C has fewer constructs but leaves memory and conventions to the programmer. C++ has more constructs to learn and takes some of that bookkeeping over.