directly derived from C or are heavily influenced by its syntax and design • C++: • Built on C with object-oriented features. • Objective-C: • A C-based language with object-oriented extensions. • Java and C#: • Adopted C-like syntax and many concepts. • JavaScript, Go, Rust, Swift, PHP, ... • Designed in the 70s! • 1978: Kernighan + Ritchie: "The C Programming Language" • From 1989 -> 2018 ANSI C standard. • 2018: C18 (ISO/IEC 9899:2018) language standard • Free doc: https://en.cppreference.com/w/c
to develop operating systems due to its low-level capabilities and direct access to system hardware. Examples include UNIX, Linux, Windows, and macOS kernels. • Embedded Systems: • C's ability to directly interact with hardware makes it ideal for programming microcontrollers and embedded systems found in appliances, automotive systems, and consumer electronics. • Compilers and Interpreters: • Many compilers and interpreters for other programming languages are themselves written in C due to its performance and ability to manipulate system resources effectively. • Game Development, Networking, IoT Devices, Libraries...
binary code (0s and 1s) that the CPU directly executes. • Human Readability: Not human-readable; it's difficult to interpret and write directly. • Portability: Not portable across different types of CPUs, as different CPUs have different instruction sets. • Usage: Used for critical performance-sensitive applications, but typically generated automatically by compilers.
a symbolic representation of machine code instructions. • Human Readability: Slightly human-readable with mnemonics for operations (e.g., MOV, ADD) and labels for memory addresses. • Portability: Hardware-specific and not portable across different CPU architectures. • Usage: Used for system programming, device drivers, and performance-critical code where hardware control is necessary. • First C – compiler was written with assembly
a set of commands that a particular processor (CPU) can execute • These instructions are low-level commands that tell the CPU to perform basic operations • add, subtract, .. • AND, OR, XOR • Data movement • Control Flow • Input / Output • Assembly code is basically using these instruction sets • x86, ARM, MIPS, PowerPC
a tool called assembler • NASM (Linux + Windows) • MASM (Microsoft Macro Assembler) • FASM (Flast Assembler) • Translates the assembly instructions into binary machine code that CPU can execute
focus on programming logic than detailed knowledge of hardware • Portability: same code can be compiled to different architectures • Readability • Maintainability
// This file contains declarations for the I/O functions like printf and scanf. #include <stdio.h> // The main function where program execution begins. // It returns an integer value to the operating system. int main() { // The printf function is used to print the string "Hello, World!" to the console. // The \n is a newline character, which moves the cursor to the next line after printing the string. printf("Hello, World!\n"); // The return statement ends the main function and returns the value 0 to the operating system. // Returning 0 typically indicates that the program finished successfully. return 0; }
you need a 32-bit or 64-bit toolchain. • 64-bit is generally preferred for modern development due to better performance and the ability to handle larger memory. • Posix vs MCF • POSIX Threads • Standard threading model used in Unix-like systems. Code is portable across systems. Widely Supported. #include <pthread.h> • MCF Threads • Not standardized, less portable, designed for massive concurrency, efficiently handling very large number of threads.
integers (whole numbers), both positive and negative. • float (32 bits, 4 bytes)* • Stores single precision floating point numbers (numbers with a fractional part). • double (64 bits, 8 bytes)* • Stores double precision floating point numbers, providing more precision than float. • char (8 bits, 1 byte)* • Stores individual characters or small integers (usually 1 byte). • * Usually, can vary depending on OS and compiler
int temperature = -5; // Can store negative values float weight = 65.5; // A floating-point number float height = 175.0f; // Suffix 'f' denotes a float literal double distance = 123.456; // A double precision floating point number char initial = 'A'; // Stores a single character char escape = '\n'; // Escape character for new line
and negative numbers. • unsigned • Only allows positive values and zero, effectively doubling the maximum value of a data type. • short • Used with int to specify a shorter integer. • long • Used with int to specify a longer integer.
of a single type, e.g., int arr[10];. • Pointers • Variables that store memory addresses of another variable, e.g., int *ptr;. • Structures • A user-defined data type that allows the combination of data items of different kinds, e.g., struct { int age; char *name; } person;. • Union • Similar to structures, but the members that share the same memory location, allowing different types of data in the same location at different times. • Function Pointers • Variables that point to functions.
numbers short int distance = 32000; // Short integer, typically 2 bytes long int population = 1000000; // Long integer, typically 4 or 8 bytes unsigned long int stars = 4294967295; // Large range positive numbers signed short temperature = -32768; // Allows for negative numbers in a small range
convert a variable from one data type to another. • This can be done explicitly using casting operators or implicitly by the compiler • Example int i = 10; float f = (float) i; double d = 9.5; int x = (int) d; float temp = 65.99; char ch = (char) temp;
compiler when passing values between different types without explicit cast • However, care must be taken as implicit casting can sometimes lead to unexpected results or data loss: • Example int i = 42; float f = i; // We do not lose information
output function that is used extensively for formatting and printing data to the console • int printf(const char *format, ...); • format: A format string that includes text to be printed, placeholders for variables (format specifiers), and formatting instructions. • ...: Represents a variable number of arguments that replace the format specifiers in the format string.
or %i Integer in decimal base 42 42 %u Unsigned decimal integer 150 150 %f Floating-point number 3.14159 3.141590 %lf Double precision floating point (used interchangeably with %f in printf) 3.1415926535 3.141593 %e or %E Scientific notation (lowercase or uppercase) 123456.789 1.234568e+05 %g or %G Shortest representation of %f or %e (lowercase or uppercase) 0.00012345 1.2345e-04 %x or %X Unsigned hexadecimal integer (lowercase or uppercase) 255 ff or FF %o Unsigned octal integer 10 12 %c Character 65 A %s String "Hello" Hello %p Pointer address Address of x 0x7ffeefbff8d8 %% Percent sign N/A %
read formatted input from the standard input, typically the keyboard. • It is one of the most common input functions in C and serves as a counterpart to printf, which is used for formatted output. • int scanf(const char *format, ...); • format: A format string that contains one or more format specifiers, which specify the type and format of the data to be read. This string also can contain literals and whitespace characters, which are used to match the input. • ...: The additional arguments must be pointers to variables where the read values are stored.
- Reads a float. • %lf - Reads a double. • %c - Reads a single character. • %s - Reads a string until a whitespace is encountered. • %[...] - Reads a string that matches a set of characters specified within the brackets.
Local Stack Not automatic Duration of function call Limited to the block where declared Temporary data within functions Global Data Segment (.data or .bss) Automatic (zero if uninitialized) Entire program duration Accessible from any part of the program Data needed across multiple functions or modules Static (within functions) Data Segment (.data or .bss) Automatic (zero if uninitialized) Entire program duration Limited to the function/block declared Preserving state between function calls Static (global) Data Segment (.data or .bss) Automatic (zero if uninitialized) Entire program duration Limited to the file where declared Module-wide private data Dynamic Heap Not automatic Controlled by programmer Controlled by pointers Flexible size data or when amount is unknown at compile time
Memory on the stack is automatically created when a function starts and cleaned up when the function exits • Best used for small data that will not need to be resized and for data whose lifetime is well-known and coincides with the scope of the block in which it is declared
of memory where blocks of memory are dynamically allocated and freed, often in an arbitrary order. • The heap can grow dynamically and is generally limited by the size of the virtual memory in the operating system. • Heap allocation involves more complex management, which can include searching for a block of free memory of adequate size, which makes it slower compared to stack allocation • The lifetime of heap variables is controlled by the programmer
int a = 5; printf("Address of a is %p\n", &a); int *memoryAddressOfA = &a; printf("Address is a is %p\n", memoryAddressOfA); // let's change a *memoryAddressOfA = 6; printf("Value of a is %d\n", a); return 0; }
= malloc(sizeof(int)); // Allocate memory on the heap printf("Address is %p\n", heapVar); printf("Value is %d\n", *heapVar); *heapVar = 20; // Assign value to allocated memory printf("Heap variable value: %d\n", *heapVar); free(heapVar); // Free the allocated memory heapVar = NULL; // Good practice to set pointer to NULL after freeing return 0; }
{ int *heapVar = malloc(sizeof(int)); // Allocate memory on the heap printf("Address is %p\n", heapVar); printf("Value is %d\n", *heapVar); *heapVar = 20; // Assign value to allocated memory printf("Heap variable value: %d\n", *heapVar); } // free(heapVar); // Free the allocated memory // heapVar = NULL; // Good practice to set pointer to NULL after freeing return 0; }
= NULL; // Declare heapVar outside the if block if(1) { heapVar = malloc(sizeof(int)); // Allocate memory on the heap printf("Address is %p\n", heapVar); *heapVar = 20; // Assign value to allocated memory printf("Value is %d\n", *heapVar); printf("Heap variable value: %d\n", *heapVar); } // Free the allocated memory outside the if block if (heapVar != NULL) { free(heapVar); heapVar = NULL; // Good practice to set pointer to NULL after freeing } return 0; }
= {1, 2, 3, 4, 5}; // Array declared on the stack int i; // Loop variable also on the stack printf("Array elements are: "); for (i = 0; i < 5; i++) { printf("%d ", arr[i]); } printf("\n"); return 0; }
malloc and free functions int main() { int size = 5; int *arr = malloc(size * sizeof(int)); // Dynamically allocate memory for the array on the heap // Initialize array elements for (int i = 0; i < size; i++) { arr[i] = i + 1; // Assign values to the array } printf("Array elements are: "); for (int i = 0; i < size; i++) { printf("%d ", arr[i]); } printf("\n"); free(arr); // Free the allocated memory arr = NULL; // Set pointer to NULL to avoid dangling pointer return 0; }
for malloc and free functions int main() { int size; // Ask user for the size of the array printf("Enter the size of the array: "); scanf("%d", &size); int *arr = malloc(size * sizeof(int)); // Dynamically allocate memory for the array on the heap // Initialize array elements for (int i = 0; i < size; i++) { arr[i] = i + 1; // Assign values to the array } printf("Array elements are: "); for (int i = 0; i < size; i++) { printf("%d ", arr[i]); } printf("\n"); free(arr); // Free the allocated memory arr = NULL; // Set pointer to NULL to avoid dangling pointer return 0; }
arrays can be problematic due to its potential to cause buffer overflow • Buffer overflow occurs when the data written to a buffer exceeds its storage capacity, which can lead to undefined behavior, program crashes, or security vulnerabilities. • Example • char name[10]; • printf("Enter your name: "); • scanf("%s", name); // Unsafe: does not limit input size
char str[20]; printf("Enter a string: "); fgets(str, sizeof(str), stdin); // Read string with space handling // Replace \n with \0. \0 marks the end of the string int index = strcspn(str, "\n"); str[index] = '\0'; printf("You entered: %s\n", str); // String manipulation int len = strlen(str); printf("String length: %d\n", len); strcat(str, " World"); printf("String after concatenation: %s\n", str); return 0; }
int size = 256; // Define the maximum size of the input char *str = malloc(size); // Dynamically allocate memory for the string printf("Enter a string: "); fgets(str, size, stdin) // Remove the newline character if present str[strcspn(str, "\n")] = '\0'; printf("You entered: %s\n", str); free(str); // Free the allocated memory return 0; }
specific task • Help you organize your programs by dividing them into smaller, manageable, and reusable parts • Using functions can make your code more modular, easier to maintain, and more readable.
data the function will return. It can be any data type such as int, char, void, etc. A void return type means the function does not return a value. • Function Name: • The identifier by which the function can be called in other parts of the program. • Parameters (Optional): • Variables that accept values passed into the function. These parameters act as local variables within the function. • Function Body: • The block of code that defines what the function does. It includes a series of statements enclosed in curly braces {}. • Return Statement (Conditional): • This statement is used to return a value from the function. It is required if the function declares a return type other than void.
y); int main() { int result = multiply(10, 5); printf("Result: %d\n", result); return 0; } // Function definition int multiply(int x, int y) { return x * y; // Return the product of x and y }
that allow you to encapsulate multiple data items of potentially different types into a single cohesive unit. • Composite Type: • A struct (short for "structure") is a user-defined data type in C that groups together different data items (called members) under a single name. • Encapsulation: • Structs help in encapsulating related data items, making it easier to manage and organize data in complex programs.
statements, expressions, control structures (if, for, while) • Primitive data types: int, char, float, double • Functions and pointers • Differences from C • Classes and objects • Access specifiers: public, private, protected • Constructors and destructors • Inline functions
declare the interfaces to your code, including functions, classes, constants, and macros. • Inclusions: • They are included in other files using the #include directive to share declarations across multiple source files without redefining them.
declared in the header files. • Class Method Definitions: Implements the methods of the classes declared in the header files. • Local Functions: Defines any additional functions that are not exposed to other parts of the program.
std::cout << mymath1::max(5, 5) << std::endl; std::cout << mymath2::max(5, 5) << std::endl; return 0; } Possible to have max – method in two different namespaces, no name collisions
the same name with different parameters. • It helps improve code readability and usability. • Functions must differ in the type or number of parameters. • Return type alone is not sufficient to overload a function.
void eat() { std::cout << "I can eat!" << std::endl; } }; // Creating an object int main() { Animal animal1; animal1.eat(); // Output: I can eat! return 0; }
{ std::cout << "I can eat!" << std::endl; } }; // Derived class class Dog : public Animal { public: void bark() { std::cout << "I can bark!" << std::endl; } }; int main() { Dog dog1; dog1.eat(); // Output: I can eat! dog1.bark(); // Output: I can bark! return 0; }
Allocate memory on the heap int* ptr = nullptr; if (true) { // The condition is always true, but it could be any condition ptr = new int; // Step 2: Assign a value to the allocated memory *ptr = 42; // Step 3: Use the allocated memory std::cout << "Value: " << *ptr << std::endl; // Memory leak here: No delete statement to deallocate the memory // The allocated memory is never freed } // Outside the if block, there is no delete statement // ptr still points to the allocated memory but it's not deallocated return 0; }
allows for generic programming. • Enable functions and classes to operate with any data type without being rewritten for each type • There are two basic main types of templates • function templates • class templates
T add(T a, T b) { return a + b; } int main() { int intResult = add(3, 4); // T is int double doubleResult = add(2.5, 3.1); // T is double std::cout << "intResult: " << intResult << std::endl; // Outputs: intResult: 7 std::cout << "doubleResult: " << doubleResult << std::endl; // Outputs: doubleResult: 5.6 return 0; }
list of integers on the heap std::list<int>* myList = new std::list<int>; // Insert elements into the list myList->push_back(10); // Add 10 to the end myList->push_back(20); // Add 20 to the end myList->push_back(30); // Add 30 to the end // Print the list elements std::cout << "List elements: "; for (int value : *myList) { std::cout << value << " "; } std::cout << std::endl; // Insert at the front myList->push_front(5); // Add 5 to the front // Print the list elements again std::cout << "List elements after push_front: "; for (int value : *myList) { std::cout << value << " "; } std::cout << std::endl; // Delete an element by value myList->remove(20); // Remove 20 from the list // Print the list elements again std::cout << "List elements after removal: "; for (int value : *myList) { std::cout << value << " "; } std::cout << std::endl; // Access the first and last elements std::cout << "First element: " << myList->front() << std::endl; std::cout << "Last element: " << myList->back() << std::endl; // Free the memory allocated for the list delete myList; return 0; } // g++ -std=c++11 *.cpp -o myapp
• Only one unique_ptr can own a resource at a time • When the unique_ptr goes out of scope, it automatically deletes the resource. • shared_ptr allows multiple pointers to share ownership of a resource. • The resource is deleted when the last shared_ptr owning it is destroyed.
ptr = new int(10); // Allocate memory on the heap std::cout << "Value: " << *ptr << std::endl; // Forgetting to delete the allocated memory } // Memory allocated inside the if block is not freed, causing a memory leak return 0; }
int main() { if (true) { std::unique_ptr<int> ptr = std::make_unique<int>(10); // Allocate memory on the heap using unique_ptr std::cout << "Value: " << *ptr << std::endl; // No need to manually delete, unique_ptr automatically deletes the memory when it goes out of scope } // Memory allocated inside the if block is automatically freed when the unique_ptr goes out of scope return 0; }
its surrounding scope. • Basic Syntax: • [ capture ] ( params ) -> ret { body } • Capture Clause ([]): Specifies which variables from the surrounding scope are captured and how. • Parameter List (( params )): Defines the parameters the lambda takes. • Return Type (-> ret) (optional): Specifies the return type. If omitted, the compiler deduces it. • Body ({ body }): Contains the code to be executed when the lambda is called.
the lambda function with std::function std::function<int(int, int)> add = [](int a, int b) -> int { return a + b; }; // Use the lambda function to add two numbers int sum = add(5, 3); // Print the result std::cout << "The sum of 5 and 3 is: " << sum << std::endl; return 0; }
lambda function auto add = [](int a, int b) -> int { return a + b; }; // Use the lambda function to add two numbers int sum = add(5, 3); // Print the result std::cout << "The sum of 5 and 3 is: " << sum << std::endl; return 0; }
two integers and a callback function void performOperation(int a, int b, const std::function<int(int, int)>& callback) { // Call the callback function with the provided integers int result = callback(a, b); // Print the result std::cout << "The result of the operation is: " << result << std::endl; } int main() { // Define the lambda function for summing two numbers auto sum = [](int a, int b) -> int { return a + b; }; // Define the lambda function for subtracting two numbers auto extract = [](int a, int b) -> int { return a - b; }; // Use the lambda function for summing as a callback performOperation(5, 3, sum); // Should print "The result of the operation is: 8" // Use the lambda function for extracting as a callback performOperation(5, 3, extract); // Should print "The result of the operation is: 2" return 0; }
Function that takes two integers and a callback function void performOperation(int a, int b, const std::function<int(int, int)>& callback) { // Call the callback function with the provided integers int result = callback(a, b); // Print the result std::cout << "The result of the operation is: " << result << std::endl; } int main() { // Use an anonymous lambda function for summing as a callback performOperation(5, 3, [](int a, int b) -> int { return a + b; }); // Should print "The result of the operation is: 8" // Use an anonymous lambda function for subtracting as a callback performOperation(5, 3, [](int a, int b) -> int { return a - b; }); // Should print "The result of the operation is: 2" return 0; }
// Function that reads a file in a different thread and invokes the callback with the file content void fileRead(const std::string& filePath, std::function<void(const std::string&)> callback) { // Create a thread to read the file std::thread([filePath, callback]() { std::ifstream inputFile(filePath); if (!inputFile) { std::cerr << "Unable to open file: " << filePath << std::endl; return; } std::string content; std::string line; while (std::getline(inputFile, line)) { content += line + '\n'; } inputFile.close(); // Invoke the callback with the file content callback(content); }).detach(); } int main() { // Example usage of fileRead function fileRead("path/to/foo.txt", [](const std::string& content) { std::cout << "File content:\n" << content << std::endl; }); // Prevent main from exiting immediately std::this_thread::sleep_for(std::chrono::seconds(2)); // Adjust time as needed for file reading to complete return 0; } allows the thread to run independently from the std::thread object that originally represented it. After calling detach, the thread becomes a daemon thread