C++ Revision for CDAC CMCE: PGCP-AC Notes

Revision Published 30 min read

A quick revision of the C++ syllabus for the CDAC CMCE exam for PGCP-AC (Advanced Computing). It covers five topics in order and stays within C++98 to C++11.

  1. Getting Started
  2. Beginning with C++
  3. Conditions, Loops and Arrays
  4. Functions
  5. Memory Management and Pointers

All code was compiled with g++ -std=c++11 -Wall -Wextra -pedantic, and the outputs shown are the real ones.


1. Getting Started

Setup

Install a compiler (g++), write a .cpp file, then compile and run it:

g++ -std=c++11 main.cpp -o main
./main

Compilation has four steps: preprocess → compile → assemble → link. Syntax errors appear at compile time. “Undefined reference” errors appear at link time.

The four stages of building a C++ program: preprocess, compile, assemble and link

History

  • 1979: Bjarne Stroustrup starts “C with Classes” at Bell Labs.
  • 1983: renamed C++ (from the ++ increment operator).
  • 1998: first ISO standard, C++98. It added templates, the STL, exceptions, namespaces and bool.
  • 2003: C++03, a small bug-fix update.
  • 2011: C++11. It added auto, nullptr, range-based for, lambdas, smart pointers and enum class.
TaskC++98 styleC++11 style
Null pointerNULL or 0nullptr
Type from the valueWrite the typeauto x = 5;
Loop over an arrayIndex loopfor (int v : a)
Type aliastypedefusing
Enumerationenumenum class
Smart pointerauto_ptr (deprecated in C++11)unique_ptr, shared_ptr, weak_ptr
Initialization= or ()Also {}

C vs C++

CC++
ProceduralProcedural and object-oriented
malloc / freenew / delete
printf / scanfcout / cin
'A' is an int'A' is a char
void* converts to int* automaticallyNeeds a cast
No references or overloadingHas references and overloading
Structures hold only dataStructures can also hold functions
No namespaces or exceptionsHas namespaces and exceptions
Header <stdio.h>Header <iostream> (and <cstdio>)

C++ is not a strict superset of C. This line is valid C but an error in C++:

int *p = malloc(4);   // C++ error: invalid conversion from 'void*' to 'int*'

Features of C++

Classes and objects, inheritance, polymorphism, encapsulation, templates, exceptions, namespaces, references, function overloading and operator overloading.

Program structure

#include <iostream>          // 1. header
using namespace std;         // 2. names from std

int main() {                 // 3. execution starts here
    cout << "Hello, C-CAT!" << endl;
    return 0;                // optional in main
}
  • main must return int. void main() is an error.
  • Reaching the end of main is the same as return 0;.

Advanced concepts, in one line each

  • Templates: write one function or class for many types.
  • STL: ready-made containers (vector, map) and algorithms.
  • Exceptions: handle errors with try, catch and throw.
  • Smart pointers: pointers that free memory for you (see topic 5).

Common traps

  • sizeof('a') is 1 in C++ (it is sizeof(int) in C).
  • int f() means “no parameters” in C++.
  • void main() is not valid standard C++.

2. Beginning with C++

Tokens

The smallest parts of a program: keywords (int, if), identifiers (names you choose), constants (10, 'a'), strings ("hi"), operators (+, <<) and punctuators (;, {}).

Initialization

int a = 5;     // copy initialization
int b(5);      // direct initialization
int c{5};      // brace (list) initialization, C++11
int d{3.5};    // ERROR: narrowing conversion
FormExamplePutting 3.5 into an int
Copyint a = 3.5;Allowed, a is 3
Directint a(3.5);Allowed, a is 3
List (C++11)int a{3.5};Compile error
  • Local variables are not initialized automatically. Reading one is undefined behaviour.
  • Global and static variables start at 0.
  • int a[5] = {1, 2}; makes the rest of the array 0.

Operators

TypeOperators
Arithmetic+ - * / %
Relational< > <= >= == !=
Logical&& || !
Unary++ -- - ! sizeof
Ternarycondition ? a : b
Assignment= += -= *= /= %=

Prefix vs postfix (start with a = 5):

