Writing an effective Résumé

Hello World! Hope it’s going well for you guys. As the blog title reads, this write-up is about some simple ways to make an effective resume. Personally, I have been on both the sides- the applicant as well as the interviewer. So, I can say that I have a fair idea of what would make a good resume :). The aim of this blog is to share what I think may be helpful to you guys. So, without further ado let’s start decoding easy ways to make a better resume.

I. Use a good editing tool

Let’s start with the basics ! This is the first thing that made my resume look and feel 100 times better.  Earlier I would use Microsoft word for my editing. And, psst, I sucked at it! I do not know the shortcuts to getting the right boundaries, page offsets and also getting alignments I want. (Sure, judge me all you want! :D). Then I started using online editors (such as overleaf) that allow me to build resume using Latex.

What better way than to “code” your resume ;). This trick has been so useful. Sure, it needs a little more effort than word. But, the outcome is worth the effort. It is easy to use them, they allow for anytime-anywhere access, since they store your profile online.

There are many templates available. I would say – do check them out~ .I have added my template picks in case you guys decide to give it a try (at the end of this blog). Let’s now focus on what should be the content, in the following two tricks.

II. Focus on your strengths and interests

This tricks needs your answer to two important questions. Firstly, what you are confident about and secondly how is it relevant to what you want out of this job.

One way to finding answer for these is, to write down what is it that you enjoy and are confident about along with how this can be aligned with the job requirements (the job that you are applying for).

For example – it can be programming in Python or Java or may be you have a good understanding of machine learning algorithms or you love to design new algorithms. Whatever it is, just write down on a paper first.

Then, begin to build your resume around this topic of interest. What is your relevant experience or how have you prepared for a career in it (do you have hobby projects, coursework etc), try and list them. Keep the focus around this through your resume.

Why? Because, the content on your resume to some extent dictates what you may be questioned about. So, when you focus on your strengths while adding content, it automatically translates to confidence in handling the questions thrown at you, during the course of your interview.

III. Avoid this !

I understand the urge to stand out, with your resume. I also understand the pressure of a job search. But, I cannot stress here enough that you should put things in your resume that is truly your work. It’s not too difficult for a person in the industry to identify when you blatantly lie or go overboard on exaggerating. Do not write about things that you have not done or do not understand completely. And, when you do put up impressive numbers or things you have done, you should be able to explain as to how you achieved that. Else, this simply adds unnecessary pressure when you are at the interview, in case you are asked about it.

For example, if you want to write about how you improved the reliability of a software by 15%, then you should be ready to justify how you achieved that number. Just throwing some metrics in the resume is not going to take you a long way. So, focus on what you have done and be prepared to answer how you achieved those numbers as well.

Few more good points I can think of :

  • Put your educational qualification (if experienced, add the highest level degree)
  • Keep your sentences short and effective (long sentences make the reader lose interest)
  • Put other work experience or projects that are not directly relevant to the job you are seeking in a separate section (For example – “Additional Experience“)
  • If you are sharing your resume online, you could add clickable links to your website or project pages, conference journals etc, throughout your resume
  • You could also mention about interpersonal skills (good team player etc). But, also be prepared to prove it or provide examples if needed

And finally proof read, also get it reviewed before using it !

Good luck !


Have selected two templates that you could start with, as listed below :

Template 1  Recent College graduate resume

  • contains all the information needed – fits into one page and is also not cluttered
  • The sections seem sufficient and the content is crisp

Template 2 Experienced candidate resume

  • clearly lists all the prior experience in a concise format

When you open the template with an overleaf account, it opens up the display to edit the contents. Left view has the Latex code that can be edited and the right side shows the output resume document, as in the snapshot below.


And, that’s all I had for this tidbit. Hope these simple tricks helped you guys make a better resume. As always let me know what you feel. You can reach me at decodergirlblog@gmail.com.

Note : This is not sponsored. Overleaf was useful to me and hence I am sharing info about it.


Linked Lists

Data structures Series  – Blog 02

Linked Lists

Welcome back! Let’s continue with the data structures theme. Here’s the blog on the next data structure – that would solve the problem we had with arrays. Essentially, these two problems :

a. Arrays had fixed size , that needed to be specified when we define them. Resizing is an issue. So, developer might allocate a worst case array size, that may not be efficient if the number of elements to be held varies.

b. When we want to rearrange data in an array or insert data in between or front of an array, it  gets expensive.

