C and Cpp Basics - marzoukali/cs-notes GitHub Wiki

General Notes:

  • Todo: How to use CMake and makefile to build the project.
  • switch to C99 mode in CodeBlocks: To switch to C99 mode in CodeBlocks, follow the next steps:
    1. Click Project/Build options, then in tab Compiler Settings choose subtab Other options, and place -std=c99 in the text area, and click Ok.
    2. This will turn C99 mode on for your Compiler.

Recommended C Style and Coding Standards:

Arrays:

One-dimensional:

Notes:

  • We can define dynamic arrays with malloc or static arrays with new keyword in c.
  • A very descriptive example to shows exactly where the arrays stored in memory (C++):
// C++ code -- non-vector parts are also true for C
 
int arr1[100000]; // BSS
vector<int> arr2; // HEAP
 
struct DumbStruct {
    int someArr[10000];
};
 
int main () {
    int arr3[100000]; // STACK
    vector<int> arr4; // HEAP
    int* arr5 = new int[100000]; // HEAP
    int* arr6 = (int*) malloc(100000 * sizeof(int)); // HEAP
    static int arr7[100000]; // BSS
    DumbStruct struct; // STACK
    DumbStruct* struct2 = new DumbStruct(); // HEAP
    vector<DumbStruct> structarr; // HEAP
    int n;
    scanf("%d", &n);
    int arr8[n]; // STACK (assuming C99 -- this does not compile in C++)
}

Structures:

Notes:

  • A structure is a user-defined data type in C/C++. A structure creates a data type that can be used to group items of possibly different types into a single type.
  • Note: In C++, the struct keyword is optional before in declaration of a variable. In C, it is mandatory.
  • What is designated Initialization in C99?

Struct array:

struct Point 
{ 
   int x, y; 
}; 
  
int main() 
{ 
   // Create an array of structures 
   struct Point arr[10]; 
  
   // Access array members 
   arr[0].x = 10; 
   arr[0].y = 20; 
  
   printf("%d %d", arr[0].x, arr[0].y); 
   return 0; 
} 

Struct Pointer:

struct Point 
{ 
   int x, y; 
}; 
  
int main() 
{ 
   struct Point p1 = {1, 2}; 
  
   // p2 is a pointer to structure p1 
   struct Point *p2 = &p1; 
  
   // Accessing structure members using structure pointer 
   printf("%d %d", p2->x, p2->y);
   // We can access the members this way too: (*p2).x but the above is much handy.
   return 0; 
}

Pointers:

What is a pointer?

A pointer is a variable whose value is the address of another variable, i.e., direct address of the memory location.

Why we are using pointers:

  1. Cary another location address even if it's in a stack or heap.
  2. Accessing Heap.
  3. Accessing Hard disk (File Type pointer).
  4. Parameter passing.

A simple declaration and usage:

#include <stdio.h>

int main () {

   int  var = 20;   /* actual variable declaration */
   int  *ip;        /* pointer variable declaration */

   ip = &var;  /* store address of var in pointer variable*/

   printf("Address of var variable: %x\n", &var  );

   /* address stored in pointer variable */
   printf("Address stored in ip variable: %x\n", ip );

   /* access the value using the pointer */
   printf("Value of *ip variable: %d\n", *ip );

   return 0;
}

Output:

Address of var variable: bffd8b3c
Address stored in ip variable: bffd8b3c
Value of *ip variable: 20

NULL Pointers:

It is always a good practice to assign a NULL value to a pointer variable in case you do not have an exact address to be assigned.

#include <stdio.h>

int main () {

   int  *ptr = NULL;

   printf("The value of ptr is : %x\n", ptr  );
 
   return 0;
}

Output:

The value of ptr is 0

Allocating space in heap using malloc:

The function malloc is used to allocate a certain amount of memory during the execution of a program. The malloc function will request a block of memory from the heap. If the request is granted, the operating system will reserve the requested amount of memory.