CodeValue givena afterwards
b = ++a;New value, 66
b = a++;Old value, 56

Division:

ExpressionResult
7 / 23
7 / 2.03.5
7.0 / 23.5
7 % 21 (% works on integers only)

Often confused:

PairDifference
= and ==Assign vs compare
&& and &Logical AND, stops early, vs bitwise AND, always evaluates both sides
'a' and "a"One character vs a string (size 2, with '\0')

Common traps

  • Integer division: 7 / 2 is 3, but 7 / 2.0 is 3.5.
  • Remainder sign: -7 % 3 is -1, and 7 % -3 is 1. The result takes the sign of the left operand.
  • Prefix vs postfix: a++ gives the old value, ++a gives the new one.
  • = vs ==: if (x = 5) assigns 5 and is always true.
  • Short-circuit: in x++ && y++, if the left side is false, y++ never runs.
  • Undefined behaviour: changing the same variable twice in one expression, e.g. i++ + ++i.

Lab programs

#include <iostream>
#include <cmath>
#include <bitset>
#include <string>
using namespace std;
int main() {
    cout << "Hello, C-CAT!" << endl;

    int a = 12, b = 30;
    cout << "Sum: " << a + b << '\n';

    string s1 = "1011", s2 = "110";                       // binary numbers
    int sum = stoi(s1, nullptr, 2) + stoi(s2, nullptr, 2);
    cout << "Binary sum: " << bitset<8>(sum) << " (" << sum << ")\n";

    char c = 'A';                                         // characters
    cout << c + 1 << ' ' << char(c + 1) << '\n';

    double p = 10000, r = 5, t = 2;                       // compound interest
    cout << "Compound interest: " << p * pow(1 + r / 100, t) - p << '\n';
    cout << "Power: " << pow(2, 10) << '\n';

    int x = 3, y = 7, tmp = x;                            // swap
    x = y;
    y = tmp;
    cout << "Swapped: " << x << ' ' << y << '\n';

    double len = 5, wid = 3;                              // area of rectangle
    cout << "Area: " << len * wid << '\n';
}

Output:

Hello, C-CAT!
Sum: 42
Binary sum: 00010001 (17)
66 B
Compound interest: 1025
Power: 1024
Swapped: 7 3
Area: 15

Note that c + 1 prints 66 because adding to a char gives an int. Cast back with char(c + 1) to print B.


3. Conditions, Loops and Arrays

Conditional statements

if (marks >= 90) cout << "A";
else if (marks >= 60) cout << "B";
else cout << "C";

switch (day) {
    case 1: cout << "Mon"; break;
    case 3: cout << "Wed"; break;
    default: cout << "Other";
}
  • switch works on integers and characters (and enums), not on strings or floats.
  • Case labels must be constants.
  • Without break, execution falls through into the next case.
if / else ifswitch
TestsAny conditionOne value against constants
Ranges (marks >= 60)YesNo
Types allowedAnything that makes a true/false resultIntegers, characters, enums
Needs breakNoYes, to avoid fall-through
Default branchelsedefault

Loops and jump statements

LoopChecks the conditionRuns at least once?
forBefore each passNo
whileBefore each passNo
do-whileAfter each passYes
JumpWhat it does
breakLeaves the loop (or switch)
continueSkips the rest of this pass and starts the next one
returnLeaves the whole function
UseWhen
forYou know how many times to repeat
whileYou repeat until a condition changes
do-whileThe body must run at least once (e.g. a menu)

Arrays

int a[5] = {1, 2, 3};          // a[3] and a[4] are 0
int m[2][3] = {{1, 2, 3}, {4, 5, 6}};   // 2D array
  • Indexes start at 0, and the last valid index is size - 1.
  • Going outside the array is undefined behaviour. The compiler does not stop you.
  • Array size: sizeof(a) / sizeof(a[0]).

Command-line arguments

int main(int argc, char* argv[])
  • argc is the number of arguments, including the program name.
  • argv[0] is the program name, and argv[1] onwards are the arguments.

Lab program

