---
title: 'C and C++ Differences'
source: 'https://academia.sh/en/courses/cpp-fundamentals/c-and-cpp-differences'
course: 'C++ Fundamentals and Object Design'
language: en
updated: '2026-09-26T03:02:45+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - "Measures whether a struct passed by value works with the caller's own object or a copy"
    - "Infers which object a call works with only from the function's declared parameter type"
    - 'Counts the fundamental elements C++ adds to C'
---

# C and C++ Differences

Introduces the course's four measurement kinds with a core run that measures which object a struct passed by value in C works with in C++.

In M11/K03, "Object-Like Design" imitated object behavior in C by hand-assembling a
struct together with function pointers bound to it; which function ran was stored in a
manually built table. This course asks the same question with C++'s own language
features: **which object, exactly, does an expression work with** — the named object
itself, a new object copied from it, a part inside it, or an unnamed, short-lived
object?

## The Measurement Axis

Throughout the course, every lesson places an expression into one of four kinds:

- **self** — the expression works with the whole of the named object.
- **copy** — the expression works with a new object constructed by copying from the
  named object.
- **part** — the expression works with a subobject inside the named object.
- **temporary** — the expression works with a new, unnamed object that dies at the end
  of the full expression.

These four kinds are not a guess but the result of a run: the core below traces every
expression through its address, its copy constructor's source, and its object's
lifetime, and determines the kind itself.

## The Core's Full Run

The core is three files. `trace.hpp` and `trace.cpp` carry the tracing infrastructure:
every object gets a serial number on construction, and if it is copied it keeps its
source's number; `touch` records the object the expression actually works on, and
`finish` compares the declared kind against the measured one and prints the row.
`which_one.cpp` runs all fourteen measurements belonging to this course's fourteen
lessons inside a single `main`.

```cpp
// trace.hpp — a trace that measures which object an expression works on
#ifndef TRACE_HPP
#define TRACE_HPP

#include <cstddef>
#include <typeinfo>

enum class Kind { self, copy, part, temporary };

// Every traced object derives from this base: it gets a serial number when
// constructed, and if copied it keeps its source's number.
class Trace {
public:
    Trace();
    Trace(const Trace& source);
    Trace& operator=(const Trace& source);
    ~Trace();
    int id() const { return id_; }
    int source() const { return source_; }

private:
    int id_;
    int source_;
};

void begin_record(const void* addr, std::size_t size, const std::type_info& type, int id);
void touch_record(const void* addr, std::size_t size, const std::type_info& type,
                  const Trace* trace, int source);

// Records the named object: the measurement is made against this object.
template <class T>
void begin(const T& whole) {
    begin_record(&whole, sizeof whole, typeid(T), whole.id());
}

// Records the object the expression really works on and classifies it.
template <class T>
void touch(const T& obj) {
    touch_record(&obj, sizeof obj, typeid(T), &static_cast<const Trace&>(obj),
                 obj.source());
}

// Closes the measurement, compares the declared kind with the measured one, prints the row.
void finish(const char* expr, Kind declared);

// Prints the count per kind and the existing / new object totals.
void summary();

#endif
```