When the amount of memory is not needed anymore, you must return it to the operating system by calling the function free.

#include<stdio.h>

	int main()
	{
		int *ptr_one;

		ptr_one = (int *)malloc(sizeof(int));

		if (ptr_one == 0)
		{
			printf("ERROR: Out of memory\n");
			return 1;
		}

		*ptr_one = 25;
		printf("%d\n", *ptr_one);

		free(ptr_one);

		return 0;
	}

Note: In C++ It's easy to just allocate space in heap using new keyword like that:

int *p;
p = new int[5];

And this is similar to the below in C:

int *p;
p = (int *)malloc(5*sizeof(int));

Allocating a struct to the heap:

    struct Point{int x; int y;};

int main()
{
     struct Point *p;
     p = (struct Point *)malloc(sizeof(struct Point));
     p->x = 1;
     free(p);
     return 0;
}

delete operator vs free(..) function in heap deallocations:

  • free(..) is a c function that can be used with c++ too but delete is a C++ operator.
  • delete free the allocated memory and calls the destructor. But free() de-allocate memory but does not call the destructor.
  • delete is faster than free() because an operator is always faster than a function.

Functions:

Call by value:

void swap1(int a, int b) {
    int temp = a;
    a = b;
    b = temp;
    assert(a == 17);
    assert(b == 42);
    // they're swapped!
}

int x = 42;
int y = 17;
swap1(x, y);
assert(x == 42);
assert(y == 17);
// no, they're not swapped!

Call by pointer (extra memory):

void swap2(int* a, int* b) {
    int temp = *a;
    *a = *b;
    *b = temp;
    assert(*a == 17);
    assert(*b == 42);
    // they're swapped!
}

int x = 42;
int y = 17;
swap2(&x, &y); // give the function pointers to our variables
assert(x == 17);
assert(y == 42);
// yes, they're swapped!

For swapping something like char* which is the first pointer to a string in c we need to swap the addresses:

void swapStrings(char** a, char** b){
    char *temp = *a;
    *a = *b;
    *b = temp;
    assert(strcmp(*a, "world") == 0);
    assert(strcmp(*b, "Hello") == 0);
}

char* x = "Hello";
char* y = "world";
swapStrings(&x, &y);
assert(strcmp(x, "world") == 0);
assert(strcmp(y, "Hello") == 0);

