General Computers Architecture and Digital Hardware Questions
Do you know the Harvard and Von Neumann architectures? What is its main difference?
Response: Von Neumann: Same memory for program and data Harvard: Different memory for data and program
Do you know about the RISC and CISC instruction set architectures (ISA)? What are some of their (main) characteristics?
Response: RISC:
- ISA based on the idea that instructions are simple and execute single operations.
- Has single-specific load/stores instructions.
- Easier to implement Pipeline with RISC ISA CISC:
- ISA based on the idea that a single instruction are complex and trigger multiple operations
Do you know about the memory hierarchy? Why do we need it? Why not just use fast memory (cache)?
Response:
It's needed to close the gap between the CPU and memory speed, fast memory (like cache memory) is expensive
What are the most common stages of execution of an instruction in a simple RISC architecture?
Response:
- Fetch: Get instruction from memory (Ins memory)
- Decode: Check instruction and get registers used (Bank of Registers & Control Unit)
- Execute: Actual arithmetic operation (ALU)
- Write back: Write data/result to data memory and/or Registers (DAT Mem & Bank of registers)
In processors context What's Pipeline? And why do we use it? Why not implement as a unicycle?
Response:
- Pipelining: is an implementation technique where multiple instructions are overlapped in execution.
- The computer pipeline is divided in stages and each stage completes a part of an instruction in parallel.
- This implementation increases instruction throughput
- A disadvantage is that Combinational propagation time can cause meta-stability (uncertain values).
What is cache coherence
Response:
Special case of memory coherence which refers to the need of having the data that is shared/replicated among different caches in sync.
Relevant mostly on Multiprocessing systems for example a system with two or more processors where each have an L1 cache but share an L2 cache, if some data that is being used for both is modified by one of them it could result on cache incoherence
Can you draw a CPU architecture diagram or its main blocks?
Response:
This image is an example of a simple RISC architecture as a reference in which the control unit is implicit
In computer architecture what defines whether a system is a 32-bits or 64-bits system?
a) Register Size
b) Memory data buses
c) Memory address buses In general a combination of all these (registers and data bus) but more specifically the address buses
Programing
C General Programing Questions
Which of the following is NOT a reserved word in C?
a) sizeof
b) try
c) signed
d) register
According with the following code, which is the output after its execution?
a) Depends on _________ The Architecture (and other stuff in general) because of the pointer on a 64-bit arch it'll most likely be 40, 8
b) 40, 4
c) 10, 4
d) 10, 10
e) 40, 40
f) 4, 4
g) Compilation error
How many times does the following loop run?for(x=0; x=3; x++)
a) Three times
b) Four Times
c) Never
d) Forever Cause x=3 always evaluates to true this becomes an infinite loop
Which is the output after running the following code?
#include <stdio.h>
int main(int argc, char *argv[]) {
int x;
while(x < 100) {
printf("%d, ",x);
x++;
}
}
a) 0, 1, 2, 3, ... 99,
b) 0, 1, 2, 3, ... 100,
c) Undefined Variable int x was not initialized so we can't know for sure
According with the following code, which is the output after its execution? Semicolon at line 5 is not a typing error, it is part of the C code.
#include <stdio.h>
int main(int argc, char *argv[]) {
int i = 0, x = 0;
for(i=0; i<10; i++);
{
x++;
}
printf("%d\n", x);
}
a) 0
b) 1 Variable int x is not incremeneted by the loop due to the semicolon, int i does increment up to 10
c) 10
d) Compilation Error
Which is the output after running the following code?
#include <stdio.h>
int main(int argc, char *argv[]) {
int i = 0;
while(i++<10);
printf("%d\n", i);
}
a) 9
b) 10
c) 11 Loop breaks when i evaluates to 10 but evaluation is performed before increment
d) Compilation Error
Which is the output after running the following code?
#include <stdio.h>
int main(int argc, char *argv[]) {
int i = 0;
int x = 0;
for(i=0; i<10; i++){
if((i>2) && (i<5))
continue;
if(i==8)
break;
x++;
}
printf("%d, %d\n",x,i);
}
a) 10, 10
b) 10, 8
c) 8, 8
d) 6, 8 Loop runs 8 times but in 2 of those cycles x does not increment due to continue;
What does the following code mean:for(;;);
a) Invalid Code
b) Infinite loop
c) Ignored by the compiler
c) Compiler error
According with the following code?
#include <stdio.h>
int main(int argc, char *argv[]) {
int x = 1;
int* p = &x;
*p = 2;
*p++ = 10;
printf("%d\n",x);
}
15-1. What is the value ofxprinted?
a) 2
b) 3
c) 10 The pointer gets incremented but first 10 is assigned to x
d) 11
15-2. Does the pointerppoints to a variable defined inmain?
b) Yes, points to the variable x
b) No it was pointing to the address of x but the pointer was incremented so now it's not pointing to any variable defined in main ¯\_(ツ)_/¯
What is a macro in C? Can you provide an example?
Response:
- C preprocessor directive used as an abbreviations for longer constructs. E.g. #define SHIFT_L(x) ((x)<<1) , #define DEF_ID int
How would you define a type usingtypedef? What's the difference between defining a type usingtypedefand with a macro, using#define?
Response:
- A macro defines C preprocessor directives which are replaced by the preprocessor and typedefs actually define new custom types. E.g. typedef int TYPE_ID;
Which is the output after executing the following code?
#include <stdio.h>
int main(int argc, char *argv[]) {
int a = 0;
int b = 1;
int c = 2;
c = (a > b) ? a : b++;
printf("%d %d %d\n", a, b, c);
}
a) 0 2 1 Value of b gets evaluated first and assigned to c then b is incremented
b) 2 0 2
c) 0 2 2
d) Invalid code
Assuming a compiler that supports C99 and according with the following code, which is the output after its execution?
b) pt=5 ptz=7 Pointers point to the start of the structs, doesn't matter type of init, in memory values are aligned as struct definition
c) pt=5 ptz=14
d) Invalid Code
e) Compilation Error
What is the difference, related to memory, between a local variable, global variable and a variable allocated with malloc (dynamic)?
Response:
- They are stored in different sections of memory, local variables are stored in the stack and dynamic are stored in the heap,
global variables will be in the data section, in a Linux based if its initialized it will be in the .bss section
Which operator is used to access a member of a structure referenced by a pointer?
a) *
b) ->
c) >>
d) &
Assuming a 32-bit system where int variables are 4-bytes long and not specificing any pragmas, which is the output after the execution of the following code?
#include <stdio.h>
int main(int argc, char *argv[]) {
typedef struct _SMX_ {
int x;
char a;
int y;
char b;
int z;
char c;
} SMX, * P_SMX;
typedef struct _BAS_ {
int x;
int y;
int z;
char a;
char b;
char c;
} BAS, * P_BAS;
printf("%d %d\n", sizeof (SMX), sizeof (BAS));
}
a) 15 15
b) 6 6
c) 24 16 Packing of the struct not explicitly specified, for example you could specify this with #pragma pack(1). Due to not specifying it declaration order will cause more memory to be used.
d) 16 16
e) Compile error, becasue the way sizeof is used on custom types
Which character is used to terminate the strings in C?
a) .
b) \x
c) \END
d) \0
Which is the output after executing the following code?
a) 7 The pointer arithmetics is equivalent to arr[2] + 1 = 6 + 1 = 7
b) 8
c) 6
d) 5
According with the following code, which is the output after its execution?
#include <stdio.h>
void f1(int *, int);
void f2(int *, int);
void(*p[2]) (int*, int);
int main(int argc, char *argv[]) {
int a = 3;
int b = 5;
p[0] = f1;
p[1] = f2;
p[0](&a , b);
printf(" %d %d " , a ,b);
p[1](&b , a);
printf(" %d %d " , a ,b);
}
void f1(int* p , int q){
int tmp;
tmp = *p;
*p = q;
q = tmp;
}
void f2(int* p , int q){
int tmp;
tmp = *p;
*p = q;
q = tmp;
}
a) 5 3 5 3
b) 3 5 3 5
c) 5 5 5 5 The first call changes the value of a to 5 the following swap doesn't affect much since both variables already have 5 stored
d) 3 3 3 3
Which is the output of the following program?
#include <stdio.h>
int counter (int i) {
static int count = 0;
count = count + i;
return (count);
}
int main(int argc, char *argv[]) {
int i , j;
for (i = 0; i <= 5; i++)
j = counter(i);
printf("%d\n", j);
}
a) 10
b) 15
c) 5
d) 4
What's the return value of the following function calling it withv = 13?
#include <stdio.h>
int fn(int v) {
if (v == 1 || v == 0) return 1;
if (v%2 == 0) return fn(v/2) + 2;
else return fn(v-1) + 3;
}
a) 13
b) 10
c) 16
d) Invalid Code
e) Runtime error
Assuming a 32-bit architecture which of the following data structures uses less memory?
a) struct mystruct { int i; float f; int b; }; => 12 bytes
b) union myunion { unsigned int ui; float f; char c[4]; }; => 4 bytes
c) char array[10]; => 10 bytes
Which code represents a call to a function which is located at the address 0 of the system memory?
a) (*(void(*)(0)))();
b) (*(void(*)()0))();
c) (*(void(*)())0)(); Cast 0 to type void(*)() (pointer to a function that receives no parameter and returns nothing) then dereference it
According with the following code, which is the output after its execution?
#include <stdio.h>
int main(int argc, char *argv[]) {
int x[] = {3, 5, 2, 4};
int y = 0;
int i = 1;
y = i + x[i++];
x[i] = y + i++;
printf("%d ",y);
for(i=0; i<4; i++) { printf("%d ",x[i]); }
}
a) Undefined
b) 7 3 5 9 4
c) 6 3 5 8 4
d) 7 3 5 2 9 They key statements get evaluated to y = 2 + x[1] = 2 + 5 = 7 and x[3] = 7 + 2 = 9
What does the following declaration mean in C?int func();
a) func is a function with any number and type of arguments. Omitting void in the declaration makes it possible that in the implementation parameters can be added of any type
b) func is a function with no arguments.
c) func would receive a single int argument as default.
d) Invalid function declaration
What does thevolatilemodifer means?
Response:
- Means that a variable can change out of the normal flow of the program
- It means a variable can be modified in the program by something such as an interrupt handler, the hardware, or a concurrently executing thread.
What is the build process of a C program?
Response:
- Preprocessor >> compilation >> Linking. Preprocessor step is omitted in the following image
C Coding Problems
Write a function in C that calculates the factorial of a given number. Function takes as input an integer and returns the factorial of that number
#include <stdio.h>
int factorial(int);
int main(int argc, char *argv[]) {
printf("Factorial of 5 = %d\n", factorial(5));
return 0;
}
// F(n) = nF(n-1) ; if n == 0 then return 1
int factorial(int n) {
if (n == 0) return 1;
else return (nfactorial(n-1));
}
Write a function in C that calculates the Fibonacci series ([0, 1, 1, 2, 3, 5, 8, 13, 21]). Function takes as input an integer
representing the position of the Fibonacci series and returns the corresponding value of Fibonacci Serie for that posistion
#include <stdio.h>
int fibonacci(int);
int main(int argc, char *argv[]) {
printf("Fibb of 2 = %d\n", fibonacci(2));
return 0;
}
// F(n) = F(n-1) + F(n-2) , F0 = 0, F1 = 1 ; if n<2 then return n
int fibonacci(int n) {
if (n < 2) return n;
else return (fibonacci(n-1) + fibonacci(n-2));
}
Write a function in C that performs bubble sort search in an array. Function takes as input the array and size and sorts it
// Most common implementation with two loops
// Most inner loops through array and swaps elements
// Second loop repeats until no more swaps are needed
void bubble_sort(int* array, int len) {
int temp = 0;
char swap = 0;
do {
swap = 0;
for(int i = 1; i < len; i++) {
if(array[i-1] > array[i]) {
temp = array[i];
array[i] = array[i-1];
array[i-1] = temp;
swap = 1;
}
}
} while(swap != 0);
}
Write a function in C that inverts the values of an array. Function takes as input an array and modifies it accordingly
void invert_array(int* array, int len) {
int temp = 0;
for(int i = 0; i < len/2; i++) {
temp = array[len-i-1];
array[len-i-1] = array[i];
array[i] = temp;
}
}
Write a function in C that counts the number of bits set to 1 in a variable. Function takes as input the variable and returns a number representing the number of bits set
#include <stdio.h>
unsigned int count_bits(unsigned char);
int main(int argc, char *argv[]) {
unsigned char test_var = 0xA5;
printf("# bits set in 0x%x -> %d \n", test_var, count_bits(test_var));
return 0;
}
unsigned int count_bits(unsigned char var) {
unsigned int count = 0;
while(var > 0) {
if(var & 0x01) {
count++;
}
var >>= 1;
}
return count;
}
Write a function to invert the order of the bits in a variable. Function must receive a pointer to the variable and the change must occur in the space of memory pointed by that argument.
#include <stdio.h>
void invert_bits(unsigned char*);
int main(int argc, char *argv[]) {
unsigned char test_var = 0xB3;
printf("Before invert: 0x%x ", test_var);
invert_bits(&test_var);
//// or
// bitfields_invert_bits(&test_var);
printf("After invert bits: 0x%x \n", test_var);
return 0;
}
// Solution 1: Invert bits using shift and bit operations
void invert_bits(unsigned char* in) {
char temp = *in;
int size = sizeof(temp)*8;
int lbit, rbit;
for(int i = 0; i < size/2; i++) {
lbit = (1 << (size-i-1)) & temp;
rbit = (1 << i) & temp;
if(lbit) {
temp |= (1 << i);
} else {
temp &= ~(1 << i);
}
if(rbit) {
temp |= (1 << (size-i-1));
} else {
temp &= ~(1 << (size-i-1));
}
}
*in = temp;
}
// #pragma pack(1) is there to make sure the bit fields pack down to a byte,
// with no extra "padding" bits in there. (since we are relying on compiler
// implementation details when using bitfields.)
#pragma pack(1)
typedef struct bitfields_byte {
unsigned int b0: 1; // bit0
unsigned int b1: 1; // bit1
unsigned int b2: 1; // bit2
unsigned int b3: 1; // bit3
unsigned int b4: 1; // bit4
unsigned int b5: 1; // bit5
unsigned int b6: 1; // bit6
unsigned int b7: 1; // bit7
} bitfields_byte_t;
typedef union bitfields_union {
bitfields_byte_t mybyte;
unsigned char b;
} bitfields_union_t;
// Solution 2: Invert bits using bitfields and a union
void bitfields_invert_bits(unsigned char* varp) {
bitfields_union_t var;
unsigned char temp = 0;
var.b = *varp;
// Invert bit7 with bit0
temp = var.mybyte.b7;
var.mybyte.b7 = var.mybyte.b0;
var.mybyte.b0 = temp;
// Invert bit6 with bit1
temp = var.mybyte.b6;
var.mybyte.b6 = var.mybyte.b1;
var.mybyte.b1 = temp;
// Invert bit5 with bit2
temp = var.mybyte.b5;
var.mybyte.b5 = var.mybyte.b2;
var.mybyte.b2 = temp;
// Invert bit4 with bit3
temp = var.mybyte.b4;
var.mybyte.b4 = var.mybyte.b3;
var.mybyte.b3 = temp;
*varp = var.b ;
}
Using C syntax declare a function that returns the sum of two integers, then declare a function pointer that points
to that function and call the function using the pointer to the function. Can we call the function using the following syntax (*pFunc)(1, 2); ?
#include <stdio.h>
int sum_integers(int, int);
// Option A: Declare as global variable and init function pointer in declaration
int (*pFunc)(int, int) = sum_integers;
// Option B: Define a function pointer type
typedef int (* my_func_ptr_t)(int, int);
int main(int argc, char *argv[]) {
// Declare function pointer using defined type and init the pointer
my_func_ptr_t pFuncLocal;
pFuncLocal = sum_integers;
// All calls are equivalent and valid
printf("Call function explicitly: %d\n", sum_integers(1 , 2));
printf("Call using function pointer pFunc: %d\n", pFunc(1 , 2));
printf("Call using function pointer pFuncLocal: %d\n", pFuncLocal(1 , 2));
printf("Call dereferencing pFunc: %d\n", (*pFunc)(1 , 2));
printf("Call dereferencing pFuncLocal: %d\n", (*pFuncLocal)(1 , 2));
printf("Call casting function address without &: %d\n" , ((int (*)(int, int)) (sum_integers))(1, 2));
printf("Call casting function address with &: %d\n", ((int (*)(int, int)) (&sum_integers))(1, 2));
printf("Call casting function address and dereferencing without &: %d\n", (*((int (*)(int, int)) (sum_integers)))(1, 2));
printf("Call casting function address and dereferencing with &: %d\n", (*((int (*)(int, int)) (&sum_integers)))(1, 2));
printf("Call casting function address using defined type without &: %d\n" , ((my_func_ptr_t) (sum_integers))(1, 2));
printf("Call casting function address using defined type with &: %d\n", ((my_func_ptr_t) (&sum_integers))(1, 2));
printf("Call casting function address using defined type and dereferencing without &: %d\n", (*((my_func_ptr_t) (sum_integers)))(1, 2));
printf("Call casting function address using defined type and dereferencing with &: %d\n", (*((my_func_ptr_t) (&sum_integers)))(1, 2));
// All these expressions return the address of the function
printf("Address stored in pFuncLocal: 0x%llx\n", (long long int) (pFuncLocal));
printf("Address stored in pFunc: 0x%llx\n", (long long int) (pFunc));
printf("Get address of sum_integers function without using &: 0x%llx\n", (long long int) (sum_integers));
printf("Get address of sum_integers function using & : 0x%llx\n", (long long int) (&sum_integers));
return 0;
}
int sum_integers(int a, int b) {
return (a + b);
}
Write a function that detects endianness. For example it returns 1 if running on a Little-Endian architecture and 0 when running on a Big-Endian one.
int main(int argc, char *argv[]) {
printf("is little endian: %d\n", is_little_endian());
return 0;
}
unsigned char is_little_endian(void) {
unsigned int x = 1;
unsigned char* y = (char*) (&x);
return ((*y) == 1);
}
Write a function to convert a 32-bit int variable from Little-Endian to Big-Endian. Function must receive a pointer to the int that we would like to change and the change must occur in the space of memory pointed by that argument. Hint: little endian -> least significant first ; big-endian -> most significant first
#include <stdio.h>
// Solution 1: Use 8-bits pointer to swap contents in memory
void convert_endian(unsigned int* varp) {
unsigned char* p = (unsigned char*) varp;
unsigned int size = sizeof(unsigned int);
unsigned char temp = 0;
for(int i = 0; i < size/2; i++) {
temp = p[size - i - 1];
p[size - i - 1] = p[i];
p[i] = temp;
}
}
// Solution 2: Use a union with an array
typedef union word_to_bytes {
unsigned int word;
unsigned char bytes[4];
} word_to_bytes_t;
void convert_endian_union(unsigned int* varp) {
word_to_bytes_t converter;
converter.word = *varp;
char temp = 0;
temp = converter.bytes[3];
converter.bytes[3] = converter.bytes[0];
converter.bytes[0] = temp;
temp = converter.bytes[2];
converter.bytes[2] = converter.bytes[1];
converter.bytes[1] = temp;
*varp = converter.word;
}
int main(int argc, char *argv[]) {
unsigned int x = 0xDC10AB22;
printf("Before endian conversion: 0x%x\n", x);
convert_endian(&x); //convert_endian_union(&x);
printf("After endian conversion: 0x%x\n", x);
return 0;
}
Write a function to add matrixes. Function receives as paramaters pointers to the matrixes, the number of rows and colums and should work for any matrix size.
The function must return a pointer to the beginning of the resulting matrix. Global variables for any of the matrixes can be used
int* sum_mat(int* matA, int* matB, int rows, int cols);
int main(int argc, char argv[]) {
int matR = sum_mat(&matX[0][0], &matY[0][0], MATX_ROWS, MATY_COLS);
// TODO: free memory to avoid memory leaks -> free()
return 0;
}
int* sum_mat(int* matA, int* matB, int rows, int cols) {
int* matR = (int*) malloc(sizeof(int)rowscols);
for (int r = 0; r < rows; r++) {
for (int c = 0; c < cols; c++) {
*(matR + c + r*cols) = *(matA + c + r*cols) + *(matB + c + r*cols);
}
}
return matR;
}
Write a function to multiply matrixes. Function receives as paramaters pointers to the matrixes, the number of rows and colums of each matrix and should check if multiplication is possible.
The function must return a pointer to the beginning of the resulting matrix. Global variables for any of the matrixes can be used
// Option 1: Use only pointers
int* mul_mat_pointers(int* matA, unsigned int rowsA, unsigned int colsA,
int* matB, unsigned int rowsB, unsigned int colsB) {
int* matR = NULL;
// Inners should match to be able to perfom multiplication
if(colsA == rowsB) {
// Outers determine size of resulting matrix
matR = (int*) malloc(sizeof(int) * rowsA * colsB);
for (int r = 0; r < rowsA; r++) {
for (int c = 0; c < colsB; c++) {
int sum = 0;
// Inners determine number of multiplications needed, use colsA or rowsB it's the same
for(int i = 0; i < colsA; i++) {
// For pointer iteration always multiply by 'columns' to access elements
sum += ((*(matA + r*colsA + i)) * (*(matB + i*colsB + c)));
}
// Again multiply by 'columns' to access element
*(matR + r*colsB + c) = sum;
}
}
}
return matR;
}
// Option 2: Use indexes
int matrixR[ROWS_A][COLS_B] = {};
int* mul_mat_indexes(int matA[ROWS_A][COLS_A], int matB[ROWS_B][COLS_B]) {
// Malloc Matrix if you want to use dynamic memory
//int** matrixR = malloc(ROWS_A * sizeof(int**)); for(int i = 0; i < ROWS_A; i++) matrixR[i] = malloc(COLS_B*sizeof(int*));
if(COLS_A == ROWS_B) {
for (int r = 0; r < ROWS_A; r++) {
for (int c = 0; c < COLS_B; c++) {
int sum = 0;
// Inners determine number of multiplications needed, use COLS_A or ROWS_B it's the same
for(int i = 0; i < COLS_A; i++) {
sum += (matA[r][i] * matB[i][c]);
}
matrixR[r][c] = sum;
}
}
}
return &matrixR[0][0];
Implement an interface for a Stack (LIFO) data structure in C, it must consist of at least 2 functions to push and pop data from the Stack
#include <stdio.h>
#include <stdlib.h>
// For stack use only tail/top pointer and hold reference to prev node
// on init set pointer to NULL, on push move tail,
// on pop move tail to prev (tail->prev)
typedef struct stack_node {
unsigned int data;
struct stack_node* prev;
} stack_node_t;
stack_node_t* tail = NULL;
unsigned int stack_is_empty(void) {
return (tail == NULL);
}
void stack_push(unsigned int dat) {
stack_node_t* newNode = (stack_node_t*) malloc(sizeof(stack_node_t));
// Test for malloc return: if(newNode != NULL)
newNode->data = dat;
newNode->prev = tail;
tail = newNode;
}
signed int stack_pop(void) {
signed int dat = -1;
stack_node_t* toFreeNode = NULL;
if(!stack_is_empty()) {
toFreeNode = tail;
dat = toFreeNode->data;
tail = tail->prev;
free(toFreeNode);
}
return dat;
}
int main(int argc, char *argv[]) {
printf("Stack Test!\n");
stack_push(3);
stack_push(2);
stack_push(1);
unsigned int a = 0;
a = stack_pop();
a = stack_pop();
a = stack_pop();
return a;
}
Implement an interface for a Queue (FIFO) data structure in C, it must consist of at least 2 functions to add and get data from the Queue
#include <stdio.h>
#include <stdlib.h>
// For queue use tail and head pointers and hold reference to next node
// on init set both, on add move tail,
// on get move head and check if tails needs to be set to NULL
typedef struct queue_node {
unsigned int data;
struct queue_node* next;
} queue_node_t;
queue_node_t* head = NULL;
queue_node_t* tail = NULL;
signed int queue_get(void) {
signed int dat = -1;
queue_node_t* toFreeNode = NULL;
if(!queue_is_empty()) {
toFreeNode = head;
dat = toFreeNode->data;
head = head->next;
if(head == NULL) {
tail = NULL;
}
free(toFreeNode);
}
return dat;
}
int main(int argc, char *argv[]) {
printf("Queue Test!\n");
queue_add(3);
queue_add(2);
queue_add(1);
unsigned int a = 0;
a = queue_get();
a = queue_get();
a = queue_get();
return a;
}
Implement an interface for a Linked List data structure in C, it must consist of at least 2 functions: 1) append which adds elements at the end of the list, 2) get which removes an element from certain index data from the Linked List and returns the value stored in that index
int main(int argc, char *argv[]) {
printf("linked list test!\n");
linked_list_append(0);
linked_list_append(1);
linked_list_append(3);
unsigned int a = 0;
a = linked_list_get(2);
a = linked_list_get(1);
a = linked_list_get(0);
return a;
}
General Object Oriented Programming (OOP) Questions
What is a class? an object? What are its two main components?
Response:
- A Class is the code or the blueprint/recipe that is used to create objects (instance of classes) which contains attributes=characteristics=state and methods =functions=behavior
What are the main characteristics of OOP (Object Oriented Programming)?
Response:
- Abstraction: Abstract or isolate the characteristics and behaviors of and object in order to represent it into a SW module
- Encapsulation: The ability to concentrate/encapsulate attributes=data and functionality=code in the same SW entity
- Inheritance: SW reusability technic that allows to absorb all the functionality of a base class in order to extend its functionality
Polymorphism: the ability that certain functionality/code can behave differently depending on the context. E.g. the move() method in a animals hierarchy class (all animals move but do it differently)
C++ General Programming Questions
What is the auto reserved word in used for in C++?
Response:
- It is used for implicit type declarations. States that the type of the variable will be deduced from the initializer.
In the context of C++ what's a reference? What's the difference between a reference and a pointer
Response:
It's like a pointer because it gives a way to access a variable or object but the difference is that once its initialized it cannot
refer to another variable, so it's like a pointer that can only point to a single variable or object
In the context of C++ what are templates? What are they used for?
Response:
They are a feature of C++ that allows to parametrize a class and/or functions to work with a set of types
(make a class or function generic for different data types).
In other words it allows classes and functions to work on many different data types without being rewritten for each one.
#include <iostream>
template<typename T>
T sum_types(T varA, T varB) {
return varA + varB;
}
int main(int argc, char *argv[]) {
std::cout << sum_types<std::string>("10", "11") << std::endl;
std::cout << sum_types<unsigned int>(10, 11) << std::endl;
}
In the context of C++ what is Operator Overloading? what is it used for?
Response:
It's a feature that allows operators to have different implementations depending on their arguments or the data types in which
they operate on, it allows an operator to behave differently depending on the context that its used or the types
In the context of C++ what's a virtual function and a pure virtual? What are they used for?
Response:
Used to implement polymorphism, virtual functions allow to execute different actions depending on the object thats is being referenced (pointed to)
When a function is declared with the virtual modifier the code executed when it's called always depends on the object that is being referenced (pointed to), It does NOT matter the type of the pointer or reference used to perform the call, what matters is the object
On non-virtual functions the code executed when the function is called depends on the reference or pointer used NOT on the object
pure virtual are functions with no implementation that are used to define a common interface for a classes hierarchy, these functions force a derivative class to implement them
A class with one or more pure virtual functions is called an abstract class
// Assuming both have the function 'print' implemented (Deriv class overwriting it)
// if 'print' is 'virtual' then the 'print' function defined in 'DerivClass' is called
// if 'print' is 'non-virtual' then the 'print' function defined in 'BaseClass' is called
BasePtr->print();
C++ Coding
Write a functions that swaps two values? Function must use C++ references.
// Using Pointers
void swap_ptr(int* a, int* b) {
int temp = *a;
*a = *b;
*b = temp;
}
// Using C++ References
void swap_ref(int& a, int& b) {
int temp = a;
a = b;
b = temp;
}
Define the following class hierachy in C++:
create a class Person which has the attributes name (string) and age (unsigned int), it's constructor must receive these as parametert to init attributes. This class has a method called print that prints name and age of the person
create a class Student that inherits from Person to include an attribute school (string), it's constructor inits this attribute and calls base class to init the other attributes. Student class overwrites the base class print method to now print name, age and school of the student
Overload operator ++ in the class Person to increment the age atrribute when called. E.g. the code Person p1("Jose", "27"); ++p1; should run and make age attribute of p1 equals to 28
Create a std::shared_ptr; of type Person that points to a newly created instance of Student (use the operator new to create this instance) and call the function print using this pointer
// file: person.hpp
#include <iostream>
class Person {
public:
Person(std::string name, unsigned int age);
virtual void print(void);
Person& operator++(void);
private:
std::string name;
unsigned int age;
};
class Student : public Person {
public:
Student(std::string name, unsigned int age, std::string school);
virtual void print(void);
private:
std::string school;
};
// file: student.cpp
#include "student.hpp"
// Base class constructor (Person in this case) must be called from the initialization list
Student::Student(std::string name, unsigned int age, std::string school) : Person(name, age) {
this->school = school;
}
void Student::print(void) {
Person::print();
std::cout << "Student school: " << this->school << std::endl;
}
int main(int argc, char *argv[]) {
std::shared_ptr<Person> personPtr (new Student ("Jose M", 27, "edX"));
++(*personPtr);
personPtr->print(); // This calls the 'print' function from 'Student' becasue 'print' is a virtual function
}
Python General Programming Questions
In Python what is the difference between lists, tuples and sets?
Response: Lists are mutable i.e they can be edited, add, remove and modify elements. Tuples are immutable (tuples are lists which can’t be edited), you can concatenate or slice them to form new tuples but we can't modify originals tuple or it's values Set collection of unique elements (non-repeated elements) and not iterable
Python is a compiled or interpreted language and what this means?
Response:
Python is an interpreted language Compiled languages are converted into native machine code/instructions, which are executed directly by the hardware/processor (a certain computer architecture) Interpreted languages are first translated into an intermediate ouput which then is consumed by an interpreter that will actually execute your program
What is a decorator in Python?
Response:
A decorator in Python is something that allows you to alter/modify a function easily without changing the actual implementation of the function
What is an anonymous function in Python and how it works?
Response:
An anonymous function is a function that is defined without a name, using lambda keyword, they can have any number of arguments but only one expression
Python Coding
Write a program in Python that when executed will prompt the user for text input and reply with the correct response as
provided in the following table. The program should continue to prompt for input and reply with the appropriate response until
an input value is provided that is not valid, i.e. not in the table. Please make all input case insensitive.
Write a function in Python that receives as a parameter a string and returns the first character that is repeated in that string, returns None if no character is repeated
# Solution 1 With one loop
def rep(string):
string = string.lower()
temp = ""
# Iterate over string, add character to temp string to compare again
# if exists it means character is repeated since a character was already added
for c in string:
if c in temp:
return c;
else:
temp +=c
return None # If reached end of loop means no characters are repetead so return 'None'
# Solution 2 using sets
def rep_set(string):
string = string.lower()
# Creates a string with unique characters, need to preserve order
# that's the reason to use 'string.index' function as key given a letter returns index
orderedset = ''.join(sorted(set(string), key=string.index))
# Iterate over string wiht unique characters and the first that repeats
for c in orderedset:
if string.count(c) > 1:
return c;
return None # If reached end of loop means no characters are repetead so return 'None'
print(rep("Python")) # Returns 'None' cause no letter is repeated
print(rep("Goodness")) # Returns 'o'
Write a function in Python that receives an integer and returns in a list all the divisors of that number
def divisor(num):
divs = []
for i in range(1, num+1):
if (num % i == 0):
divs.append(i)
return divs
print(divisor(7)) # Returns [1, 7] 7 is a prime number
print(divisor(10)) # Returns [1, 2, 5, 10]
print(divisor(12)) # Returns [1, 2, 3, 4, 6, 12]
Write a function in Python that receives a list of integers and returns a list of the same size where each element is
created by the multiplication of all the other elements of the list received as parameter except for the one in the index you are trying to calculate.
So for example given the list [1, 2, 3, 4] it returns [24, 12, 8, 6] because for index 0: it multiples 2x3x4=24 avoiding the value 1, for index 1: 1x3x4=12 avoiding the value 2 and so on...
# Solution 1: Two separate loops
def mult_list(inlist):
reslist = []
# Multiply all elements
acc = 1
for n in inlist:
acc *= n;
# Creates elements of results list by dividing the multiplication
# of all elements by each element of the input list
for i in inlist:
reslist.append(acc//i) # use // for integer mult Python3
return reslist
# Solution 2: Copy list
def mult_list_cpy(inlist):
reslist = []
for e in inlist:
# Copy list and remove element that wont be included in current calculation
temp = list(inlist)
temp.remove(e);
acc = 1;
# multiply rest of elements
for n in temp:
acc *= n;
reslist.append(acc)
return reslist
print(mult_list([1, 2, 3, 4])) # [24, 12, 8, 6]
Write a function in Python that receives a list of integers (positive and negative) and returns the sum of all the two digit numbers in that list
so for example given the list [1, 1000, 80, -91] it returns -11 because it's the sum of 80 and -91 (-91+80=-11)
# Solution 1: Filter
def two_digits_sum(A):
# Use Filter to return a list only with two-digit numbers, use `sum` so add all elements of that list
two_digits = list(filter(lambda x: (abs(x) > 9 and abs(x) < 100), A))
return (sum(two_digits))
print(two_digits_sum(A))
Consider the format HH:MM:SS to define a certain point in time of the day for example 16:11:16 to describe 4:11pm with 16 seconds.
A point in time, like this one, is considered "interesting" if it needs 2 or less different digits to represent it so the previous
example would be considered "interesting" because it only needs the digits 1 and 6.
Considering this definition of "interesting", write a function in Python that receives a start (S) and end (T) points in time in the format HH:MM:SS and returns the number of points
considered "interesting" between this time interval including both limits (S and T), under the assumption T will always be a later time than (S).
So for example given S="22:22:21" and T="22:22:23" as inputs to your function it should return 3 because 22:22:21, 22:22:22 and 22:22:23
are all considered "interesting"
# Solution 1: Using datetime and time delta
from datetime import datetime, time, timedelta
# Check if a time point represented as a string in the format "HH:MM:SS"
# is considered "interesting"
def is_time_interesting(time_str):
time_str = time_str.replace(":","")
slen = len(set(time_str))
return (slen == 2 or slen == 1)
def count_interesting_time_points(S, T):
FORMAT = "%H:%M:%S"
# Create datetime objects with inputs
So = datetime.strptime(S, FORMAT)
To = datetime.strptime(T, FORMAT)
int_cnt = 0
# Iterate through all points in time interval
while So <= To:
# Convert datetime object to string to check if is considered "interesting"
if is_time_interesting(So.strftime(FORMAT)):
int_cnt += 1
# Use datetime object to create next point in time
So += timedelta(seconds = 1)
return int_cnt
S = "22:22:21"
T = "22:22:23"
print(count_interesting_time_points(S, T))
Binary gap within a positive integer N is any maximal sequence of consecutive zeros that is surrounded by ones at both ends in the binary representation of N.
For example, number 9 has a binary representation 1001 and contains a binary gap of length 2, numbers 32(100000) and 15 (1111) have no binary gaps, while
545(1000100001) and 529(1000010001) contains two binary gaps: one of length 4 and one of length 3 each
Considering this definition of "binary gap", write a function that, given a positive integer N, returns the length of its longest binary gap.
The function should return 0 if N doesn't contain a binary gap.
N = 529
# Solution 1: use regexp
import re
def binary_gap(N):
bin_str = "{0:b}".format(N)
bin_gaps = 0
finds = re.findall('1(0+)(?=1)', bin_str)
if finds:
# max() call returns the longest string of the ones found
# getting the length gives the longest binary gap
bin_gaps = len(max(finds, key=len))
return bin_gaps
print(binary_gap(N))
# Solution 2: split string
def binary_gap_split(N):
bin_str = "{0:b}".format(N)
bin_gaps = bin_str.split("1")
del bin_gaps[0]
del bin_gaps[-1]
if bin_gaps:
return len(max(bin_gaps, key=len))
else:
return 0
print(binary_gap_split(N))
# Solution 3: use a state machine to detect sequence
def binary_gap_state_mach(N):
shift = 1
state = "WAIT_1"
max_gap = 0
zeros_cnt = 0
while N > 0:
is_one = (N & 1 != 0)
if state == "WAIT_1":
if is_one:
state = "WAIT_0"
else:
state = "WAIT_1"
elif state == "WAIT_0":
if is_one:
state = "WAIT_0"
else:
zeros_cnt = 1
state = "COUNT_ZEROS"
elif state == "COUNT_ZEROS":
if is_one:
state = "WAIT_0"
max_gap = max(max_gap, zeros_cnt)
else:
zeros_cnt += 1
state = "COUNT_ZEROS"
N >>= 1
return max_gap
print(binary_gap_state_mach(N))
A right rotation of an array shifts each element of the array 1 unit to the right and the last element of the array is moved to the first place.
For example [3, 8, 9, 7, 6] right rotated 3 times is -> [9, 7, 6, 3, 8].A left rotation of an array shifts each element of the array 1 unit to the left and the first element of the array is moved to the last place
For example [1, 2, 3, 4, 5] left rotated 4 times is -> [5, 1, 2, 3, 4].
Considering this, write two functions that, given an array A consisting of N integers and an integer K,
returns the array A rotated K times, one function must return the array rotated to the right and the other the array rotated to the left
def right_rotate(A, K):
size = len(A)
if K > size and size > 0:
K = K % size
left = A[0:size-K]
right = A[size-K:]
res = right + left
return res
print(right_rotate([3, 8, 9, 7, 6], 3))
def left_rotate(A, K):
size = len(A)
if K > size and size > 0:
K = K % size
left = A[0:K]
right = A[K:]
res = right + left
return res
print(left_rotate([1, 2, 3, 4, 5], 14))
Write a function in Python, that given two strings as inputs returns whether one is an anagram of the other,
for example it returns True for the inputs night and thing
# Solution 1: Sort and compare strings
def is_anagram_sort(s1, s2):
s1 = s1.lower().replace(" ", "")
s2 = s2.lower().replace(" ", "")
return (sorted(s1) == sorted(s2))
print(is_anagram_sort("night", "thing"))
# Solution 2: Create dicts and compare them
def string_to_dict(s):
d = {}
for c in s:
if c in d:
d[c] += 1
else:
d[c] = 1
return d
def is_anagram(s1, s2):
s1 = s1.lower().replace(" ", "")
s2 = s2.lower().replace(" ", "")
d1 = string_to_dict(s1)
d2 = string_to_dict(s2)
return (d1 == d2)
print(is_anagram("night", "thing"))
Linux & Operating Systems Questions
What is the kernel of an OS?
Response:
Refers to the core program of an Operating System the one that handles the essential tasks of the OS (like the handling of memory and input/output requests)
What's the difference between user mode and kernel mode?
Response:
Usually user applications run in user mode and when it requests a service from the operating system the system must transition from user to kernel mode to fulfill the request
User mode: executing code has no ability to directly access hardware, reference memory or other resources
Kernel mode: executing code has access to the underlying hardware and resources and it can execute restricted CPU instructions
What do applications running in user mode use to request a service from the OS
Response: System Calls are the way a user app requests that an operating-system service be performed
What is a Linux Distribution? What differentiate Linux distributions?
Response:
Linux Distro is combination of the Linux kernel with other system components (or programs/packages) to make a Linux based OS. So the programs, packages that are included, the windows manager and the package manager are a few singularities for each distro
A custom build of the GNU/Linux operating system combined with other programs/packages
What do you need to compile the Linux kernel?
Response:
In general you need a compiler (E.g. gcc) and the Linux kernel sources (.c, .h… files) files. You can do native build or cross compile
What is the difference between a Thread and a Process?
Response:
Both processes and threads are independent sequences of execution
Threads are of the same process and run in a shared memory space (E.g. a process can have multiple threads running independently to solve a problem)
Processes run in separate memory space (A Process is usually associated with a running program instance and inside that process there could be multiple Threads)
Embedded Systems & RTOS Questions
What is a Timer?
Response:
A peripheral used to measure time usually implemented with a counter
What is an Interrupt?
Response:
An event that interrupts the software and transfer it's execution to a handler or specific code section.
Book Definition: An interrupt is the automatic transfer of software execution in response to a hardware event that is asynchronous with the current software execution.
How does an ADC work in general?
Response:
At a SW level it needs to be configured to define it's sample rate, it's trigger mechanism (usually can be by SW or by other peripheral),
if its going to cause interrupts, once configured we need to trigger it's operation to then read the register that holds the ADC conversion
then usually clear the flags and repeat the process
At a HW level it can be implemented using analog comparators, a DAC and a generator of digital values
What is the difference between ROM and Flash memory?
Response:
Both are non-volatile so they preserve it's data after power is removed
A ROM It's a pure Read-Only Memory so it's content cannot be modified, its content is usually stored as part of the manufacturing process (E.g. fuses)
A Flash It's a type of EEPROM (Electrically Erasable Programmable Read-Only Memory) that can be erased and programmed electrically (usually in blocks of specific size)
What is the difference between a HW breakpoint and a SW breakpoint?
Response:
A Hardware Breakpoint is implemented using special logic that is integrated into the device. For example with a set of programmable comparators that are connected to the program address bus.
A Software Breakpoint is implemented in software usually with a special instruction (opcode) that gets inserted in the code
What is a Race Condition?
Response:
A race condition occurs when two or more threads can access shared data/resource and they try to change it. Therefore, the result of the change
is dependent on the order the threads execute, which could cause unexpected results if we need the shared data/resource to be accessed in a specific order.
It is a race condition because both threads are "racing" to access/change the data.
Validation, Verificaion, Testing
What is the difference between Validation and Verification?
Response:
Verification: is intended to check that something (a product, service, software, or system) meets a set requirements (design specifications)
Validation: is intended to ensure that something (a product, service,software, or system) meets the operational needs of the user (informally meets user expectations and use cases)
These definitions can vary depending on the context and in practice are usually used interchangeably
What is a Test Case and an Use Case?
Response:
Test Case: It is what documents all the conditions, procedures and inputs that are needed to execute a single test and the expected outputs of the test
Use Case: It is what describes the user action and system response for a particular functionality, one Use Case can have many Test Cases.
What is a Test Plan?
Response:
It is a document that defines the test strategy, it specifies all the components needed for testing something (it could be a code, a SW module, a device, a project etc.)
it outlines the testing objectives, resources, test schedule, test estimation and test deliverables.
What is a Bug?
Response:
It is an error, flaw, failure or fault in the system that causes it to produce an incorrect or unexpected result, or to behave in unintended ways.
When the expected results (accordingly to the requirement documents) don’t match with the actual results
From the testing perspective what is done when a Bug is found? What is the Bug life cycle from a testing perspective?
Response:
Find and verify the bug, report it (create a bug report), this gets assigned to design/development team to fix it,
give follow up with development team and after the fix is provided, testing must verify whether the bug exists again
if it's fixed the bug is closed if not cycle is repeated
What is the difference between 'White Box' and 'Black Box' Testing?
Response:
White Box: is a testing methodology that tests internal structures or workings of system, it test internal paths and functionality of the system
Black Box: is a testing methodology which is used to test without knowing the internal structure of code or system only focusing on inputs and outputs or the external interfaces of a system
Other Questions
Explain what is make and a Makefile?
Response:
make: is a dependency resolver program that controls the generation of files and usually executables (and libraries)
Makefile: file used by the make utility that has the knowledge of how to build/generate an executable, program or other file(s)
What is the difference between a Shared and a Static Library?
Response:
Shared Library: they are usually .so or .dll files, all the code relating to the library is in this file, which it is referenced by programs using it at run-time. A program using a shared library only makes references to the code, it doesn't hold a copy of the code. Reduce program size and independency between code but increases loading cost and places a dependency on the system running the program
Static Library: they are usually .a or .lib files, all the code relating to the library is in this file and it is directly linked into the program at compile time. A program using a static library takes copies of the code that it uses from the static library and makes it part of the program. Increase size of program but improves speed and removes run time dependencies
In a software context what is the main difference between a Framework and a Library?
Response:
Both can be defined as collection of software (classes, methods, functions, objects, etc.) but the main difference is who or what is in control, something also known as Inversion of Control (IoC)
A Framework: helps you create a specific type of application (e.g. Web application, a GUI app, a mobile app, etc.) but it requires the user to define/create certain functionality, this means your code is called by the framework who is the one that is actually in control of the flow
A Library: helps you perform specific operations (e.g. mathematical complex number operations, string manipulation, image compression, etc.) and the library is called by your code or application, this means your code has the main control of the flow
Want to show support?
If you find the information in this page useful and want to show your support, you can make a donation