0% found this document useful (0 votes)
12 views24 pages

C Language Topics for Interview

This document serves as a comprehensive guide for C programming interview preparation, detailing the stages of C program compilation, storage classes, memory corruption issues, dynamic memory usage, and various data structures such as stacks and queues. It also covers advanced topics like inline functions, bitwise operations, and the use of structures, unions, and enumerations. Additionally, it includes practical coding exercises and implementation strategies for common algorithms and functions.

Uploaded by

Shivani garg
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
12 views24 pages

C Language Topics for Interview

This document serves as a comprehensive guide for C programming interview preparation, detailing the stages of C program compilation, storage classes, memory corruption issues, dynamic memory usage, and various data structures such as stacks and queues. It also covers advanced topics like inline functions, bitwise operations, and the use of structures, unions, and enumerations. Additionally, it includes practical coding exercises and implementation strategies for common algorithms and functions.

Uploaded by

Shivani garg
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
You are on page 1/ 24

C - PROGRAMMING

INTERVIEW PREPARATION GUIDE


C PROGRAM COMPILATION STAGES:

1. Pre-Processing stage ( .c  .i )
• Header files expanded
• Macros will get replaced
• Specific code will be included based on conditional macro’s
• Comments will be removed

2. Compilation/Translation stage ( .i  .s )
• Syntax and semantics analyses will be done
• Reports errors if any which will help programmer to fix it.
• Generates assembly code

3. Assembly stage ( .s  .o )
• Assembly code converted into machine readable code.

4. Linking stage ( .o  .exe/.out )


• Internal/External functions linkage will be done
• Generates the architecture dependent executable file to execute
STORAGE CLASSES C Memory
S.No Storage Storage Initial Scope of the Life of the Layout
. Class value variable variable
Name STACK
1 auto Stack Garbag Within Within Mappe
Section e function / function / d to
Block Block RAM HEAP
2 register CPU Garbag Within Within .data
Registers / e function / function /
Stack Block Block DATA .bss

Section Mappe
.rodata

3 local static Data 0 Within Till program d to


Section function / exists Flash CODE
Block
global Data 0 Within a file Till program
static Section only exists
 Cannot declare static member as a structure
4 extern Data 0 Throughout a Till program
variable Section program Initialized
exists Uninitialized
 Structure variables can be declared as static variables variables
 Functions can be declared as static Strings will be stored here.
 Its possible to declare const variable within a Global const and static const
behaviour is undefined
structure but needs to be initialized as soon as
Modifiers
• short
• long
• unsigned
• signed

Qualifiers
• const
 Constant variable gets stored in different sections of the memory based on how you are
declaring:
 auto const variable will be stored in stack section and using pointer you can modify it.
 Uninitialized global const variables will be stored in .bss section and using pointers you
can modify it.
 global const and Static const variable behaviour is undefined.
 Strings gets stored in RODATA section.
• volatile
 Assume that there is a TEMP sensor which will sense the temperature data and its driver can
read the data and write into some shared memory and there is an application which will
display the whether condition on the screen by reading the data from that same shared
memory.
 These are 2 different programs and in application your program only reads this memory value
so compiler will optimizes it to get better performance but we need always updated value
from memory so to avoid optimization we will use volatile.
Memory Corruptions
Global Memory Corruption
A global data can be corrupted due to Array index overflow from the previous data or
Array index underflow from the next data.
Uninitialized pointers can also cause this issue.

To debug:
• Put a write break point and debug it.
• If no debug setup available then browse through the code to find if any previous or next data
causing this.
• Or else looking at the patterns what has written in your global location can also give you an idea.

Heap Memory Corruption


Corruption on the heap can be very hard to detect. A heap corruption could lead to a crash
in heap management primitives that are invoked by memory management functions like malloc
and free.

Reasons:
• If a crash is observed in memory management primitives of the operating system, heap
corruption is a possibility. It has been observed that memory buffer corruption sometimes leads to
corruption of OS buffer linked list, causing crashes on OS code.
• If a memory corruption is observed in an allocated buffer, check the buffers in the vicinity of this
buffer to look for source of corruption.
• Corruption of buffers close to heap boundary might be due to stack overflow or stack overwrite
Stack Memory Corruption
Stack corruption is a phenomenon in which some memory locations at stack are accessed
unintentionally due to wrong coding leading to change in values at those memory locations. Since
the data corruption happens on stack memory locations, hence the term Stack Corruption.