#include <iostream>
using namespace std;
int main(int argc, char* argv[]) {
    int marks = 72;
    if (marks >= 90) cout << "Grade A\n";
    else if (marks >= 60) cout << "Grade B\n";
    else cout << "Grade C\n";

    int day = 3;
    switch (day) {
        case 1: cout << "Mon\n"; break;
        case 3: cout << "Wed\n"; break;
        default: cout << "Other\n";
    }

    int sum = 0;
    for (int i = 1; i <= 5; ++i) sum += i;
    cout << "Sum 1..5 = " << sum << '\n';

    int n = 1234, rev = 0;
    while (n > 0) { rev = rev * 10 + n % 10; n /= 10; }
    cout << "Reversed: " << rev << '\n';

    int k = 0;
    do { ++k; } while (k < 3);
    cout << "k = " << k << '\n';

    int m[2][3] = {{1, 2, 3}, {4, 5, 6}};
    for (int i = 0; i < 2; ++i) {
        for (int j = 0; j < 3; ++j) cout << m[i][j] << ' ';
        cout << '\n';
    }

    cout << "argc = " << argc << '\n';
    for (int i = 0; i < argc; ++i) cout << "argv[" << i << "] = " << argv[i] << '\n';
}

Run as ./prog hello world:

Grade B
Wed
Sum 1..5 = 15
Reversed: 4321
k = 3
1 2 3 
4 5 6 
argc = 3
argv[0] = ./prog
argv[1] = hello
argv[2] = world

4. Functions

Four forms of function

FormExample
No arguments, no return valuevoid greet()
No arguments, returns a valueint five()
Arguments, no return valuevoid show(int n)
Arguments and a return valueint add(int a, int b)

Prototype

A prototype declares a function before it is used, so the compiler knows its name, parameter types and return type: int add(int, int);

Call by value vs call by reference

  • By value: the function gets a copy, so the caller’s variable does not change.
  • By reference (int&): the function works on the original variable.

Call by value copies the variable, call by reference shares it

By valueBy referenceBy pointer
Parameterint aint& aint* a
Callf(x)f(x)f(&x)
Copy madeYesNoOnly the address
Changes the caller’s variableNoYesYes (through *a)
Can be nullNoNoYes

Inline functions

inline int square(int n) { return n * n; }
  • inline is a request to the compiler, which may ignore it.
  • Unlike a macro, an inline function is type-checked and evaluates its arguments only once.
MacroInline function
Handled byPreprocessor (text replacement)Compiler
Type checkingNoneYes
Example#define SQ(x) x*xinline int sq(int x) { return x * x; }
Result for 1+2SQ(1+2) becomes 1+2*1+2, which is 5sq(1+2) is 9

Function declaration vs definition:

Declaration (prototype)Definition
Looks likeint add(int, int);int add(int a, int b) { return a + b; }
Has a bodyNoYes
PurposeTells the compiler the function existsGives the code

Math library

Include <cmath> for sqrt, pow, fabs, ceil, floor, sin, cos, log and exp.

Lab program

#include <iostream>
#include <cmath>
using namespace std;
int add(int, int);                       // prototype
void greet() { cout << "Hi\n"; }
int five() { return 5; }
void show(int n) { cout << n << '\n'; }
int add(int a, int b) { return a + b; }
inline int square(int n) { return n * n; }
void swapRef(int& a, int& b) { int t = a; a = b; b = t; }
void noChange(int a, int b) { int t = a; a = b; b = t; }

int main() {
    greet(); show(five()); cout << add(2, 3) << ' ' << square(4) << '\n';
    int x = 1, y = 2;
    noChange(x, y); cout << x << ' ' << y << '\n';
    swapRef(x, y);  cout << x << ' ' << y << '\n';
    cout << sqrt(16) << ' ' << pow(2, 3) << ' ' << fabs(-2.5) << ' '
         << ceil(2.1) << ' ' << floor(2.9) << '\n';
}

Output:

Hi
5
5 16
1 2
2 1
4 8 2.5 3 2

noChange leaves 1 2 unchanged, while swapRef gives 2 1.


5. Memory Management and Pointers

Stack and heap

StackHeap
Local variablesMemory from new
Freed automaticallyYou must free it (or use a smart pointer)
Fast and smallLarger, and slower

A pointer on the stack pointing to an int on the heap

