C++ Revision for CDAC CMCE: PGCP-AC Notes
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.
- Getting Started
- Beginning with C++
- Conditions, Loops and Arrays
- Functions
- 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.
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-basedfor, lambdas, smart pointers andenum class.
| Task | C++98 style | C++11 style |
|---|---|---|
| Null pointer | NULL or 0 | nullptr |
| Type from the value | Write the type | auto x = 5; |
| Loop over an array | Index loop | for (int v : a) |
| Type alias | typedef | using |
| Enumeration | enum | enum class |
| Smart pointer | auto_ptr (deprecated in C++11) | unique_ptr, shared_ptr, weak_ptr |
| Initialization | = or () | Also {} |
C vs C++
| C | C++ |
|---|---|
| Procedural | Procedural and object-oriented |
malloc / free | new / delete |
printf / scanf | cout / cin |
'A' is an int | 'A' is a char |
void* converts to int* automatically | Needs a cast |
| No references or overloading | Has references and overloading |
| Structures hold only data | Structures can also hold functions |
| No namespaces or exceptions | Has 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
}
mainmust returnint.void main()is an error.- Reaching the end of
mainis the same asreturn 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,catchandthrow. - Smart pointers: pointers that free memory for you (see topic 5).
Common traps
sizeof('a')is 1 in C++ (it issizeof(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
| Form | Example | Putting 3.5 into an int |
|---|---|---|
| Copy | int a = 3.5; | Allowed, a is 3 |
| Direct | int 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
| Type | Operators |
|---|---|
| Arithmetic | + - * / % |
| Relational | < > <= >= == != |
| Logical | && || ! |
| Unary | ++ -- - ! sizeof |
| Ternary | condition ? a : b |
| Assignment | = += -= *= /= %= |
Prefix vs postfix (start with a = 5):
| Code | Value given | a afterwards |
|---|---|---|
b = ++a; | New value, 6 | 6 |
b = a++; | Old value, 5 | 6 |
Division:
| Expression | Result |
|---|---|
7 / 2 | 3 |
7 / 2.0 | 3.5 |
7.0 / 2 | 3.5 |
7 % 2 | 1 (% works on integers only) |
Often confused:
| Pair | Difference |
|---|---|
= 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 / 2is3, but7 / 2.0is3.5. - Remainder sign:
-7 % 3is-1, and7 % -3is1. The result takes the sign of the left operand. - Prefix vs postfix:
a++gives the old value,++agives 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";
}
switchworks 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 if | switch | |
|---|---|---|
| Tests | Any condition | One value against constants |
Ranges (marks >= 60) | Yes | No |
| Types allowed | Anything that makes a true/false result | Integers, characters, enums |
Needs break | No | Yes, to avoid fall-through |
| Default branch | else | default |
Loops and jump statements
| Loop | Checks the condition | Runs at least once? |
|---|---|---|
for | Before each pass | No |
while | Before each pass | No |
do-while | After each pass | Yes |
| Jump | What it does |
|---|---|
break | Leaves the loop (or switch) |
continue | Skips the rest of this pass and starts the next one |
return | Leaves the whole function |
| Use | When |
|---|---|
for | You know how many times to repeat |
while | You repeat until a condition changes |
do-while | The 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[])
argcis the number of arguments, including the program name.argv[0]is the program name, andargv[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
| Form | Example |
|---|---|
| No arguments, no return value | void greet() |
| No arguments, returns a value | int five() |
| Arguments, no return value | void show(int n) |
| Arguments and a return value | int 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.
| By value | By reference | By pointer | |
|---|---|---|---|
| Parameter | int a | int& a | int* a |
| Call | f(x) | f(x) | f(&x) |
| Copy made | Yes | No | Only the address |
| Changes the caller’s variable | No | Yes | Yes (through *a) |
| Can be null | No | No | Yes |
Inline functions
inline int square(int n) { return n * n; }
inlineis a request to the compiler, which may ignore it.- Unlike a macro, an inline function is type-checked and evaluates its arguments only once.
| Macro | Inline function | |
|---|---|---|
| Handled by | Preprocessor (text replacement) | Compiler |
| Type checking | None | Yes |
| Example | #define SQ(x) x*x | inline int sq(int x) { return x * x; } |
Result for 1+2 | SQ(1+2) becomes 1+2*1+2, which is 5 | sq(1+2) is 9 |
Function declaration vs definition:
| Declaration (prototype) | Definition | |
|---|---|---|
| Looks like | int add(int, int); | int add(int a, int b) { return a + b; } |
| Has a body | No | Yes |
| Purpose | Tells the compiler the function exists | Gives 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
| Stack | Heap |
|---|---|
| Local variables | Memory from new |
| Freed automatically | You must free it (or use a smart pointer) |
| Fast and small | Larger, and slower |
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 + 1moves bysizeof(int)bytes. - For an array
a,a[i]is the same as*(a + i). - Use
nullptr(C++11) instead ofNULL.
| Array | Pointer | |
|---|---|---|
| Holds | A block of elements | One address |
sizeof | Whole array size | Size of a pointer |
| Can be reassigned | No | Yes |
| In a function parameter | Becomes a pointer | Stays a pointer |
NULL | nullptr | |
|---|---|---|
| Standard | C and C++98 | C++11 |
| What it is | A 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
| Pointer | Reference |
|---|---|
Can be nullptr | Cannot be null |
| Can point elsewhere later | Cannot be changed to refer to another variable |
Use * to read | Used like a normal variable |
const with pointers
| Declaration | You cannot change |
|---|---|
const int* p | the value (*p = 2 is an error) |
int* const p | the pointer (p = &w is an error) |
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 []
newcalls constructors and returns the right pointer type.mallocdoes neither.- Match
newwithdelete, andnew[]withdelete[]. A mismatch is undefined behaviour. - Deleting the same pointer twice is undefined behaviour.
- Forgetting to delete causes a memory leak.
new | malloc | |
|---|---|---|
| Language | C++ operator | C library function |
| Returns | Correct pointer type | void* (needs a cast in C++) |
| Calls the constructor | Yes | No |
| On failure | Throws bad_alloc | Returns NULL |
| Freed with | delete / delete[] | free |
delete | delete[] | |
|---|---|---|
| Frees | One object from new | An array from new[] |
| Using the wrong one | Undefined behaviour | Undefined behaviour |
| Mistake | What happens | Fix |
|---|---|---|
Forgetting delete | Memory leak | Call delete, or use a smart pointer |
Using a pointer after delete | Dangling pointer, undefined behaviour | Set it to nullptr after delete |
Calling delete twice | Undefined behaviour | Set to nullptr (deleting nullptr is safe) |
new[] with delete | Undefined behaviour | Use 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
enumconverts tointautomatically. - An
enum classneedsLevel::Highand does not convert tointautomatically. Usestatic_cast<int>(Level::High).
enum | enum class (C++11) | |
|---|---|---|
| Access | RED | Level::High |
Converts to int automatically | Yes | No |
| Names leak into surrounding scope | Yes | No |
typedef | using (C++11) | |
|---|---|---|
| Example | typedef unsigned int uint; | using uint = unsigned int; |
| Result | Same | Same |
| Reads | Name at the end | Name 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;
| Object | Pointer to object | |
|---|---|---|
| Declared | Box b(3); | Box* b = new Box(3); |
| Member access | b.getW() | b->getW() or (*b).getW() |
| Destroyed | Automatically at end of scope | When you call delete b |
thisis a hidden pointer to the current object inside a member function.- Use
this->wwhen a parameter has the same name as a member. staticmember functions have nothis.
Smart pointers (C++11, header <memory>)
| Pointer | Idea |
|---|---|
unique_ptr | One owner. Cannot be copied, only moved with std::move |
shared_ptr | Many owners. Memory is freed when the last owner goes away |
weak_ptr | Watches a shared_ptr without owning it. Use lock() to get access |
unique_ptr | shared_ptr | weak_ptr | |
|---|---|---|---|
| Owns the object | Yes, alone | Yes, with others | No |
| Can be copied | No (only moved) | Yes | Yes |
| Counts owners | No | Yes, use_count() | Does not add to the count |
| Typical use | Single owner | Shared ownership | Break 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_uniquebelongs to C++14. In C++11, writeunique_ptr<int> p(new int(5)).weak_ptris used to break reference cycles betweenshared_ptrobjects.
Common traps
- A dangling pointer (pointing at freed memory) must not be used.
- An array passed to a function becomes a pointer, so
sizeofinside 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
5 7 7.agets the old value 5, then++imakesi7.3 -3 -1 1 3.5. Integer division drops the fraction, and the remainder takes the sign of the left operand.1 0.x++is 0 (false), soy++never runs.BC. Case 2 matches, there is nobreak, so case 3 also runs and thenbreakstops it.30 20 40 4.pmoves two elements,p[1]is one further, and the array has 4 elements.1 20.ais a copy, butbis a reference toy.9 1.**ppisx, and*ppisp.1 7 7.thisequals the object’s address, andp->vis the same as(*p).v.2 1 0 1.weak_ptrdoes not count as an owner. After the lastshared_ptris reset, the weak pointer has expired.0 5 6.BLUEis one more thanGREEN.
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
| Q | Answer | Reason |
|---|---|---|
| 1 | B | Stroustrup created C++. Ritchie made C, Gosling made Java |
| 2 | B | A character literal is a char in C++ |
| 3 | B | a++ uses 5, so b = 6, and a becomes 6 |
| 4 | C | = assigns. 5 is non-zero, so the condition is true |
| 5 | C | Brace initialization does not allow narrowing |
| 6 | A | Missing array elements are set to 0 |
| 7 | C | The program name counts as one argument |
| 8 | B | This is called fall-through |
| 9 | B | A reference refers to the original variable |
| 10 | B | The compiler decides whether to inline |
| 11 | C | const int* means the value cannot be changed through p |
| 12 | C | new[] must pair with delete[] |
| 13 | B | unique_ptr cannot be copied; use move |
| 14 | C | weak_ptr only observes |
| 15 | B | A scoped enum does not convert to int automatically |
Last-Minute Checklist
-
sizeof('a')is 1 in C++, andmainreturnsint - Narrowing is an error with
{}initialization -
/on integers drops the fraction, and%takes the sign of the left operand -
do-whileruns at least once, andswitchfalls through withoutbreak - Out-of-range array access is undefined behaviour
-
argcincludes the program name - Reference parameters change the original variable
-
const int*vsint* const -
newpairs withdelete, andnew[]withdelete[] -
unique_ptris moved,shared_ptrcounts owners,weak_ptronly watches -
enum classneedsType::Valueand no automaticintconversion