Note: Most of the content here are not my original content. They are mainly taken from free to use existing resources such as geekforgeek or tutorialpoint etc. You can google any topics in the notes below and you can find these content original sources. Hence All credits goes to original authors and contributors. I have just compiled it.
Note: for c++ setup in VScode please watch this video link
A C program consists of
- Preprocessor command and directive (#include)
- Functions, variable, statements of expression and comments
Compilation of basic C program more info here
- Preprocessing (*.i files)
- Compilation (*.s files - assembly files)
- Assembly (*.o files)
- Linking - static and dynamic linking(*.dll files) (Linking of all the *.o files to *.exe)
Make sure to use Debuger in C/C++ programs to code better More info here
Advance debugging skills More info here
Command to see the compilation process for C program in GCC.
gcc -wall -save-temps main.c -o output
Some code snipped for basic C program is
"main": {
"prefix": "maincode",
"body": [
"#include <stdio.h>",
"int main() {",
"\t$1",
"return 0;",
"}",
],
"description": "For settingup basic main program of C"
}
Some other concept of C program are keywords and reserved words. Identifiers are names of functions or variables. Be careful how to declare variable in C as it does not allow special characters such as @, $ etc as identifiers. C programs are case sensitive. Readablity and maintainablity of programs are good things. We can use tools like makefile for large C programs compilations. C program is composed of keywords, identifiers, constants, string literals and symbols.
Different types of variables in C Programming are primary, derived -array,pointer, structure, union etc. and user defined. more info here
int,char,float, double, long,
Syntax
type variable_name1,name2 = value;
Arthmatic +,-./..%
bitwise - &,|,^ -xor,<< - shift left,>> - shift right, ~ -one's complement
logical &&,||,!,
assigment =, += (AddAND operator add right operand to left operand and store in the left operand), -=,=,/=
relational operators ==,!=,>=,<=,>,<
sizeof() operator,
& - addressOF operator
'*' - pointer declaration and dereferencing operator
?: - conditional expression
-> - AccessThroughPointer operator
Very important concept in C is called operator Precedence More info Link
Tells the complier about what type of datatype is in a variable.
Exaples: %d for integer, %f for floating point, %c for character, %l for long, %lf for double, %Lf for long double etc.
Another type of format specifiers are escape sequences such as \n for newline, \t for tab, \r for carriage return etc.
And finally we have comments in C // for single line comments and /**/ for multiline comments.
Value that cannot be changed is constants.
// is the keyword
const
Or we can use #define
#define
- If statement
- if else statement
- if else if else ladder
- Switch cases (including default case ) : This avoid too much of if statements.
- Nested if statements
- Nested switch is also possible
int expression = 10;
switch (expression) // must be an integer expression or character expression
{
case x:
// code block
break;
// it is not a must to have break statements BUT its recomended to use break statements.
case y:
// code block
break;
default:
// code block
}
Used for repeations. There are three major loops
- for loop
- While loop (integer and boolean)
- Do while loop (integer and boolean) - At least one execution is gaurenteed Make sure that you will not enter the infinite loop.
continue; // statement is used to just break out of the loop to next iteration by skipping some statements of the codes inside the loops.
break; // statement can be used to break out of the loops and switch cases
This statement is mainly used if we want to break multiple loops with single statements
goto; // used for jumpuing statements AVOID using it as it causes confusion in c code execution
Modularity and reusability is obtained through functions. Easy debugging, divide work among people and easy maintainability. Different types of functions are library function and user defined functions.
Functions need DECLARATION, DEFINITION and CALL
return_type function_name (para1_type para1_name, para2_type para2_name)
{
// body of the function
}
Recursive function: When a function call a copy of itself to solve a problem. Please not it require a termination condition. Also recursion can be replaced with iteration and often problems are broken into base cases and recursive cases. e.g. tower of hanoi, fibonacci series and factorial problems . More info here
type function_name (args) {
// function statements
// base condition
// recursion case (recursive call)
}
We can provide arrays as function parameters using the following
- by declaring array as function parameters
- by passing address of array as parameter through pointer.
Arrays in C are always passed to the function as pointers pointing to the first element of the array.
List of collection of data of same type. Its stored in continuous consecutive locations. It can also store derived data types in memory location. their is no limit to dimension of array. 1D array is Vectors, 2D array is Matrices, 3 or more D array are tensors. Array has indexes to access its element. we can also use pointers to access elements of array. Any element of an array can be accessed very quickly using indexes or base address arthmatics. More info here
data_type array_name [size];
or
data_type array_name [size1] [size2]...[sizeN]; // N-dimensional array
The strings is nothing but character array with termination character such as \0, also known as null character. \0 is used to declare termination character. Without null character you cannot store the string. Hence length of string is length of string + 1. String is not a builtin data type in C, its just a character array terminated by null character.
char variable_name[r] = {list of string};
char name[] = {"STRING"}; // C compiler will automatically put null character at the end of the string AS WE HAVE USED "" to define string
char namechar[]= {'S','T','R','I','N','G','\0'} // null character is used
We have builtin string functions in C.
- strlen(stringname) returns length of string
- strcpy(s1, s2) Copies the contents of string s2 to string s1.
- strcmp(str1, str2) Compares the first string with the second string. If strings are the same it returns 0.
- strcat(s1, s2) Concat s1 string with s2 string and the result is stored in the first string.
- strlwr() Converts string to lowercase.
- strupr() Converts string to uppercase.
- strstr(s1, s2) Find the first occurrence of s2 in s1.
Variable that stores address of another variable is called pointer. Pointer size depends on size of the variable its storing. Pointers are declared using * symbol. More information can be found Link
'*' is used to declare pointer variable.
'*ptr' is known as dereferencing operator for pointer variable ptr.
'&' is known as address of operator, used to store address to pointer variable.
Pointer Syntax
datatype * ptr;
Usage of Pointer
- Declaration of pointer
int *ptr;
- Initialization of pointer
int var = 10;
int * ptr;
ptr = &var; // pointer initialization
- dereferencing of pointer
printf("Value at *ptr = %d \n", *ptr); // pointer dereferencing
Types of pointers
- int, float etc. pointers
- NULL pointer - pointer with NULL value is assigned, often used with if statement
data_type *pointer_name = NULL;
When you declare a pointer without any initial value, the pointer has garbage value unless you declare it NULL. also NULL is used to check the status of pointer. Null pointer value is reserved for referencing the not valid object. We cannot dereference null pointer. NULL pointer is preferred over uninitialized pointer. Null pointer is a value of pointer where as void pointer is a type of pointer. Technically Null pointer is ((void *)0) void pointer multiplied by zero.
- Array pointer and multi-dimensional array - more info
char *ptr = &array_name;
- struct pointer
struct struct_name *ptr;
- function pointer
int (*ptr)(int, char);
- double pointer
datatype ** pointer_name;
*pointer_name; // get the address stored in the inner level pointer
**pointer_name; // get the value pointed by inner level pointer
- void pointer - generic pointer
//void * pointer_name;
int x=10;
char y='A';
void *p;
p = &x; // void pointer is type casted to int pointer
p = &y; // void pointer is type casted to char pointer
It has no data type associated with it. And hence it can easily be type casted to any other data type, its a general purpose pointer.
In dynamic memory allocation the pointer is (void *) using malloc() and calloc(). We cannot dereference void pointer directly. And pointer arthmatic is not allowed in void pointer.
- wild pointer - pointer with no initial value or garbage value
A pointer that has not been initialized to anything (not even NULL) is known as a wild pointer. The pointer may be initialized to a non-NULL garbage value that may not be a valid address.
int *ptr;
char *str;
- Dangling pointer
Its a pointer with pointing to a freed memory location or the location that has been deleted. Dangling pointer arises during object destruction. Its adisable to setup a dangling pointer to NULL unless been used again. Also make sure that your functions will always check for NULL and dangling pointers.
Three main ways a dangling pointer is formed: Deallocation, our of scope variable, and returning local variable in the function call.
When memory is free but the pointer keeps pointing the same spot with some garbage value, give rise to a dangling pointer. Secondly when the local variable is not static and the function returns a pointer to that local variable. The pointer pointing to the local variable becomes dangling pointer. And thirdly When a variable goes out of scope the pointer pointing to that variable becomes a dangling pointer.
and many more types of pointer
Pointer Arithmetics
There are 4 types of arithmetic operation performed on pointers.
- ++ and -- , + and -
- Increment in a Pointer
- Decrement in a Pointer
- Addition of integer to a pointer
- Subtraction of integer to a pointer
- Subtracting two pointers of the same type
- Comparison of pointers of the same type.
- Assignment of pointers of the same type.
int a = 2;
int* ptr;
ptr = &a;
// Pointer arithmetic
ptr = ptr + 1; // pointer ptr + the size of the pointer ptr which here is 8 bytes
Uses of Pointers in C
- Pass Arguments by Reference
- Accessing Array Elements
- Return Multiple Values from Function
- Dynamic Memory Allocation
- Implementing Data Structures
- In System-Level Programming where memory addresses are useful.
- In locating the exact value at some memory location.
- To avoid compiler confusion for the same variable name.
- To use in Control Tables.
Advantages of Pointers
- Pointers are used for dynamic memory allocation and de-allocation.
- An Array or a structure can be accessed efficiently with pointers
- Pointers are useful for accessing memory locations.
- Pointers are used to form complex data structures such as linked lists, graphs, trees, etc.
- Pointers reduce the length of the program and its execution time as well.
Disadvantages of Pointers
- Memory corruption can occur if an incorrect value is provided to pointers.
- Pointers are a little bit complex to understand.
- Pointers are majorly responsible for memory leaks in C.
- Pointers are comparatively slower than variables in C.
- Uninitialized pointers might cause a segmentation fault.
Pointers and Two-Dimensional Arrays
In a two-dimensional array, we can access each element by using two subscripts, where the first subscript represents the row number and the second subscript represents the column number. The elements of 2-D array can be accessed with the help of pointer notation also.
Suppose arr is a 2-D array, we can access any element arr[i][j] of the array using
the pointer expression * (*(arr + i) + j). Now we’ll see how this expression can be derived.
Multi dimension array is stored in the same way as single dimension array, but access in multi dimension way with indexes. With pointer access of values is different than from indexes.
// C program to print the elements of 3-D
// array using pointer notation
#include<stdio.h>
int main()
{
int arr[2][3][2] = {
{
{5, 10},
{6, 11},
{7, 12},
},
{
{20, 30},
{21, 31},
{22, 32},
}
};
int i, j, k;
for (i = 0; i < 2; i++)
{
for (j = 0; j < 3; j++)
{
for (k = 0; k < 2; k++)
printf("%d\t", *(*(*(arr + i) + j) +k));
printf("\n");
}
}
return 0;
}
The output of the above program is
5 10
6 11
7 12
20 30
21 31
22 32
The memory layout of the multi dimension array is below.
More information in array pointers can be found here
Actual and formal parameters in function call expressions. In one way it is called by the direct value call/formal value call expression and in the second way it is called by by the addresses/actual value call in the function call expression. hence the return value of the function will also be changed by the call expression. By call by value the original values of the variables will not be changed where as the original values of the variables will be changed when call by reference. more information here
It is user defined datatype. It also combines multiple datatypes. Often DOT operator is used to navigate structures.
struct structure_name {
data_type member_name1;
data_type member_name1;
....
....
};
// varible structure declaretion
struct structure_name {
data_type member_name1;
data_type member_name1;
....
....
}variable1, varaible2, ...;
// structure declared beforehand
struct structure_name variable1, variable2, .......;
// accessing data member in structure
structure_name.member1;
strcuture_name.member2;
// initialization of structure with values
struct structure_name str;
str.member1 = value1;
str.member2 = value2;
str.member3 = value3;
.
.
.
// Or we can do the same as
struct structure_name str = { value1, value2, value3 };
// or
struct structure_name str = { .member1 = value1, .member2 = value2, .member3 = value3 };
We can use typedef in declaration of structure in C, so that we do not have to to write long structure declarations further in the programme. We can almost use struct as a variable after declaring the structure. typedef is used to give alternate name to existing data structures. Also known as alias name
typedef previous_name alias_name;
//examples:
typedef unsigned long ul; // now ul can be used instead of unsigned long
ul l1,l2,l3; // now l1 and l2 and l3 are unsigned long
int* p,q; // note it means p is a pointer and q is an integer
// to make both pointer, we use typedef
typedef int* intptr;
intptr ptr1,ptr2; // both ptr1 and ptr2 are integer pointer
struct Foo{
int i;
};
// another way of using typedef with structures
typedef struct student {
int x;
} newstudent;
// defining new name for str1
typedef struct Foo fun;
// we can use typedef in main programme as
int main(){
newstudent A;
fun B;
A.x = 1;
B.i = 2;
......
return 0;
}
// Nested structure
struct parent {
int member1;
struct member_str member2 {
int member_str1;
char member_str2;
...
}
...
}
// Another way to nested structure
struct parent {
struct mem a;
};
struct mem {
int var;
};
// Accessing nested members
str_parent.str_child.member;
Accessing elements in the structure through the pointers is unique. We use dereferencing '*' operator or the arrow '->' operator. The confusion starts as operator and their precedence will play a crucial role in the access of pointer variable. Operator precedence details can be found here
A structure pointer is defined as the pointer which points to the address of the memory block that stores a structure known as the structure pointer. Complex data structures like Linked lists, trees, graphs, etc. are created with the help of structure pointers.
Disclaimer: Please revise array pointers especially 2D and 3D matrices using pointers to enhance understanding of pointer operations. More details are here
Note: difference between structure and class in C++ can be found here
// Declaration of structure
struct Student {
int roll_no;
};
int main()
{
struct Student s1;
struct Student* ptr = &s1; // Declaration od structure pointer
s1.roll_no = 27;
// Method 1 of accessing the structure pointer
printf("Roll Number: %d\n", (*ptr).roll_no); // Access structure using pointer ptr with dereferencing operator '*' and require brackets to manage precedence of operator.
// Method 2 of accessing the structure pointer
printf("Roll No: %d\n", ptr->roll_no); // Access structure using pointer ptr with '->' operator and hence does not require brackets.
return 0;
}
More information for structure pointer can be found here
Difference between c and c++ structure can be found here
It is similar to structure but more memory efficient in implementation and execution compare to structure.
// Declaration for union
union union_name {
datatype member1;
datatype member2;
...
};
// instance of union
union union_name var1, var2, var3...;
// Access to union
var1.member1;
Structure | Union |
---|---|
The size of the structure is equal to or greater than the total size of all of its members. | The size of the union is the size of its largest member. |
The structure can contain data in multiple members at the same time. | Only one member can contain data at the same time. |
It is declared using the struct keyword. | It is declared using the union keyword. |
As we know that local variables will take precedence over global variables, if declared with the same name, same is applicable to formal arguments of the functions.
Static variables will preserve their values even if they go out of scope. And memory will remain throughout the program. Also you always need to initialize the value of static variables. You cannot initialize static variables by calling a function.
static data_type var_name = var_value;
Static variable example below
#include <stdio.h>
// function with static variable
int fun()
{
static int count = 0; // You always need to initialize the static variable otherwise it might be initialized from 0
// static int count= fun(); // you cannot run a function to initialize the static variable
count++;
return count;
}
int main()
{
printf(" %d\n", fun());
printf(" %d\n", fun());
printf(" %d\n", fun());
printf(" %d\n", fun());
return 0;
}
Output is
1
2
3
4
Static Memory Allocation | Dynamic Memory Allocation |
---|---|
Allocation before program execution | Allocation in the program execution |
No memory reuse and cannot be freed | Memory can be freed and reallocated |
Less efficient | More efficient |
Memory in any c program is allocated in four segments. Learn Embedded or micro-controller programming to understand how memory management works in C programming.
- code and text
- Static/Global variables - data segment(if initialized) and bss segments(if data is not initialized) bss= block started by symbol
- Stack (LIFO principle for function calls)
- Heap - for dynamic memory allocation
Use of heap for dynamic memory allocation is
- we create a pointer in our main function to point to a certain block of memory in the heap.
- Address of this pointer is stored in the local variable in the main function. You need to use good debugging skill to understand your program.
- This memory allocated in the heap, even if we over write the pointer, cannot get free automatically as C and C++ do not have concept of garbage collector. Hence you need to cleanup the memory allocated if unused, otherwise it will cause memory leak issues.
command to check memory usage in C and C++ is assuming name of you program is myprogram.c
gcc myprogram.c ; size .\a.exe
the result will look like using zize command of gcc
text data bss dec hex filename
10624 4052 384 15060 3ad4 .\a.exe
There are 4 library functions provided by C defined under <stdlib.h> header file to facilitate dynamic memory allocation in C programming.
- malloc()
The “malloc” or “memory allocation” method in C is used to dynamically allocate a single large block of memory with the specified size.
//ptr = (cast-type*) malloc(byte-size)
ptr = (int*) malloc(100 * sizeof(int));
//Since the size of int is 4 bytes, this statement will allocate 400 bytes of memory. And, the pointer ptr holds the address of the first byte in the allocated memory.
- calloc()
“calloc” or “contiguous allocation” method in C is used to dynamically allocate the specified number of blocks of memory of the specified type. It initializes each block with a default value ‘0’. It has two parameters or arguments as compare to malloc().
//ptr = (cast-type*)calloc(n, element-size);
//here, n is the no. of elements and element-size is the size of each element.
ptr = (float*) calloc(25, sizeof(float));
//This statement allocates contiguous space in memory for 25 elements each with the size of the float.
- free()
“free” method in C is used to dynamically de-allocate the memory. The memory allocated using functions malloc() and calloc() is not de-allocated on their own. Hence the free() method is used, whenever the dynamic memory allocation takes place. It helps to reduce wastage of memory by freeing it.
free(ptr); // assuming ptr is pointer referring to allocated memory location
- realloc()
“realloc” or “re-allocation” method in C is used to dynamically change the memory allocation of a previously allocated memory. re-allocation of memory maintains the already present value and new blocks will be initialized with the default garbage value.
ptr = realloc(ptr, newSize);
//where ptr is reallocated with new size 'newSize'.
Storage classes can be divided in 4 major categories as shown below.
-
Auto
This is the default storage class for all the variables declared inside a function or a block. Hence, the keyword auto is rarely used while writing programs in C language. Auto variables can be only accessed within the block/function they have been declared and not outside them (which defines their scope). Of course, these can be accessed within nested blocks within the parent block/function in which the auto variable was declared. However, they can be accessed outside their scope as well using the concept of pointers given here by pointing to the very exact memory location where the variables reside. They are assigned a garbage value by default whenever they are declared. -
extern
Extern storage class simply tells us that the variable is defined elsewhere and not within the same block where it is used. Basically, the value is assigned to it in a different block and this can be overwritten/changed in a different block as well. So an extern variable is nothing but a global variable initialized with a legal value where it is declared in order to be used elsewhere. It can be accessed within any function/block. Also, a normal global variable can be made extern as well by placing the ‘extern’ keyword before its declaration/definition in any function/block. This basically signifies that we are not initializing a new variable but instead, we are using/accessing the global variable only. The main purpose of using extern variables is that they can be accessed between two different files which are part of a large program. -
static
This storage class is used to declare static variables which are popularly used while writing programs in C language. Static variables have the property of preserving their value even after they are out of their scope! Hence, static variables preserve the value of their last use in their scope. So we can say that they are initialized only once and exist till the termination of the program. Thus, no new memory is allocated because they are not re-declared. Their scope is local to the function to which they were defined. Global static variables can be accessed anywhere in the program. By default, they are assigned the value 0 by the compiler. -
register
This storage class declares register variables that have the same functionality as that of the auto variables. The only difference is that the compiler tries to store these variables in the register of the microprocessor if a free register is available. This makes the use of register variables to be much faster than that of the variables stored in the memory during the runtime of the program. If a free registration is not available, these are then stored in the memory only. Usually, a few variables which are to be accessed very frequently in a program are declared with the register keyword which improves the running time of the program. An important and interesting point to be noted here is that we cannot obtain the address of a register variable using pointers.
Preprocessing means mainly removal of comments, expansion of macros, and expansion of #include files. Preprocessor are not part of compiler. It's just a text substitution tool and every preprocessor directive start with # symbol. More details can be found in the compilation process of C code.
Some examples of preprocessor directives are
- #define - Also known for defining macro or inline functions or substitutions.
- #undefine
- #include - It calls the content of other C files. Header files or .h files mostly include the function prototypes as promise to be resolved at the time of calling.
- #ifdef
- #ifndef
- #else
- #endif
- #elif
- #if
- #pragma - #pragma once means the compiler should include all these files only once at the time of compilations. Irrespective of how many time you open these functions. We can also use #ifndef instead of #pragma once.