Pointers

int x = 5;
int* p = &x;     // p holds the address of x
*p = 9;          // x is now 9
int* q = nullptr;   // points to nothing
  • Pointer arithmetic moves by whole elements: p + 1 moves by sizeof(int) bytes.
  • For an array a, a[i] is the same as *(a + i).
  • Use nullptr (C++11) instead of NULL.

An array of four ints with a pointer to the third element

ArrayPointer
HoldsA block of elementsOne address
sizeofWhole array sizeSize of a pointer
Can be reassignedNoYes
In a function parameterBecomes a pointerStays a pointer
NULLnullptr
StandardC and C++98C++11
What it isA macro for a null constant (often 0)A keyword of its own type
f(NULL) with f(int) and f(char*)Ambiguous error (GCC)f(nullptr) picks the pointer version

Pointer vs reference

PointerReference
Can be nullptrCannot be null
Can point elsewhere laterCannot be changed to refer to another variable
Use * to readUsed like a normal variable

const with pointers

DeclarationYou cannot change
const int* pthe value (*p = 2 is an error)
int* const pthe pointer (p = &w is an error)

const int pointer versus int const pointer

Dynamic memory

int* p = new int(5);          // one int
int* arr = new int[3]{1, 2, 3};   // array of 3
delete p;                     // single object
delete[] arr;                 // array: always use []
  • new calls constructors and returns the right pointer type. malloc does neither.
  • Match new with delete, and new[] with delete[]. A mismatch is undefined behaviour.
  • Deleting the same pointer twice is undefined behaviour.
  • Forgetting to delete causes a memory leak.
newmalloc
LanguageC++ operatorC library function
ReturnsCorrect pointer typevoid* (needs a cast in C++)
Calls the constructorYesNo
On failureThrows bad_allocReturns NULL
Freed withdelete / delete[]free
deletedelete[]
FreesOne object from newAn array from new[]
Using the wrong oneUndefined behaviourUndefined behaviour
MistakeWhat happensFix
Forgetting deleteMemory leakCall delete, or use a smart pointer
Using a pointer after deleteDangling pointer, undefined behaviourSet it to nullptr after delete
Calling delete twiceUndefined behaviourSet to nullptr (deleting nullptr is safe)
new[] with deleteUndefined behaviourUse delete[]

Enumerations and type aliases

enum Color { RED, GREEN = 5, BLUE };   // RED=0, GREEN=5, BLUE=6
enum class Level { Low, High };        // C++11, scoped

typedef unsigned int uint;             // old style
using Counter = unsigned long;         // C++11 style, same effect
  • A plain enum converts to int automatically.
  • An enum class needs Level::High and does not convert to int automatically. Use static_cast<int>(Level::High).
enumenum class (C++11)
AccessREDLevel::High
Converts to int automaticallyYesNo
Names leak into surrounding scopeYesNo
typedefusing (C++11)
Exampletypedef unsigned int uint;using uint = unsigned int;
ResultSameSame
ReadsName at the endName first, easier to read

Pointers to objects and this

class Box {
    int w;
public:
    explicit Box(int w) : w(w) {}
    Box* setW(int w) { this->w = w; return this; }
    int getW() const { return w; }
};

Box* b = new Box(3);
b->setW(9);                // same as (*b).setW(9)
cout << b->getW();         // 9
delete b;
ObjectPointer to object
DeclaredBox b(3);Box* b = new Box(3);
Member accessb.getW()b->getW() or (*b).getW()
DestroyedAutomatically at end of scopeWhen you call delete b
  • this is a hidden pointer to the current object inside a member function.
  • Use this->w when a parameter has the same name as a member.
  • static member functions have no this.

Smart pointers (C++11, header <memory>)

PointerIdea
unique_ptrOne owner. Cannot be copied, only moved with std::move
shared_ptrMany owners. Memory is freed when the last owner goes away
weak_ptrWatches a shared_ptr without owning it. Use lock() to get access

unique_ptr has one owner, shared_ptr has many, weak_ptr only watches