```cpp
// trace.cpp — the counts and the measurement state live only in this unit
#include "trace.hpp"

#include <cstdio>
#include <functional>

namespace {

int created = 0;
const char* const names[] = { "self", "copy", "part", "temporary" };
int counts[4] = {};
int existing = 0;
int fresh = 0;

const char* whole_addr = nullptr;
std::size_t whole_size = 0;
const std::type_info* whole_type = nullptr;
int whole_id = 0;

bool touched = false;
bool same = false;
bool inside = false;
bool copy_of = false;
bool ended = false;
const Trace* watched = nullptr;

}  // namespace

Trace::Trace() : id_(++created), source_(0) {}

Trace::Trace(const Trace& source) : id_(++created), source_(source.id_) {}

Trace& Trace::operator=(const Trace&) {
    return *this;  // assignment changes the object but constructs no new one
}

Trace::~Trace() {
    if (this == watched) {
        ended = true;
        watched = nullptr;
    }
}

void begin_record(const void* addr, std::size_t size, const std::type_info& type, int id) {
    whole_addr = static_cast<const char*>(addr);
    whole_size = size;
    whole_type = &type;
    whole_id = id;
    touched = same = inside = copy_of = ended = false;
    watched = nullptr;
}

void touch_record(const void* addr, std::size_t size, const std::type_info& type,
                  const Trace* trace, int source) {
    const char* start = static_cast<const char*>(addr);
    std::less<const char*> before;
    touched = true;
    same = start == whole_addr && size == whole_size && type == *whole_type;
    inside = !before(start, whole_addr) && !before(whole_addr + whole_size, start + size);
    copy_of = source == whole_id;
    ended = false;
    watched = trace;
}

void finish(const char* expr, Kind declared) {
    int measured = -1;
    if (touched) {
        if (same) {
            measured = static_cast<int>(Kind::self);
        } else if (inside) {
            measured = static_cast<int>(Kind::part);
        } else if (copy_of) {
            measured = static_cast<int>(Kind::copy);
        } else if (ended) {
            measured = static_cast<int>(Kind::temporary);
        }
    }
    int k = static_cast<int>(declared);
    bool exists = declared == Kind::self || declared == Kind::part;
    counts[k]++;
    if (exists) {
        existing++;
    } else {
        fresh++;
    }
    std::printf("%-36s %-8s %s%s\n", expr, exists ? "existing" : "new", names[k],
                measured == k ? "" : "  MISMATCH");
    watched = nullptr;
}

void summary() {
    std::printf("kinds: self %d, copy %d, part %d, temporary %d\n",
                counts[0], counts[1], counts[2], counts[3]);
    std::printf("existing %d, new %d\n", existing, fresh);
}
```