Reasons :
• Due to bad written code, all the stack memory gets eaten up (like infinite function call)
• Accessing array out of bounds.
• An undefined/freed pointer pointing or storing a garbage stack address.
• When due to some reason, the return address of a function call gets corrupted.

Dynamic Memory Use Cases

In most of the compilers, some extra bytes will be allocated for any dynamic memory allocations for
book-keeping information like SIZE of the allocation, etc.

Extra bytes for Actual requested SIZE


header
1) malloc( 0 ) returns atleast small chunk of memory (16B for example) which needs to be freed
later.
2) malloc(-ve value) will try to allocate huge block and fails if memory is not available because
malloc takes unsigned value and –ve value rounded to huge positive value.
3) free(ptr) looks for small chunk to find out SIZE of the allocated memory and frees it.
4) realloc (ptr, 0) also works as malloc (0).
Note:
1) Size of a pointer is equal to size of ARCH-bit/8.
For example, for 64-bit machine, 64/8 = 8 bytes and for 32-bit, 32/8 = 4 bytes.

2) How to know the STACK is growing up/down?

main()
{
char x[2];

if((&x[1] - &x[0]) < 0 )


printf(“DOWN STACK\n”);
else
printf(“UP STACK\n”);
}

3) sizeof operator implementation:

#define SIZEOF(X) ({__typeof__(X) Y; ((char*)(&Y+1) - (char*)(&Y));})

4) Container of a structure variable:

#define CONTAINER_OF(ptr, type, member) ((type*) ((char*)ptr - (char*)&(((type*)0)-


>member)))
STRUCTURE:
Structure is an user defined data type which will keeps all the related data members of an entity into a single
container.

For example PROCESS is an entity and its structure can be defined as below:

struct process
{ x0 x0
without struct padding With struct padding
uint32_t process_no; x1 x1
uint32_t pp_no; x2 x2
struct process *child_processes;
… x3 x3
}; y0 y0
STRUCTURE PADDING: z0 0
ARM
By default any processor access the memory word by word. z1 0
For example, on 32-bit ARM processor memory will be accessed 4 bytes (1 word) at a time.
z2 0
struct xyz z3 z0
{
int x; z1
char y; z2
float z;
}; z3
Bitfields:
Bitfields are used in the scenario where we use flag in our code and for that no need to waste lot of
memory instead we can use few bits from the byte or word. We can save the memory with bitfields.
Declaration:
struct bitfield
{
char flag1:1;
char flag2:4;
}b;

To access them, we need to do bit-wise operations:


b.flag1 = 1;
b.flag2 = 0xC;