unique_ptrshared_ptrweak_ptr
Owns the objectYes, aloneYes, with othersNo
Can be copiedNo (only moved)YesYes
Counts ownersNoYes, use_count()Does not add to the count
Typical useSingle ownerShared ownershipBreak cycles, observe safely
unique_ptr<int> u(new int(10));
unique_ptr<int> u2 = move(u);      // u is now empty

shared_ptr<int> s1 = make_shared<int>(7);
shared_ptr<int> s2 = s1;           // use_count() is 2
weak_ptr<int> w = s1;              // does NOT change use_count()
  • std::make_unique belongs to C++14. In C++11, write unique_ptr<int> p(new int(5)).
  • weak_ptr is used to break reference cycles between shared_ptr objects.

Common traps

  • A dangling pointer (pointing at freed memory) must not be used.
  • An array passed to a function becomes a pointer, so sizeof inside the function gives the pointer size.
  • "abc" has size 4 because of the hidden '\0'.

Lab program: string functions using pointers

#include <iostream>
using namespace std;
int myStrlen(const char* s) { const char* p = s; while (*p) ++p; return static_cast<int>(p - s); }
void myStrcpy(char* d, const char* s) { while ((*d++ = *s++)) {} }
void myStrcat(char* d, const char* s) { while (*d) ++d; while ((*d++ = *s++)) {} }
int myStrcmp(const char* a, const char* b) {
    while (*a && *a == *b) { ++a; ++b; }
    return static_cast<unsigned char>(*a) - static_cast<unsigned char>(*b);
}
int main() {
    char a[20] = "Hello", b[20];
    myStrcpy(b, a);
    myStrcat(b, " C++");
    cout << myStrlen(b) << ' ' << b << ' ' << myStrcmp(a, "Hello") << ' '
         << (myStrcmp("abc", "abd") < 0) << '\n';
}

Output: 9 Hello C++ 0 1

Full demo of memory topics

int* p = new int(5);
int* arr = new int[3]{1, 2, 3};
cout << *p << ' ' << arr[2] << '\n';          // 5 3
delete p; delete[] arr;

Box* b = new Box(3);
b->setW(9);
cout << b->getW() << ' ' << (*b).getW() << '\n';   // 9 9
delete b;

unique_ptr<int> u(new int(10));
unique_ptr<int> u2 = move(u);
cout << (u == nullptr) << ' ' << *u2 << '\n';      // 1 10

shared_ptr<int> s1 = make_shared<int>(7);
shared_ptr<int> s2 = s1;
weak_ptr<int> w = s1;
cout << s1.use_count() << ' ';                 // 2
s2.reset();
cout << s1.use_count() << ' ';                 // 1
if (shared_ptr<int> t = w.lock()) cout << *t << ' ';   // 7
s1.reset();
cout << w.expired() << '\n';                   // 1

Output Prediction

Try each one first. All outputs were checked by compiling and running the code.

Q1

int i = 5; int a = i++; int b = ++i;
cout << a << " " << b << " " << i;

Q2

cout << 7/2 << " " << -7/2 << " " << -7%3 << " " << 7%-3 << " " << 7/2.0;

Q3

int x = 0, y = 0;
if (x++ && y++) {}
cout << x << " " << y;

Q4

int n = 2;
switch (n) {
    case 1: cout << "A";
    case 2: cout << "B";
    case 3: cout << "C"; break;
    default: cout << "D";
}

Q5

int a[] = {10, 20, 30, 40};
int* p = a; p += 2;
cout << *p << " " << *(p - 1) << " " << p[1] << " " << sizeof(a) / sizeof(a[0]);

Q6

void f(int a, int& b) { a = 10; b = 20; }
int x = 1, y = 2; f(x, y);
cout << x << " " << y;

Q7

int x = 5; int* p = &x; int** pp = &p;
**pp = 9;
cout << x << " " << (*pp == p);

Q8

struct C { int v; C* me() { return this; } };
C c{7}; C* p = &c;
cout << (p->me() == p) << " " << p->v << " " << (*p).v;

Q9

shared_ptr<int> a = make_shared<int>(5);
shared_ptr<int> b = a;
weak_ptr<int> w = a;
cout << a.use_count() << " ";
b.reset();
cout << a.use_count() << " " << w.expired();
a.reset();
cout << " " << w.expired();