```cpp
// which_one.cpp — fourteen expressions, one question for each: which object
#include <algorithm>

#include "trace.hpp"

/* 1. C and C++ differences: struct passed by value */
struct Point : Trace {
    int x = 0;
    int y = 0;
};
static void shift(Point p) {
    p.x += 1;
    touch(p);
}
static void c_differences() {
    Point n;
    begin(n);
    shift(n);
    finish("struct passed by value", Kind::copy);
}

/* 2. Access control: the object a member function works on */
class Account : public Trace {
public:
    void deposit(int amount) {
        balance_ += amount;
        touch(*this);
    }
    int balance() const { return balance_; }

private:
    int balance_ = 0;
};
static void access() {
    Account a;
    begin(a);
    a.deposit(50);
    finish("this of a member function", Kind::self);
}

/* 3. Constructors: the object a converting constructor builds */
class Quantity : public Trace {
public:
    Quantity(int value) : value_(value) {}  // not explicit: converts from int
    int value() const { return value_; }

private:
    int value_;
};
static int show(const Quantity& q) {
    touch(q);
    return q.value();
}
static void constructors() {
    Quantity q(3);
    begin(q);
    show(5);
    finish("argument built from an int", Kind::temporary);
}

/* 4. RAII: the member the guard releases */
class Resource : public Trace {
public:
    void acquire() { held_ = true; }
    void release() {
        held_ = false;
        touch(*this);
    }

private:
    bool held_ = false;
};
class Guard {
public:
    explicit Guard(Resource& r) : r_(r) { r_.acquire(); }
    ~Guard() { r_.release(); }
    Guard(const Guard&) = delete;
    Guard& operator=(const Guard&) = delete;

private:
    Resource& r_;
};
class Counter : public Trace {
public:
    void increment() {
        Guard g(resource_);
        value_++;
    }

private:
    Resource resource_;
    int value_ = 0;
};
static void raii() {
    Counter c;
    begin(c);
    c.increment();
    finish("member released by the guard", Kind::part);
}

/* 5. Copying: a deeply copied buffer */
class Buffer : public Trace {
public:
    explicit Buffer(int size) : size_(size), data_(new int[size]()) {}
    Buffer(const Buffer& other) : Trace(other), size_(other.size_), data_(new int[other.size_]) {
        std::copy(other.data_, other.data_ + other.size_, data_);
    }
    Buffer& operator=(const Buffer& other) {
        if (this != &other) {
            int* fresh = new int[other.size_];
            std::copy(other.data_, other.data_ + other.size_, fresh);
            delete[] data_;
            data_ = fresh;
            size_ = other.size_;
        }
        return *this;
    }
    ~Buffer() { delete[] data_; }
    void write(int i, int value) {
        data_[i] = value;
        touch(*this);
    }
    int read(int i) const { return data_[i]; }

private:
    int size_;
    int* data_;
};
static void copying() {
    Buffer a(3);
    begin(a);
    Buffer b(a);
    b.write(0, 9);
    finish("deeply copied buffer", Kind::copy);
}

/* 6. References: an object passed by reference */
static void nudge(Point& p) {
    p.y += 1;
    touch(p);
}
static void references() {
    Point n;
    begin(n);
    nudge(n);
    finish("struct passed by reference", Kind::self);
}

/* 7. Operator overloading: the result of a + b */
class Money : public Trace {
public:
    explicit Money(int cents) : cents_(cents) {}
    Money operator+(const Money& b) const { return Money(cents_ + b.cents_); }
    Money& operator+=(const Money& b) {
        cents_ += b.cents_;
        return *this;
    }
    int cents() const { return cents_; }

private:
    int cents_;
};
static int use_money(const Money& m) {
    touch(m);
    return m.cents();
}
static void operators() {
    Money a(150);
    Money b(250);
    begin(a);
    use_money(a + b);
    finish("result of a + b", Kind::temporary);
}

/* 8. Inheritance: a function of the base class */
class Vehicle : public Trace {
public:
    void accelerate() {
        speed_++;
        touch(*this);
    }

private:
    int speed_ = 0;
};
class Truck : public Vehicle {
public:
    int load() const { return load_; }

private:
    int load_ = 0;
};
static void inheritance() {
    Truck t;
    begin(t);
    t.accelerate();
    finish("function of the base class", Kind::part);
}

/* 9. Virtual functions: virtual call via base reference */
class Shape : public Trace {
public:
    virtual ~Shape() = default;
    virtual void draw() const { touch(*this); }
};
class Circle : public Shape {
public:
    void draw() const override { touch(*this); }

private:
    int radius_ = 1;
};
static void virtuals() {
    Circle c;
    begin(c);
    const Shape& s = c;
    s.draw();
    finish("virtual call via base reference", Kind::self);
}

/* 10. Abstract classes: a call through an interface */
class Logger : public Trace {
public:
    virtual ~Logger() = default;
    virtual void log() = 0;
};
class LineLogger final : public Logger {
public:
    void log() override {
        lines_++;
        touch(*this);
    }

private:
    int lines_ = 0;
};
static void use(Logger& l) {
    l.log();
}
static void abstraction() {
    LineLogger l;
    begin(l);
    use(l);
    finish("call through an interface", Kind::self);
}

/* 11. Multiple inheritance: the single subobject of a virtual base */
class Device : public Trace {
public:
    void power_on() {
        on_ = true;
        touch(*this);
    }

private:
    bool on_ = false;
};
class Printer : public virtual Device {
    int pages_ = 0;
};
class Scanner : public virtual Device {
    int resolution_ = 0;
};
class MultiFunction : public Printer, public Scanner {};
static void diamond() {
    MultiFunction m;
    begin(m);
    m.power_on();
    finish("subobject of the virtual base", Kind::part);
}

/* 12. Exceptions: an exception caught by value */
class Error : public Trace {
public:
    explicit Error(int code) : code_(code) {}
    int code() const { return code_; }

private:
    int code_;
};
static void exceptions() {
    const Error* thrown = nullptr;
    try {
        try {
            throw Error(7);
        } catch (const Error& inner) {
            thrown = &inner;
            throw;
        }
    } catch (Error e) {
        begin(*thrown);
        touch(e);
        finish("exception caught by value", Kind::copy);
    }
}

/* 13. Namespaces: an object named by a using declaration */
namespace accounting {
class Ledger : public Trace {
public:
    void post() {
        entries_++;
        touch(*this);
    }

private:
    int entries_ = 0;
};
Ledger main_ledger;
}  // namespace accounting

static void namespaces() {
    using accounting::main_ledger;
    begin(accounting::main_ledger);
    main_ledger.post();
    finish("object named by a using declaration", Kind::self);
}

/* 14. Type conversions: a reference converted to the base */
class Meter : public Trace {
public:
    void read() const { touch(*this); }
};
class Thermometer : public Meter {
public:
    int degrees() const { return degrees_; }

private:
    int degrees_ = 20;
};
static void conversions() {
    Thermometer t;
    begin(t);
    static_cast<const Meter&>(t).read();
    finish("reference converted to the base", Kind::part);
}

int main() {
    c_differences();
    access();
    constructors();
    raii();
    copying();
    references();
    operators();
    inheritance();
    virtuals();
    abstraction();
    diamond();
    exceptions();
    namespaces();
    conversions();
    summary();
    return 0;
}
```