Linked lists are the data structures which will help us in addressing these issues. Arrays and linked lists both essentially provide a way to store data for a client. Although linked lists to be discussed today, are good for cases arrays may not be great, they do come with their own limitations. We will cover it all in this blog, keep on reading.

Basic Stuff

If you have ever wondered why people often quiz you about linked lists? Wonder no more. It’s  because these are the most basic data structures that form the foundation for understanding more advanced data structures ( such as: binary trees, queues) etc.

Let’s suppose you need to write some basic C program to read all words (strings) contained in an input file. The first question that we would have is – How would you store the words read by the program? If we used a string array here, what would be the size to be allocated for the array? The number of words in the file is not known.

In this case, if we used a linked list, we can read words without wasting memory or be running out of memory (as would be the case, if we used arrays). This is because, as the name implies, linked lists are nothing but a list of elements linked together using a pointer parameter, in addition to the data that needs to be held – words in this case. So, the items to be inserted are just linked to the list, dynamically as and when required.

Now that we have a use case in mind, let’s learn about the internals of linked lists, operations supported and some overheads associated with their usage.


Linked list data structure are similar to arrays, but use an extra parameter – a pointer to link the items together. With this additional overhead of pointer per entry of data to be held, we can overcome the disadvantages of arrays and use memory efficiently. Linked lists use a different strategy for allocating memory for elements as compared to arrays. They allocate memory per element, when a new element needs to be inserted, not as a block as in case of arrays.

As shown below, a set of integer elements are organized in a linked list as shown on the right. We can note that there is an extra address field, that links the items together in the list. Values {2,3,5,7,8} are the data elements. We can clear;y see there is an overhead per element, to hold the pointer to next data element, in case of linked lists. But, even with this, we may be more memory efficient than arrays, when we perform operations such as always insert to the top of the list or storing elements in memory when the number of elements is not known apriori.

Fig 1. Arrays and linked lists elements in memory

We define a linked list element/node and a create a linked list, as below :