Q10

enum Color { RED, GREEN = 5, BLUE };
cout << RED << " " << GREEN << " " << BLUE;

Answers

  1. 5 7 7. a gets the old value 5, then ++i makes i 7.
  2. 3 -3 -1 1 3.5. Integer division drops the fraction, and the remainder takes the sign of the left operand.
  3. 1 0. x++ is 0 (false), so y++ never runs.
  4. BC. Case 2 matches, there is no break, so case 3 also runs and then break stops it.
  5. 30 20 40 4. p moves two elements, p[1] is one further, and the array has 4 elements.
  6. 1 20. a is a copy, but b is a reference to y.
  7. 9 1. **pp is x, and *pp is p.
  8. 1 7 7. this equals the object’s address, and p->v is the same as (*p).v.
  9. 2 1 0 1. weak_ptr does not count as an owner. After the last shared_ptr is reset, the weak pointer has expired.
  10. 0 5 6. BLUE is one more than GREEN.

MCQ Practice

1. Who created C++? A) Dennis Ritchie B) Bjarne Stroustrup C) James Gosling D) Ken Thompson

2. In C++, sizeof('a') is: A) sizeof(int) B) 1 C) 2 D) 0

3. After int a = 5; int b = a++ + 1;, the values are: A) a=5, b=6 B) a=6, b=6 C) a=6, b=7 D) a=5, b=7

4. int x = 10; if (x = 5) { ... } does what? A) Compile error B) Skips the body C) Sets x to 5 and runs the body D) Compares x with 5

5. What happens with int a{3.5};? A) a is 3 B) a is 4 C) Compile error (narrowing) D) Runtime error

6. After int a[5] = {1, 2};, the value of a[4] is: A) 0 B) 2 C) Garbage D) Compile error

7. For ./a.out hello world, argc is: A) 1 B) 2 C) 3 D) 4

8. If a switch case has no break: A) Compile error B) Execution falls through to the next case C) The program exits D) The switch repeats

9. To let a function change the caller’s variable in C++, you can use: A) Call by value B) Call by reference C) A prototype D) inline

10. The inline keyword is: A) A command that is always obeyed B) A request the compiler may ignore C) Needed for every function D) Used only for loops

11. With const int* p, which line is an error? A) p = &x; B) int y = *p; C) *p = 2; D) p = nullptr;

12. Memory from new int[10] should be freed with: A) free(p) B) delete p C) delete[] p D) remove p

13. What happens here?

unique_ptr<int> a(new int(1));
unique_ptr<int> b = a;

A) Works fine B) Compile error C) Runtime error D) Both point to the same value

14. Which smart pointer does not increase the use count? A) unique_ptr B) shared_ptr C) weak_ptr D) All of them

15. enum class L { Lo, Hi }; int x = L::Hi; is: A) Valid, x is 1 B) Compile error C) Valid, x is 0 D) Runtime error

Answer key

QAnswerReason
1BStroustrup created C++. Ritchie made C, Gosling made Java
2BA character literal is a char in C++
3Ba++ uses 5, so b = 6, and a becomes 6
4C= assigns. 5 is non-zero, so the condition is true
5CBrace initialization does not allow narrowing
6AMissing array elements are set to 0
7CThe program name counts as one argument
8BThis is called fall-through
9BA reference refers to the original variable
10BThe compiler decides whether to inline
11Cconst int* means the value cannot be changed through p
12Cnew[] must pair with delete[]
13Bunique_ptr cannot be copied; use move
14Cweak_ptr only observes
15BA scoped enum does not convert to int automatically

Last-Minute Checklist

  • sizeof('a') is 1 in C++, and main returns int
  • Narrowing is an error with {} initialization
  • / on integers drops the fraction, and % takes the sign of the left operand
  • do-while runs at least once, and switch falls through without break
  • Out-of-range array access is undefined behaviour
  • argc includes the program name
  • Reference parameters change the original variable
  • const int* vs int* const
  • new pairs with delete, and new[] with delete[]
  • unique_ptr is moved, shared_ptr counts owners, weak_ptr only watches
  • enum class needs Type::Value and no automatic int conversion