printf(“ %d %d \n”, (b.flag1 & 1), (b.flag2 & 0xF);

Typedef:
Using typedef we can give alias name to the existing/user defined data type to increase the
readability and simplicity.
typedef struct linkedlist list;
list *Node;
Typedef of function pointer:
typedef void (*ptr) (int, float);
UNIONS:
Unions will be used in the scenario where there are multiple members needed in the application for
a same entity but all the members are mutual exclusion.
All the members share the common memory area where highest no. of bytes will be allocated for
that union.
Ex:
union xyz
{
x0/y0 y1 y2 y3
char x;
int y;
}u;
Enumerations or enums:
Enums are mainly used to assign names to integral constants, the names make a program easy to
read and maintain.
enum week
{
Mon,
Tue,
Wed
..
}day;

day = Wed;  it means day = 2 as enums value starts from 0, 1, 2, etc…


Arrays and Pointers
• Pointers can hold the address of another variable or another pointer.
• On pointer, we can do addition/subtraction/increment/decrement operations but not any other. It is
meaningful to do these operations only on continuous memory locations.
• With pointers we can access 1-D array memories but cannot access 2-D array memory by using pointer to
pointer.
• Array increment/decrement cannot be performed as array is constant pointer.

Combination of pointer and const :

char *p;  p is a pointer which will point to char variable


const char *p;  p is a pointer which will point to const char variable
char *const p;  p is a constant pointer which will point to char variable
const char *const p;  p is a constant pointer which will point to const char variable

int *p; int **q; int ***ptr;

Arrays of Pointers:
int *p[5] = { &x, &y, arr, &m, &n}

Pointer to Array:
int (*q)[5] = arr;  Where arr is int arr[5]; array size must be 5 only.
int a[2][5] = { {1, 2, 3, 4, 5}, {11, 22, 33, 44, 55}};

int *ptr = (int*)a;

int (*ptr2)[5] = a;

printf(“%d %d\n”, *ptr, *(ptr + 1)); 1 2

printf(“%d %d\n”, **ptr2, **(ptr2 + 1));  1 11

Below method will not give correct result using pointer to pointer

int **ptr1 = (int **)arr;

printf(“%d %d\n”, *ptr1, *(ptr1 + 1)); 1 3 If you assign string to


This is stored in RODATA char array then that
===== section whole string will be
char *p = “Hello”; copied into the array.
This is stored in .data section So, you can modify
int main( void ) that string.
This is stored in RODATA
{ section Ex: char
char *q = “World”; str[10]=“Sat”;
This is stored in stack section str[0] = ‘M’;
}
Operations performed during function call:
• Push calling functions register values into the stack frame
• Update the link register(LR) with the return address
• Update the general purpose registers(R0-R4 based on compiler and system) with the actual values
of the called functions.
• Make a function call and create its stack frame.
• Push formal arguments to the stack frame from registers.
• Do the operations and return back using the address stored in LR.
• Reload the registers from stack frame and do the normal operations.

INLINE function:
Inline function must be declared with static keyword.
static inline func(void)
{

}
Recursive function
A function which calls itself is called recursive function.
Diff b/w macro’s and inline functions:
• Macro’s will be expanded in preprocessing stage and inline will be replaced in a code during
compilation stage.
• Macros will not do any type checking but inline functions will do it.
Which one is good normal function or inline function:
• Its based on the requirement.
• Normal function always creates stack frame and destroys it once done and lot of other operation
takes place which will consume lot of CPU cycles. And it won’t give better performance compared
to inline functions.
• But, with normal function call memory footprint will be less.
• Where as inline function will be expanded during compilation itself so memory footprint will be
more.
• But, it gives better performance as there is no CPU cycles wasted during execution.

Note: Formal parameter of the functions can be declared with register storage class only.
System is Little endian or big
endian
Bit-wise operations:
Set bit : n = n | ( 1 << bit )
int x = 0x1;
Reset bit: n = n & ~ ( 1 << bit )
if((char)x & 1)
printf("Little Endian\n");
Toggle bit: n = n ^ ( 1 << bit )
else
printf("Big Endian\n");
Check if kth bit is set or reset: if( n & (1 << k) != 0) then set else reset

Nibble swap in a byte: n = (n & 0xF0 >> 4) | (n & 0x0F << 4)

Little to big endian or vice-versa(32-bits):


• Given no. is power of 2 or not : if(n & (n – 1) == 0) then power of 2
• Given no. is power of 4 or not: (1) it should be power of 2 .
(2) c = 0; while((n & 1) == 0) { c++; n >>=
1;} if(c && c%2 == 0) then power of 4
• Given no. is even or odd: if(n&1 == 0) then EVEN else ODD
• Reverse the bits of a given number: for(i = 0, j = 31; i<j; i++, j--) { if( !(n & ( 1 << i)) != !(n &
(1 << j))) then toggle both bits
• Check if the given no. is –ve : if((n >> 31) & 1 ) == 1) then –ve
• Add 1 to the given no. : x = -(~x);
• Swap 2 no. using bit-wise: x = x^y; y = x^y; x = x^y;
• Find the no.of sets bits
• Find the no.of reset bits
Explore on below questions:
• Difference b/w strcpy/memcpy/memmove
• Swap 2 variables without 3rd variable (x = x+y; y= x-y; x = x-y;)
• Implement all the string functions like
atoi/strcat/strchr/strcmp/strcpy/strlen/strncat/strncmp/strncpy/strlcpy/strlcat/
strrchr/strstr
• Implement sorting algorithms ( Bubble sort/Selection sort/Insertion sort/Quick sort/Merge sort)
• Implement searching algorithms ( Linear/Binary search)
• Implement binary to decimal
• Implement decimal to binary (-ve also)
• Convert little-endian to big-endian by using UNION
DATA STRUCTURE
STACKS
A stack is a linear data structure, collection of items of the same type.
Stack follows the Last In First Out (LIFO) fashion wherein the last element entered is the first one to be popped
out.

The following are the basic operations served by the Stacks.


• IsEmpty: Checks whether the stack is empty.
• IsFull: Checks whether the stack is full.
• Push: This function adds an element to the top of the Stack.
Algo:
PUSH
begin
check IsFull
Cannot push elements & return
push element into the top of the stack
end
• Pop: This function removes the topmost element from the stack.
Algo:
POP
begin
check IsEmpty
Cannot pop elements from the stack & return
pop the elements from the top of the stack
end
• Pip: Displays the topmost element of the stack.
Algo:
PIP
QUEUE
A Queue is a linear data structure which follows a particular order in which the operations are
performed.
The order is First In First Out (FIFO).

The following are the basic operations served by the Stacks.


• Insert: This function adds elements into the queue.
Algo:
INSERT
begin
check if Queue is full
Cannot insert the elements & return
insert the elements into the queue at rear end
end

• Delete: This function deletes elements from the queue.


Algo:
DELETE
begin
check if Queue is empty
Cannot delete elements from the queue
Remove element from the queue at front end
end
Single Linked List:
A linked list is a linear data structure, in which the elements are not stored at contiguous memory
locations.

Declaration of single linked list:


struct list
{
NODE Data Next
int data;
Hea struct list *next;
};d
Data Next Data Next Data Next Data Next Data Next

Struct declaration for generic


linked list:
struct list
{
• Insert the element at the beginning of the list
void *data;
• Insert the element in the middle of the list
struct list *next;
• Insert the element at the end of the list
}
• Delete the first node from the list
• Delete the middle node from the list
• Delete the last node from the list
Questions on the linked list, stacks and queues:
1. Reverse the single linked list
2. Reverse the double linked list
3. How do you reverse a linked list without using any C pointers?
4. Given only a pointer to a node to be deleted in a singly linked list, how do you delete
it?
5. How would you detect a loop in a linked list?
6. How do you find the middle of a linked list?
7. Write a C program to return the nth node from the end of a linked list
8. Implement stack using linked list
9. Implement queue using linked list
10. Implement queue using stack
11.Find the common node between the 2 lists.
12. Implement Queue by using Stack.
13.Addition of numbers from 2 lists and strore the results in new list.

File Operations:
14. Creating a file, reading, writing a file
15. Reading files from directory recursively and searching a particular string in it
16. Print the count of that string present in each file and total count in the directory.
Libraries
• A library in C is a collection of
object files exposed for use and
build other programs, so instead
of re-write sections of code we
bring back information that is
already existing through a library.
• There are 2 different kinds of
libraries:
• Static libraries that allows us to link
to the program during compilation
• Dynamic or shared libraries those
are preferable use when you run a
lot of programs at the same time
who are using the same library.
How to create static library?
$gcc –c file1.c file2.c
The flag '-c' we use it to stop the compilation process till compilation and assembly
stage but not done the linker phase so in this case we created a new file call
'file1.o‘ and 'file2.o‘.

$ar rc lib_file.a file1.o file2.o


• It is recommended to use rc flag because 'c' allow to create the libraries if it
doesn’t already exist and 'r' to replace the older object for the new ones.

$gcc test.c –L. -l_file -o test


$gcc test.c –L/mnt/d/Code_WS/static_libs -l_file -o test
• With this code we compile the c program and creating and execute name newfile,
the meaning, gcc: Compile the file.c, -L: Search the direction of the file ‘.’ mean
current directory, -l: To use the library, -o: To create and executable name newfile.

• $./test
Dynamic library?
Dynamic libraries (also called shared libraries) are linked into the program in two stages.
• First, during compile time, the linker verifies that all the symbols required by the program, are either
linked into the program or in one of its dynamic libraries. However, the object files from the dynamic
library are not inserted into the executable file.
• Instead, when the program is started, a program in the system (called a dynamic loader) checks out which
shared libraries were linked with the program, loads them to memory, and attaches them to the copy of
the program in memory.

How to Create Dynamic Libraries?


It will be done in 2 stages:
• Compile for "Position Independent Code" (PIC):
When the object files are generated, we have no idea where in memory they will be inserted in a
program that will use them. Many different programs may use the same library, and each loads it into a
different memory in address. We need that all jump calls and subroutine calls will use relative addresses,
and not absolute addresses. Thus, we need to use a compiler flag that will cause this type of code to be
generated.
• $gcc -fPIC -c file1.c file2.c file3.c
With this command, we will create a list of object files with the names of file1.o, file2.o and file3.o.
• Library File Creation:
Unlike a static library, a shared library is not an archive file. It has a format that is specific to the
architecture for which it is being created. Thus, we need to use the compiler (either the compiler's
driver or its linker) to generate the library and tell it that it should create a shared library, not a final
program file.

$gcc -shared -o lib_name.so file1.o file2.o file3.o

How to use them (Linux only)

$gcc main.c -L. -l_name -o prog

./prog
./test: error while loading shared libraries: lib_dynlib.so: cannot open shared object file: No such file or
directory
$export LD_LIBRARY_PATH=/mnt/d/Code_WS/dynamic_libs

$./prog
THANK YOU…

You might also like