/* define node element for a Singly linked list*/
typedef struct Node{
int data;
Node* next;

/* function to create and return linked list node */
Node* create_node(int data){
Node* n = (Node*) malloc(sizeof(Node));
n->data = data; // store the data
n->next = NULL; // initialize next address to NULL
return n;

/* Example code to create a linked list of nodes 
*  Creates a linked list with three data elements/nodes 
*  Linked list : n1->n2->n3->NULL */ 

Node* create_test_list(){

Node* n1 = create_node(10);
Node* n2 = create_node(20);
Node* n3 = create_node(30);

// update pointers
n1->next = n2; // n1 holds pointer to n2
n2->next = n3; //n2  holds pointer to n3

// n3 is the last node of this list and 
// next shall be stored as NULL to imply this.


Operations supported

We can implement various operations on linked lists such as insert(), delete(), lookup(), hasloop(), removeloop()  and so on. It would be a good idea to write code for these operations, to understand linked lists better. Check [1] and [2] under references below to get started.

Doubly Linked List

These are similar to singly linked lists discussed above. But, instead of holding pointer only to the next element, there is an additional pointer per node to point to the previous element as well. Why do we need them? Wait for next week’s blog 🙂 ll1.png

/* Singly Linked list node */
typedef struct Node{
int data;
Node* next;
} Node;

/* Doubly Linked list node */
typedef struct d_Node{
int data;
Node* next ; 
Node* previous; // extra pointer in doubly linked list
} d_Node;


What do you think would be the drawback of a linked list, other than the storage of extra parameter – i.e., a pointer per element. Yes! It is the insert at the nth location and also lookup nth element operations. In arrays, we had an O(1) look up and also O(1) updates.

But, with linked lists, we have to iterate all the n-1 nodes, to find the place to insert nth node to the list. Also, if we want to look-up nth node, we need to traverse n-1 nodes, before we get the value of nth node- resulting in O(n) cost.

Visual is always better to understand. Please check the picture below.

Fig 2. Insert at nth position, operation on arrays and linked list


Further Reading

  1. Linked list problems (Stanford)
  2. Solve some problems related to linked lists

Next, we shall explore the Binary Search Tree data structure which is realized using a doubly linked list discussed here. As always, write to me at decodergirlblog@gmail.com if you have any queries or suggestions. I would love to hear from you. Until then, keep decoding and enjoying your coffee ! 😉


Did you know?

More coffee you donate, higher is your coder karma! Oh! there shall also be more blogs published here. Thank you for being awesome! 😀


Greedy Algorithms

What are Greedy Algorithms?

These refer to a class of algorithms that solve a given problem with a “heuristic” – your best guess ! If we think of it as a multi step solution, greedy algorithms essentially try to make optimal choice locally at each step, in an attempt to get a globally optimal way of solving a given problem.

Also, as they are heuristics – there is no guarantee they always result in the most optimal way to solve a given problem. There may exist some subset corner cases or test data sets on which a given greedy algorithm may not work.

My familiarity with this class of algorithms is mainly because of my work as a graduate student where I tried to implement various routing algorithms. I worked on prototyping a novel hop by hop routing protocol called GSTAR for Future Internet Architecture project. We also tried to extend the unicast protocol to do efficient multicast – using greedy approach!

So, without further ado, let’s start decoding the basics of this algorithmic paradigm.

Components of Greedy Approach

There are two parts to greedy algorithm :

  1. Optimal substructure : This essentially means that solution to a problem of size ‘n’ depends on the optimal solutions for sub-problems of size k, where k < n.
  2. Greedy choice property : A problem is said to satisfy Greedy choice property if a global optimal can be achieved by choosing local optimum.

Note – When a problem has overlapping optimal substructure, we will utilize Dynamic programming strategy (shall be covered in another blog).

Fig 1. Components of a Greedy algorithm

Using Greedy

In general if you have an optimization problem at hand, that is a problem involving finding minimum, maximum, shortest etc, greedy strategy could be used.

Internet Routing !

I wanted to add here that this is also the most popular algorithm paradigm in designing routing protocols. Routing often requires us to find optimal paths to route a packet in a network [4](a connected graph with routers as the nodes) and each edge assigned a weight that might represent delay or number of hops between the router nodes. Be it Prim’s / Krushkal’s algorithm [4] to construct a minimum spanning tree or the Dijkstra’s algorithm to find the shortest path, they all depend on a greedy approach of finding a minimum cost edge at each step and adding nodes to spanning tree or the shortest path set in an attempt to find a globally optimal solution.

Next, we will look at a few such greedy problems here on the blog and understand greedy strategies used for solving them. We can understand better when we solve problems. Let’s start with one of the popular and well known problem :

1. Coin change problem

Problem :

Input : Coin denomination set D with denominations d1>d2>d3 ..> dm and a sum “S”.

Output : Minimum number of coins required to form the sum “S” (Consider you have as many as would like of each coin denomination) , output num[k] denotes the number of coins of denomination dk.

Algorithm :

  • Repeat steps below, until the sum remaining, S = 0 :
  1. Find the largest coin denomination (say dk), which is ≤ S.
  2. Use ⌊Sk⌋ coins of denomination dk, update num[k]
  3. Update S = S mod dk


for k = 1 to m
do {
    num[k] = ⌊S/d[k]⌋ // count as many as you would need of each denomination
    S = S mod d[k]   // next sub problem to be solved

Greedy choice :

  • At each step we make a locally optimal choice of choosing largest denomination dk from set D, such that dk <= S and adding to solution set (without bothering about global optimum).
  • Sum S is then reduced by dk and next locally optimal choice is made for Sum (S – dk) and so on, at each step, until Sum remaining is zero.

Optimal Substructure :

  • Let “P” denote the original problem of making sum S, using minimum number of coins from denomination set d1, d2 … dm.
  • Suppose the Greedy solution starts with denomination dk, dk <S. Then, we have the sub-problem P of making change for (S – dk).
  • Let “O” be an optimal solution to P and let O be an optimal solution to P’. Then, clearly cost(O) = cost(O’) + 1 , (cost = min number coins) indicating that globally optimal solution depends on optimal solution to sub-problems.
  • Thus, the optimal substructure property clearly follows.

Example : Consider U.S coin denominations : 1 cents, 5 cent, 10 cent, 25 cents, 50 cents. Find the minimum number of coins required to make a sum of 30.

Answer : Minimum number of coins needed to make 30 = 2 coins 
(i.e., 25 cents and 5 cents)

But, this heuristic fails …

Now, let’s consider a case when this fails. If the coin denominations supported are only {20,15,5} and we have to find minimum coins required to make a sum of 30

Greedy algorithm described here returns 20,5,5 : 3 coins , however the actual minimum coins needed are 15,15 : 2 coins. So, yes it does not give you the most optimal solution for all cases, but works for most cases , for example – this algorithm outlined works for the United States coin denominations.

We will see how we can handle the failure cases with another way of solving this problem, a little later on this website. So, do check back.

Up next on the blog

In the next week’s blog, we will look at two more popular problems solved using Greedy approach :

  1. Fractional Knapsack Problem
  2. Activity Selection Problem

Until then, keep decoding ! Come back next week.

Caveat :

Although induction [3] helps prove a greedy strategy and this class of algorithms seem to be intuitive, simple to code; they may not be as straight forward to a beginner.

So, don’t beat yourself if you are unable to devise a solution to a popular Greedy problem in say 30 minutes or even a day. It takes time. Keep learning and applying – eventually we will get there. 🙂

Further Reading

Here are some useful links for further reading :

  1. Algorithmic Toolbox Course (Coursera) 
  2. Internet Routing / here (Stanford)
  3. Greedy proof by induction(Stanford)
  4. Greedy algorithms for graphs(MIT)
  5. Solve some greedy problems

As always, do you like what you read ? Please do write to me at decodergirlblog@gmail.com, OR –


Did you know?

More coffee you donate, higher is your coder karma! Oh! there shall also be more blogs published here. Thank you for being awesome! 😀



Data structures Series  – Blog 01

Hi Guys! It’s September and as promised in my last post, a data structure blog is ready and waiting to be read.*** Drum-rolls please ***  Here’s my first blog in the Data Structure series. 😉

Data Structures

Data structures refer to data representation or data organization for holding program data in the computer memory aiding with the processing or operations to be performed on the data.


Arrays are basic data structures in which :

a. All values held by a given array are of the same type – homogeneous

b. Values are allocated contiguous locations in computer memory

Arrays in Memory

I believe it may be a good idea for a programmer to know about behind the scenes of the code written, for efficient programming – that is how code interacts with Memory.  This block is mainly intended at visualizing how arrays are stored in computer memory.

Declaration and usage of arrays need us to know the element_type , name_for_array and array_size :

element_type indicates the data_type (such as int , char, float etc) of the element_value that shall be stored in a given array.

name_of_array – serves as constant pointer to first element of the array when we need to look up or change element_values in the array.

array_size shall indicate the number of elements held by the given array.

Fig 1 shows the side by side view of memory representation of two arrays A and T representing a 1-D and a multidimensional array respectively.

Real world data representation shows how we visualize the data that is to be stored. Declaration shows the code used to declare an array to hold this data and “corresponding memory layout” shows how it is actually stored in computer memory.  In case of 1-D arrays , this is pretty straight forward, how you see is also how  the computer represents data. However, in case of multi-dimensional arrays on the right side , note that how we see data and how it is stored varies. Data is stored row wise , each row  translated to a 1-D representation starting with 1-D array for elements of row 0 {10,20,30}, followed by row 1 {40,50,60} and so on.

For multi-dimensional arrays can be stored as row-major (as depicted) or column major. In case of column major, each column shall be represented like a 1-D array at a time, instead of row.

Fig 1. Memory Layout of arrays

Now that we have an idea of how arrays are represented in the memory, we can take a look at operations on arrays next.

Array operations

1-D Arrays

  • Various supported declarations and initialization of arrays in C/C++ is as shown below.
/* Global arrays that are initialized without size being specified */ 
int c[]={2,3,4};

/* Global arrays without explicit initialization are 
   set to zero by default */
int d[10];

//local declarations of arrays
int main(){
 /* Array size variables must be declared as constants */ 
    const int a_size = 5, b_size = 3, c_size= 3;

 /* array 'a' is initialized with element values */ 
    int a[a_size] = {3,4,5,1,6}; 

 /* array 'b' is initialized without any values using {}   
    all elements shall be by default initialized to zero*/ 
    int b[b_size]={};
  • Array elements are referenced by an index. The value of index ranges from 0 to n-1 for an array of size ‘n’. Highlighted in red shows how an integer array can be passed to a function either as an integer pointer or array with dimension or no dimension specified as shown below.
/* array as function param can be passed as a pointer */
void print_arr(int* arr, int n){

/* Given array size, we can look up array elements using index ,    
in this case i , that takes values from 0 to n-1*/ 
for (int i=0; i<n;i++){

/* another way to pass array to functions 
   using dimension-less array representation */
void print_arr(int arr[], int n){

/* array can also be passed as a sized array */
void print_arr(int arr[5], int n){
// Note: in case you pass some other dimension array, 
   no bound check is performed. So, no error reported */
// check the section below under "Fact, Did you know" :)

Multi-dimensional Arrays

/* declaring multi-dimensional array of size 2x2 */
/* each row is like a 1-D array, here 2 rows are 
   declared one after the other*/
int arr[2][2]={{3,4}{5,6}};

Assigning values to various indices and look up of element value at a given index is O(1) operation.

However, if you need to rearrange or move around data, it can get expensive. For example, as shown in Fig 2, if we want to move the last element to start of the array of size ‘n’, we will need n-1 copy operations to move the elements downwards and then place the element to the beginning of the array. We can clearly see this can get expensive when the number of elements get higher and if this operation is to be performed frequently.

Also, in case of arrays, another problem is static allocation. If we do not have an idea of  how much space is to be allocated, we may end up allocating more than required and wasting space or less than required – in which case we would need reallocation.

There is another data structure that takes care of these problems – namely “linked lists” (coming up next week ! 🙂 )

Fig 2. operations on arrays to rearrange data

Relation of arrays and pointers

If you are working on cpp or Java, worry not my friend ! 😉 it’s all taken care of, for you. While C programmers, take some time to wrap your head around this. It is simple once you understand ! 🙂

// quick pointer refresher 
For a given pointer : int* ptr ;
ptr is a pointer to an integer data

ptr holds the address 
*ptr gives the value at the address held by ptr // de-reference

// for pointer to pointer case 
int** p_to_p;

*p_to_p holds the address
*(*p_to_p) gives value at address // de-reference

Let’s consider a 2-D array :

int a[m][n];
//2D array is an array of 1-D arrays per row
// 1D array name is nothing but a pointer to an integer
//2D array name is hence a pointer to pointer to an integer
  • A[0] is address of row 0
  • Value of element in column 0 of row 0, A[0][0], can be accessed using *(A[0]+0) , ie., A[0]
  • element 1 of row 0, A[0][1] is accessed using *(A[0]+1)
  • Similarly, A[1] is address of row 1
  • Value of element 0 of row 1, A[1][0], can be accessed using *(A[1]+0) , ie., A[1]
  • value of element 1 of row 1, A[1][1], can be accessed using *(A[1]+1)
*/ So, value of c'th element on r'th row , value can be 
   looked up using pointer as below.*/
   *(A[r]+c);    ... (i) 

/* Further, element A[r] can be indexed using an offset from A[0] as A[0]+r, 
   so A[r] value can be accessed using *(A+r).
   substituting in (i), we can look up value A[r][c] using*/
    *(*(A+r)+c);   ... (ii)

Summarizing above :

  • De-reference of A  (ie., *A) gives address of first element of ROW 0 or A[0]. A[0] is a pointer to an integer
  • De-reference of A[0]  gives first entry of A or A[0][0]. ie., **A = A[0][0]
  • In general , we can look up value at column ‘c’, of row ‘r’ using  *(*(A+r)+c))

Do read up further about array operation using pointers if you are interested.

Static and Dynamic arrays

Arrays can also be further split into two types : fixed size arrays  and dynamic arrays that can be resized. In Cpp we can use vectors in case we need dynamic arrays.


If you are designing something and you have a checklist that has requirements below :

  • Efficient element look up
  • Don’t need to often  rearrange values

Arrays would be a good data structure to use, for they support O(1) look-ups.

One quick use case I can think of is to hold look up tables of fixed size which are containers for some constants to be used in computations – arrays are your BAE ! 😀

Also, check adjacency matrix required here.

Fact , Did you know?

When you access an array element of invalid index, C/C++ compilers don’t complaint. The behavior is “undefined”. They may look up some random location, if the address is in valid data region of the memory and return some junk value.  Oh well ! Talk about silly coding mistakes, undefined compiler behaviors – resulting in hard bugs to debug in production, sigh!

C++ supports std::vector which is a template class. It supports dynamic resizing of arrays. This class supports at() member function for an element lookup, which shall perform guaranteed bound check. But, it also supports operator [] to lookup elements, that is retained same as in C arrays and performs NO BOUND check.

/* declare a vector type and add only two elements */
std::vector<int> arr;

/* Try to access out of bound index 
   only valid access indices are 0 and 1 */
std::cout<<arr.at(2)<<std::endl; // error thrown
std::cout<<arr[2]<<std::endl; // NO ERROR

Related Reading

I am adding some links to my previous blogs as well as other material below for further reading.  Do check them out 🙂

  1. Interaction of program with memory
  2. Data structures for graph
  3. Char arrays
  4. Arraylists in Java 
  5. Solve programming problems related to Arrays

Until next time – keep decoding !

If this was helpful or you have some feedback, do write to me at decodergirlblog@gmail.com 🙂 OR :

Did you know?

More coffee you donate, higher is your coder karma! Oh! there shall also be more blogs published here. Thank you for being awesome! 😀


Char array versus string

Today’s tidbit is a very “basic” one. It’s about the difference between char array and string in C plus plus. If you have started with a low level language, this is a real deal ! Read on, to understand what this means. In short – the question we will try and answer today is : “Is there any difference between the two options below :”

string input= "Geekgirl"; //input is a string variable
char input[]="Geekgirl"; // input is a char array

Char Arrays

As suggested by the name char arrays are an array of “characters”. An example is as shown below. These are by default supported in C and C++. Strings are represented or implemented as char arrays that are null terminated in C language. Char arrays are hence also called the “c-strings“.

Fig 1. Char array with null terminated character  ‘\0’ (c-string)
// Usage of char arrays
//A. Declare char array
char input_text[10]; 
//Assign value via command line input

//B. Declare char array and assign value
char input_text[]={'G','e','e','k','\0'}; 

//C. Declare and assign value using double quote enclosed literal constants
//Literal constants are by automatically null terminated (String literals)
char input_text[]="Geek";

//D. using char*
char* input_text_ptr = "Geek";

Since above are char array declarations, all rules of a regular array apply to them. They cannot be assigned a value once they have been declared.

//Once input_text is declared and assigned a value as in B and C options 
above, the following assignment operations are invalid:
input_text ="Geek"; // not allowed
input_text[]="Geek"; //not allowed
input_text ={'G','i','r','l','\0'}; // not allowed

//But, they can be edited using indexing into individual elements as below:
//valid assignments listed below.

// But, when declared using char* (pointer to character) as in case D above: :
*(input_text_ptr+0) ='D'; // not allowed. 
// But, if we allocate memory 
// and then assign a value, then we can assign char value as above:
char *input_text_ptr= (char *)malloc(sizeof(char)*size); /*Stored in heap segment*/
 *(input_text_ptr+0) = 'D'; //valid

As in case of normal arrays, the name input_text is nothing but a pointer to the start of char array: address of input_text[0].

char* temp = input_text; // using char pointer 
char temp[] = input_text; // invalid operation

//below is valid 
char temp[5]; 


So, what is a string in C++ then? Well, string class in C++ provides support for common string related operations such as compare, search, swap, copy and also offer dynamic sizing  , exception handling when there is access out of bounds.

Char arrays have a “fixed” size specified either implicitly or explicitly during declaration. But, strings do not require it. This is because strings are dynamically sized during runtime, where as char arrays are sized during compile time.

string mystr = "geekgirl"; // has no size or [] in the declaration
// we are creating a string object named mystr above
char mystr[] = "geekgirl"; // c style string declaration

Memory allocation

The memory for char arrays is allocated on stack unless malloc() is used to create a char array. In case malloc() is used, memory is allocated on the Heap and the user is responsible for releasing the memory after use. Since, cstrings are created with definite size in mind, it is harder to resize them, user has to be careful about invalid access. Think of String object as a utility in C++ that manages the dynamic allocations for us and provides an easier interface to perform operations on char arrays.

Passing to a function

String passing to a function is similar to how char arrays are passed, as in the example below.

char str[]="geek";

string str1="geek";

// passing char array to a function
void displayCharArray(char s[])
 cout << "Char array contains" << s <<;

// passing string to a function
void displayString(string s)
 cout << "String contains  " << s << endl;

Which one should one use?

In case you are implementing code to be compatible with legacy C code, then you may be better off using char arrays. String class objects are easy to use, provide features like bound check exceptions and manage the allocations – sizing and dynamic resizing, managing the free operations on the memory allocated for the user string. So, if you are using C++ and no legacy code to deal with, you could go for string objects.

As always, think of your use case and decide which one is suitable for your case. That’s all in this tidbit. There is a tiny mention about using char* in this blog (pointer to char), but will cover it in another tidbit. Until then, keep decoding!~