Call by ref (no extra memory as it's an alias) (C++ only).

Passing Arrays to funcs:

Notes:

  • Passing array is always passing the address.
  • working with arrays in c can be dynamic like int * or static like int arr[]
  • Examples:

void print_array(int *arr, int length)
{
    int i;
    for(i=0; i<length; ++i)
    {
        printf("%d ", arr[i]);
    }
}

int* Generate_array(int length)
{
    int *arr = (int *)malloc(length*sizeof(int));

    int i;
    for(i=0; i<length; ++i)
    {
        arr[i] = i*2;
    }
    return (arr);
}

And the usage:

int* arr = Generate_array(5);
print_array(arr, 5);
free(arr);

Passing structs to func:

Notes:

  • struct is a value type which will be passed by value but of course we can pass it by ref or address.
  • If we have a struct that contains a ref type like an array, so this array will be copied as a what's happening exactly in the value types and changing in it will not change the passed array.

References in C++:

What is references in C++?

A reference variable is an alias, that is, another name for an already existing variable. Once a reference is initialized with a variable, either the variable name or the reference name may be used to refer to the variable.

References Vs. Pointers:

References are often confused with pointers but three major differences between references and pointers are −

  • You cannot have NULL references. You must always be able to assume that a reference is connected to a legitimate piece of storage.
  • Once a reference is initialized to an object, it cannot be changed to refer to another object. Pointers can be pointed to another object at any time.
  • A reference must be initialized when it is created. Pointers can be initialized at any time.

Example:

#include <iostream>
 
using namespace std;
 
int main () {
   // declare simple variables
   int    i;
   double d;
 
   // declare reference variables
   int&    r = i;
   double& s = d;
   
   i = 5;
   cout << "Value of i : " << i << endl;
   cout << "Value of i reference : " << r  << endl;
 
   d = 11.7;
   cout << "Value of d : " << d << endl;
   cout << "Value of d reference : " << s  << endl;
   
   return 0;
}

Output:

Value of i : 5
Value of i reference : 5
Value of d : 11.7
Value of d reference : 11.7

OOP In C & C++:

Is C really OOP:

  • Not really but we can literally encapsulate, polymorphism, play with inheritance, and even abstraction using it. It's definitely not OOP paradigm based but the concepts are supported there (Thanks pointers!) - The Clean Architecture book well mentioned this part.

C++ Class examples and notes:

  • In C++ we have two ways of defining a class:
  1. Inside class definition.
  2. Outside class definition.
  • Example:
#include <bits/stdc++.h> 
using namespace std; 
class Geeks 
{ 
    public: 
    string geekname; 
    int id; 
      
    // printname is not defined inside class defination 
    void printname(); 
      
    // printid is defined inside class defination 
    void printid() 
    { 
        cout << "Geek id is: " << id; 
    } 
}; 
  
// Definition of printname using scope resolution operator :: 
void Geeks::printname() 
{ 
    cout << "Geekname is: " << geekname;  
} 
int main() { 
      
    Geeks obj1; 
    obj1.geekname = "xyz"; 
    obj1.id=15; 
      
    // call printname() 
    obj1.printname(); 
    cout << endl; 
      
    // call printid() 
    obj1.printid(); 
    return 0; 
}
  • Constructors in C++:
  1. Default.
  2. Parametrize.
  3. Copy constructor (For deep or shallow copying)
  • Destructor ~className(){...}

Template Example:

  • Templates are expanded at compiler time. This is like macros. The difference is, compiler does type checking before template expansion. The idea is simple, source code contains only function/class, but compiled code may contain multiple copies of same function/class.

  • C++ adds two new keywords to support templates: ‘template’ and ‘typename’. The second keyword can always be replaced by keyword ‘class’.

  • Functions Templates:

template <typename T> 
T myMax(T x, T y) 
{ 
   return (x > y)? x: y; 
} 
  
int main() 
{ 
  cout << myMax<int>(3, 7) << endl;  // Call myMax for int 
  cout << myMax<double>(3.0, 7.0) << endl; // call myMax for double 
  cout << myMax<char>('g', 'e') << endl;   // call myMax for char 
  
  return 0; 
}
  • Class Template Example:
template <typename T> 
class Array { 
private: 
    T *ptr; 
    int size; 
public: 
    Array(T arr[], int s); 
    void print(); 
}; 
  
template <typename T> 
Array<T>::Array(T arr[], int s) { 
    ptr = new T[s]; 
    size = s; 
    for(int i = 0; i < size; i++) 
        ptr[i] = arr[i]; 
} 
  
template <typename T> 
void Array<T>::print() { 
    for (int i = 0; i < size; i++) 
        cout<<" "<<*(ptr + i); 
    cout<<endl; 
} 
  
int main() { 
    int arr[5] = {1, 2, 3, 4, 5}; 
    Array<int> a(arr, 5); 
    a.print(); 
    return 0; 
} 
  • Templates Notes:
  1. like normal parameters, we can pass more than one data types as arguments to templates.
  2. like normal parameters, we can specify default arguments to templates.
  3. Both function overloading and templates are examples of polymorphism feature of OOP. Function overloading is used when multiple functions do similar operations, templates are used when multiple functions do identical operations.
  4. Template specialization allows us to have different code for a particular data type.
  5. If there is a static member, Each instance of a template contains its own static variable.
  6. We can pass non-type arguments to templates.

Important links:

Well designed projects:

⚠️ **GitHub.com Fallback** ⚠️