```text
struct passed by value               new      copy
this of a member function            existing self
argument built from an int           new      temporary
member released by the guard         existing part
deeply copied buffer                 new      copy
struct passed by reference           existing self
result of a + b                      new      temporary
function of the base class           existing part
virtual call via base reference      existing self
call through an interface            existing self
subobject of the virtual base        existing part
exception caught by value            new      copy
object named by a using declaration  existing self
reference converted to the base      existing part
kinds: self 5, copy 3, part 4, temporary 2
existing 9, new 5
```

**CR1.** Five of the fourteen expressions worked with self, three with a copy, four
with a part, two with a temporary; the course's four kinds and their distribution are
born from this. The first row is this lesson's subject: `shift(Point p)` works with
the `p` it receives by value, and `p` is constructed by copying from `n` — the
`p.x += 1` inside `shift(n)` does not reach the caller's `n`. Passing a `struct` by
value in C gives the same result; C++ did not change this behavior, it added a new
option on top of it.

## Deepening the Struct Passed by Value

The reference parameter C++ adds to C carries the same call into a different kind.
The measurement below runs `shift` and `nudge` side by side in its own `main`; both
are called through `n`, both appear in the source with the `(n)` spelling, but one
takes it by value and the other by reference.

```cpp
// value-reference.cpp — same call syntax, different parameter declaration
#include <cstdio>

struct Point {
    int x = 0;
};

static void shift(Point p, const Point* outside) {
    p.x += 1;
    std::printf("shift: is p the same object as outside %s\n",
                &p == outside ? "yes" : "no");
}

static void nudge(Point& p, const Point* outside) {
    p.x += 1;
    std::printf("nudge: is p the same object as outside %s\n",
                &p == outside ? "yes" : "no");
}

int main() {
    Point n;
    shift(n, &n);
    nudge(n, &n);
    return 0;
}
```

```text
shift: is p the same object as outside no
nudge: is p the same object as outside yes
```

**CR2.** The `p` inside `shift(n)` is a separate object from the caller's `n` — of
kind copy. The `p` inside `nudge(n)` is the caller's `n` itself — of kind self. The
call syntax is `(n)` in both cases; the only thing that makes the difference is the
function's **declaration**: whether the parameter type is `Point` or `Point&`.

## C++'s Differences from C

This lesson measured only pass-by-value; the rest of the elements C++ adds to C are
measured one by one in the course's later lessons. Briefly counted: `class` and
access control, the reference type, operator overloading, `new`/`delete`, namespaces,
a distinct `bool` type, and stricter type checking than C — for example, the implicit
conversion from `void*` to another pointer type is permitted in C, but is a compile
error in C++.

## The Bounding Measurement

The expression this lesson measures is of kind **copy**, but the measurement itself
also reveals a limit: the call site does not **say** which kind it will work with.
`shift(n)` and `nudge(n)` look identical in the source — both call an identifier in
parentheses with `n`. Only the parameter type in the function's declaration
determines which object is worked with; a reader must infer this decision from the
signature, not from the call line.

## Summary

- The course's measurement axis: an expression works with one of the kinds **self**,
  **copy**, **part**, or **temporary**; the fourteen measurements fix this
  distribution as 5/3/4/2.
- The expression this lesson measures is of kind **copy**: the `Point` passed by
  value works with a copy separate from the caller's object.
- The same `(n)` call syntax shifts to kind **self** when the parameter is a
  reference; it is the declaration, not the call, that determines the kind.
- The elements C++ adds to C (class, reference, overloading, `new`/`delete`,
  namespace, `bool`, stricter type checking) were briefly counted; each deepens in
  its own lesson.
- Compilation is warning-free and the output is identical across three separate
  runs; the measurement rests on a run, not a guess.

## Next Step

This lesson showed which object a function works with based on its parameter type;
the next lesson asks the same question for a **member function** — it measures which
object `this` points to when `a.deposit(50)` is called, and that `private` access
works at the level of the class, not the object.
