Automotive Interview Questions and CAN interview questions and important C programming interview Questions are asked in Automotive interviews.This are the very important questions for automotive interviews.
12. Program to find
if a machine is big endian or little endian?
01 |
#include "stdio.h" |
02 |
#define BIG_ENDIAN 0 |
03 |
#define LITTLE_ENDIAN 1 |
04 |
int main() |
05 |
{ |
06 |
int value; |
07 |
value
= endian(); |
08 |
if (value
== 1) |
09 |
printf("Machine
is little endian\n",value); |
10 |
else |
11 |
printf("Machine
is Big Endian\n",value); |
12 |
} |
13 |
int endian()
{ |
14 |
short int word
= 0x0001; |
15 |
char *byte
= (char *) &word; |
16 |
return (byte[0]
? LITTLE_ENDIAN : BIG_ENDIAN); |
17 |
} |
13. Swap 2 variables
without using temporary variable!
a = a + b
b = a – b
a = a – b
b = a – b
a = a – b
14. Write a program
to generate the Fibonacci Series?
#include<stdio.h>
#include<conio.h>
main()
{
int n,i,c,a=0,b=1;
printf(“Enter Fibonacci series of nth term : “);
scanf(“%d”,&n);
printf(“%d %d “,a,b);
for(i=0;i<=(n-3);i++)
{
c=a+b;
a=b;
b=c;
printf(“%d “,c);
}
getch();
}Output :
Enter Fibonacci series of nth term : 7
0 1 1 2 3 5 8
#include<conio.h>
main()
{
int n,i,c,a=0,b=1;
printf(“Enter Fibonacci series of nth term : “);
scanf(“%d”,&n);
printf(“%d %d “,a,b);
for(i=0;i<=(n-3);i++)
{
c=a+b;
a=b;
b=c;
printf(“%d “,c);
}
getch();
}Output :
Enter Fibonacci series of nth term : 7
0 1 1 2 3 5 8
15. Write a program
to find unique numbers in an array?
Answer:
for
(i=1;i<=array.length;i++) {
found=false;
for (k=i+1;k<=array.length;k++) {
if (array[i]==array[k]) found=true;
}
if (!found) println(array[i]);
}
found=false;
for (k=i+1;k<=array.length;k++) {
if (array[i]==array[k]) found=true;
}
if (!found) println(array[i]);
}
16. Write a C
program to print Equilateral Triangle using numbers?
01 |
/* C program to print
Equilateral Triangle*/ |
02 |
#include<stdio.h> |
03 |
main() |
04 |
{ |
05 |
int i,j,k,n; |
06 |
07 |
printf("Enter
number of rows of the triangle \n"); |
08 |
scanf("%d",&n); |
09 |
10 |
for(i=1;i<=n;i++) |
11 |
{ |
12 |
for(j=1;j<=n-i;j++) |
13 |
{ |
14 |
printf("
"); |
15 |
} |
16 |
for(k=1;k<=(2*i)-1;k++) |
17 |
{ |
18 |
printf("i"); |
19 |
} |
20 |
printf("\n"); |
21 |
} |
22 |
getch(); |
17. Write a
program for deletion and insertion of a node in single linked list?
#include<stdio.h>
#include<stdlib.h>
typedef struct Node
{
int data;
struct Node *next;
}node;
void insert(node *pointer, int data)
{
/* Iterate through the list till we encounter the last node.*/
while(pointer->next!=NULL)
{
pointer = pointer -> next;
}
/* Allocate memory for the new node and put data in it.*/
pointer->next = (node *)malloc(sizeof(node));
pointer = pointer->next;
pointer->data = data;
pointer->next = NULL;
}
int find(node *pointer, int key)
{
pointer = pointer -> next; //First node is dummy node.
/* Iterate through the entire linked list and search for the key. */
while(pointer!=NULL)
{
if(pointer->data == key) //key is found.
{
return 1;
}
pointer = pointer -> next;//Search in the next node.
}
/*Key is not found */
return 0;
}
void delete(node *pointer, int data)
{
/* Go to the node for which the node next to it has to be deleted */
while(pointer->next!=NULL && (pointer->next)->data != data)
{
pointer = pointer -> next;
}
if(pointer->next==NULL)
{
printf(“Element %d is not present in the list\n”,data);
return;
}
/* Now pointer points to a node and the node next to it has to be removed */
node *temp;
temp = pointer -> next;
/*temp points to the node which has to be removed*/
pointer->next = temp->next;
/*We removed the node which is next to the pointer (which is also temp) */
free(temp);
/* Beacuse we deleted the node, we no longer require the memory used for it .
free() will deallocate the memory.
*/
return;
}
void print(node *pointer)
{
if(pointer==NULL)
{
return;
}
printf(“%d “,pointer->data);
print(pointer->next);
}
int main()
{
/* start always points to the first node of the linked list.
temp is used to point to the last node of the linked list.*/
node *start,*temp;
start = (node *)malloc(sizeof(node));
temp = start;
temp -> next = NULL;
/* Here in this code, we take the first node as a dummy node.
The first node does not contain data, but it used because to avoid handling special cases
in insert and delete functions.
*/
printf(“1. Insert\n”);
printf(“2. Delete\n”);
printf(“3. Print\n”);
printf(“4. Find\n”);
while(1)
{
int query;
scanf(“%d”,&query);
if(query==1)
{
int data;
scanf(“%d”,&data);
insert(start,data);
}
else if(query==2)
{
int data;
scanf(“%d”,&data);
delete(start,data);
}
else if(query==3)
{
printf(“The list is “);
print(start->next);
printf(“\n”);
}
else if(query==4)
{
int data;
scanf(“%d”,&data);
int status = find(start,data);
if(status)
{
printf(“Element Found\n”);
}
else
{
printf(“Element Not Found\n”); }
}
}}
#include<stdlib.h>
typedef struct Node
{
int data;
struct Node *next;
}node;
void insert(node *pointer, int data)
{
/* Iterate through the list till we encounter the last node.*/
while(pointer->next!=NULL)
{
pointer = pointer -> next;
}
/* Allocate memory for the new node and put data in it.*/
pointer->next = (node *)malloc(sizeof(node));
pointer = pointer->next;
pointer->data = data;
pointer->next = NULL;
}
int find(node *pointer, int key)
{
pointer = pointer -> next; //First node is dummy node.
/* Iterate through the entire linked list and search for the key. */
while(pointer!=NULL)
{
if(pointer->data == key) //key is found.
{
return 1;
}
pointer = pointer -> next;//Search in the next node.
}
/*Key is not found */
return 0;
}
void delete(node *pointer, int data)
{
/* Go to the node for which the node next to it has to be deleted */
while(pointer->next!=NULL && (pointer->next)->data != data)
{
pointer = pointer -> next;
}
if(pointer->next==NULL)
{
printf(“Element %d is not present in the list\n”,data);
return;
}
/* Now pointer points to a node and the node next to it has to be removed */
node *temp;
temp = pointer -> next;
/*temp points to the node which has to be removed*/
pointer->next = temp->next;
/*We removed the node which is next to the pointer (which is also temp) */
free(temp);
/* Beacuse we deleted the node, we no longer require the memory used for it .
free() will deallocate the memory.
*/
return;
}
void print(node *pointer)
{
if(pointer==NULL)
{
return;
}
printf(“%d “,pointer->data);
print(pointer->next);
}
int main()
{
/* start always points to the first node of the linked list.
temp is used to point to the last node of the linked list.*/
node *start,*temp;
start = (node *)malloc(sizeof(node));
temp = start;
temp -> next = NULL;
/* Here in this code, we take the first node as a dummy node.
The first node does not contain data, but it used because to avoid handling special cases
in insert and delete functions.
*/
printf(“1. Insert\n”);
printf(“2. Delete\n”);
printf(“3. Print\n”);
printf(“4. Find\n”);
while(1)
{
int query;
scanf(“%d”,&query);
if(query==1)
{
int data;
scanf(“%d”,&data);
insert(start,data);
}
else if(query==2)
{
int data;
scanf(“%d”,&data);
delete(start,data);
}
else if(query==3)
{
printf(“The list is “);
print(start->next);
printf(“\n”);
}
else if(query==4)
{
int data;
scanf(“%d”,&data);
int status = find(start,data);
if(status)
{
printf(“Element Found\n”);
}
else
{
printf(“Element Not Found\n”); }
}
}}
18. Can a variable be
both const and volatile? Yes.
The const modifier means that this code cannot change the value of
the variable, but that does not mean that the value cannot be changed
by means outside this code. For instance, in the example in FAQ
8, the timer structure was accessed through a volatile const pointer.
The function itself did not change the value of the timer, so it was
declared const. However, the value was changed by hardware on the
computer, so it was declared volatile. If a variable is both const
and volatile, the two modifiers can appear in either order.
19. what are
Constant and Volatile Qualifiers?
const
-
const
is used with a datatype declaration or definition to specify an unchanging value-
Examples:
const int five = 5; const double pi = 3.141593;
-
-
const
objects may not be changed-
The following are illegal:
const int five = 5; const double pi = 3.141593; pi = 3.2; five = 6;
-
volatile
-
volatile
specifies a variable whose value may be changed by processes outside the current program -
One example of a
volatile
object might be a buffer used to exchange data with an external device:int check_iobuf(void) { volatile int iobuf; int val; while (iobuf == 0) { } val = iobuf; iobuf = 0; return(val); }
ifiobuf
had not been declaredvolatile
, the compiler would notice that nothing happens inside the loop and thus eliminate the loop -
const
andvolatile
can be used together-
An input-only buffer for an external device could be declared as
const volatile
(orvolatile const
, order is not important) to make sure the compiler knows that the variable should not be changed (because it is input-only) and that its value may be altered by processes other than the current program
-
The keywords
const
and volatile
can
be applied to any declaration, including those of structures, unions,
enumerated types or typedef
names.
Applying them to a declaration is called qualifying the
declaration—that’s why const and volatile are called type
qualifiers, rather than type specifiers. Here are a few
representative examples:volatile i;
volatile int j;
const long q;
const volatile unsigned long int rt_clk;
struct{
const long int li;
signed char sc;
}volatile vs;
Don’t be put off; some of them are deliberately complicated: what
they mean will be explained later. Remember that they could also be
further complicated by introducing storage class specifications as
well! In fact, the truly spectacular
extern const volatile unsigned long int rt_clk;
is a strong possibility in some real-time operating system kernels.
Const
Let’s look at what is meant when
const
is
used. It’s really quite simple: const
means
that something is not modifiable, so a data object that is declared
with const
as a part of its
type specification must not be assigned to in any way during the run
of a program. It is very likely that the definition of the object
will contain an initializer (otherwise, since you can’t assign to
it, how would it ever get a value?), but this is not always the case.
For example, if you were accessing a hardware port at a fixed memory
address and promised only to read from it, then it would be declared
to be const
but not
initialized.
Taking the address of a data object of a type which
isn’t
const
and putting it
into a pointer to the const
-qualified
version of the same type is both safe and explicitly permitted; you
will be able to use the pointer to inspect the object, but not modify
it. Putting the address of a const type into a pointer to the
unqualified type is much more dangerous and consequently prohibited
(although you can get around this by using a cast). Here is an
example:#include <stdio.h>
#include <stdlib.h>
main(){
int i;
const int ci = 123;
/* declare a pointer to a const.. */
const int *cpi;
/* ordinary pointer to a non-const */
int *ncpi;
cpi = &ci;
ncpi = &i;
/*
* this is allowed
*/
cpi = ncpi;
/*
* this needs a cast
* because it is usually a big mistake,
* see what it permits below.
*/
ncpi = (int *)cpi;
/*
* now to get undefined behaviour...
* modify a const through a pointer
*/
*ncpi = 0;
exit(EXIT_SUCCESS);
}
Example 8.3
As the example shows, it is possible to take the
address of a constant object, generate a pointer to a non-constant,
then use the new pointer. This is an error in your
program and results in undefined behaviour.
The main intention of introducing const objects was to
allow them to be put into read-only store, and to permit compilers to
do extra consistency checking in a program. Unless you defeat the
intent by doing naughty things with pointers, a compiler is able to
check that
const
objects are
not modified explicitly by the user.
An interesting extra feature pops up now. What does
this mean?
char c;
char *const cp = &c;
It’s simple really;
cp
is a
pointer to a char
, which is exactly
what it would be if the const
weren’t
there. The const
means
that cp
is not to be modified,
although whatever it points to can be—the pointer is constant, not
the thing that it points to. The other way round isconst char *cp;
which means that now cp is an ordinary, modifiable pointer, but the
thing that it points to must not be modified. So, depending on what
you choose to do, both the pointer and the thing it points to may be
modifiable or not; just choose the appropriate declaration.
Volatile
After const, we treat
volatile
.
The reason for having this type qualifier is mainly to do with the
problems that are encountered in real-time or embedded systems
programming using C. Imagine that you are writing code that controls
a hardware device by placing appropriate values in hardware registers
at known absolute addresses.
Let’s imagine that the device has two registers, each
16 bits long, at ascending memory addresses; the first one is the
control and status register (csr) and the second is a data port. The
traditional way of accessing such a device is like this:
/* Standard C example but without const or volatile */
/*
* Declare the device registers
* Whether to use int or short
* is implementation dependent
*/
struct devregs{
unsigned short csr; /* control & status */
unsigned short data; /* data port */
};
/* bit patterns in the csr */
#define ERROR 0x1
#define READY 0x2
#define RESET 0x4
/* absolute address of the device */
#define DEVADDR ((struct devregs *)0xffff0004)
/* number of such devices in system */
#define NDEVS 4
/*
* Busy-wait function to read a byte from device n.
* check range of device number.
* Wait until READY or ERROR
* if no error, read byte, return it
* otherwise reset error, return 0xffff
*/
unsigned int read_dev(unsigned devno){
struct devregs *dvp = DEVADDR + devno;
if(devno >= NDEVS)
return(0xffff);
while((dvp->csr & (READY | ERROR)) == 0)
; /* NULL - wait till done */
if(dvp->csr & ERROR){
dvp->csr = RESET;
return(0xffff);
}
return((dvp->data) & 0xff);
}
Example 8.4
The technique of using a structure declaration to
describe the device register layout and names is very common
practice. Notice that there aren’t actually any objects of that
type defined, so the declaration simply indicates the structure
without using up any store.
To access the device registers, an appropriately cast
constant is used as if it were pointing to such a structure, but of
course it points to memory addresses instead.
However, a major problem with previous C compilers
would be in the while loop which tests the status register and waits
for the
ERROR
or READY
bit
to come on. Any self-respecting optimizing compiler would notice that
the loop tests the same memory address over and over again. It would
almost certainly arrange to reference memory once only, and copy the
value into a hardware register, thus speeding up the loop. This is,
of course, exactly what we don’t want; this is one of the few
places where we must look at the place where the pointer points,
every time around the loop.
Because of this problem, most C compilers have been
unable to make that sort of optimization in the past. To remove the
problem (and other similar ones to do with when to write to where a
pointer points), the keyword
volatile
was
introduced. It tells the compiler that the object is subject to
sudden change for reasons which cannot be predicted from a study of
the program itself, and forces every reference to such an object to
be a genuine reference.
Here is how you would rewrite the example, making use
of
const
and volatile
to
get what you want./*
* Declare the device registers
* Whether to use int or short
* is implementation dependent
*/
struct devregs{
unsigned short volatile csr;
unsigned short const volatile data;
};
/* bit patterns in the csr */
#define ERROR 0x1
#define READY 0x2
#define RESET 0x4
/* absolute address of the device */
#define DEVADDR ((struct devregs *)0xffff0004)
/* number of such devices in system */
#define NDEVS 4
/*
* Busy-wait function to read a byte from device n.
* check range of device number.
* Wait until READY or ERROR
* if no error, read byte, return it
* otherwise reset error, return 0xffff
*/
unsigned int read_dev(unsigned devno){
struct devregs * const dvp = DEVADDR + devno;
if(devno >= NDEVS)
return(0xffff);
while((dvp->csr & (READY | ERROR)) == 0)
; /* NULL - wait till done */
if(dvp->csr & ERROR){
dvp->csr = RESET;
return(0xffff);
}
return((dvp->data) & 0xff);
}
Example 8.5
The rules about mixing
volatile
and
regular types resemble those for const
.
A pointer to a volatile
object
can be assigned the address of a regular object with safety, but it
is dangerous (and needs a cast) to take the address of
a volatile
object and put it
into a pointer to a regular object. Using such a derived pointer
results in undefined behaviour.
If an array, union or structure is declared
with
const
or volatile
attributes,
then all of the members take on that attribute too. This makes sense
when you think about it—how could a member of a const
structure
be modifiable?
That means that an alternative rewrite of the last
example would be possible. Instead of declaring the device registers
to be
volatile
in the
structure, the pointer could have been declared to point to
a volatile
structure instead,
like this:struct devregs{
unsigned short csr; /* control & status */
unsigned short data; /* data port */
};
volatile struct devregs *const dvp=DEVADDR+devno;
Since
dvp
points to
a volatile
object, it not
permitted to optimize references through the pointer. Our feeling is
that, although this would work, it is bad style.
The volatile
declaration
belongs in the structure: it is the device registers which
are volatile
and that is where
the information should be kept; it reinforces the fact for a human
reader.
So, for any object likely to be subject to modification
either by hardware or asynchronous interrupt service routines, the
volatile type qualifier is important.
Now, just when you thought that you understood all
that, here comes the final twist. A declaration like this:
volatile struct devregs{
/* stuff */
}v_decl;
declares the type
struct devregs
and
also a volatile
-qualified object of
that type, called v_decl
. A later
declaration like thisstruct devregs nv_decl;
declares
nv_decl
which
is not qualified with volatile
!
The qualification is not part of the type
of struct devregs
but applies
only to the declaration of v_decl
.
Look at it this way round, which perhaps makes the situation more
clear (the two declarations are the same in their effect):struct devregs{
/* stuff */
}volatile v_decl;
If you do want to get a shorthand way of attaching a qualifier to
another type, you can use
typedef
to
do it:struct x{
int a;
};
typedef const struct x csx;
csx const_sx;
struct x non_const_sx = {1};
const_sx = non_const_sx; /* error - attempt to modify a const */
20.
21. What is meant by structure padding?
Answer: compilers pad structures
to optimize data transfers. This is an hardware architecture issue.
Most modern CPUs perform best when fundamental types, like ‘int’
or ‘float’, are aligned on memory boundaries of a particular size
(eg. often a 4byte word on 32bit archs). Many architectures don’t
allow misaligned access or if they do inoccur a performance penality.
When a compiler processes a structure declaration it will add extra
bytes between fields to meet alignment needs.
Most processors require specific memory alignment on
variables certain types. Normally the minimum alignment is the size
of the basic type in question, for instance this is common
char variables can be byte aligned and appear at any
byte boundary
short (2 byte) variables must be 2 byte aligned, they
can appear at any even byte boundary. This means that 0×10004567 is
not a valid location for a short variable but 0×10004566 is.
long (4 byte) variables must be 4 byte aligned, they
can only appear at byte boundaries that are a multiple of 4 bytes.
This means that 0×10004566 is not a valid location for a long
variable but 0×10004568 is.
Structure padding occurs because the members of the
structure must appear at the correct byte boundary, to achieve this
the compiler puts in padding bytes (or bits if bit fields are in use)
so that the structure members appear in the correct location.
Additionally the size of the structure must be such that in an array
of the structures all the structures are correctly aligned in memory
so there may be padding bytes at the end of the structure too
struct example {
char c1;
short s1;
char c2;
long l1;
char c3;
}
char c1;
short s1;
char c2;
long l1;
char c3;
}
In this structure, assuming the alignment scheme I have
previously stated then
c1 can appear at any byte boundary, however s1 must
appear at a 2 byte boundary so there is a padding byte between c1 and
s1.
c2 can then appear in the available memory location,
however l1 must be at a 4 byte boundary so there are 3 padding bytes
between c2 and l1
c3 then appear in the available memory location,
however because the structure contains a long member the structure
must be 4 byte aligned and must be a multiple of 4 bytes in size.
Therefore there are 3 padding bytes at the end of the structure. It
would appear in memory in this order
c1
padding byte
s1 byte 1
s1 byte 2
c2
padding byte
padding byte
padding byte
l1 byte 1
l1 byte 2
l1 byte 3
l1 byte 4
c3
padding byte
padding byte
padding byte
padding byte
s1 byte 1
s1 byte 2
c2
padding byte
padding byte
padding byte
l1 byte 1
l1 byte 2
l1 byte 3
l1 byte 4
c3
padding byte
padding byte
padding byte
The structure would be 16 bytes long.
re-written like this
struct example {
long l1;
short s1;
char c1;
char c2;
char c3;
}
long l1;
short s1;
char c1;
char c2;
char c3;
}
Then l1 appears at the correct byte alignment, s1 will
be correctly aligned so no need for padding between l1 and s1. c1,
c2, c3 can appear at any location. The structure must be a multiple
of 4 bytes in size since it contains a long so 3 padding bytes appear
after c3
It appears in memory in the order
It appears in memory in the order
l1 byte 1
l1 byte 2
l1 byte 3
l1 byte 4
s1 byte 1
s1 byte 2
c1
c2
c3
padding byte
padding byte
padding byte
l1 byte 2
l1 byte 3
l1 byte 4
s1 byte 1
s1 byte 2
c1
c2
c3
padding byte
padding byte
padding byte
and is only 12 bytes long.
I should point out that structure packing is platform
and compiler (and in some cases compiler switch) dependent.
Memory Pools are just a section of memory reserved for
allocating temporarily to other parts of the application
A memory leak occurs when you allocate some memory from
the heap (or a pool) and then delete all references to that memory
without returning it to the pool it was allocated from.
Program:
-
struct MyStructA {
-
char a;
-
char b;
-
int c;
-
};
-
struct MyStructB {
-
char a;
-
int c;
-
char b;
-
};
-
int main(void) {
-
int sizeA = sizeof(struct MyStructA);
-
int sizeB = sizeof(struct MyStructB);
-
printf(“A = %d\n”, sizeA);
-
printf(“B = %d\n”, sizeB);
-
return 0;
-
}
22. What is the
difference between macro and constant variables in C?
Macros are replaced by preprocessor, but in constant
data type will be checked by compiler. Macros are replaced without
checking the values sometimes the programmer want to change
values only in a single function at that prefer to use constant
than a macro.
The first technique comes from the C programming
language. Constants may be defined using the preprocessor directive,
#define The preprocessor is a program that modifies your source file
prior to compilation. Common preprocessor directives are #include,
which is used to
include additional code into your source file, #define, which is used to define a constant and #if/#endif, which can be used to conditionally determine which parts of your code will be compiled. The #define directive is used as follows.
include additional code into your source file, #define, which is used to define a constant and #if/#endif, which can be used to conditionally determine which parts of your code will be compiled. The #define directive is used as follows.
#define pi 3.1415#define id_no 12345
|
Wherever the constant appears in your source file, the
preprocessor replaces it by its value. So, for instance, every “pi”
in your source code will be replace by 3.1415. The compiler will only
see the value 3.1415 in your code, not “pi”. The problem with
this technique is that the replacement is done lexically, without any
type checking, without any bound checking and without any scope
checking. Every “pi” is just replaced by its value. The technique
is outdated, exists to support legacy code and should be
avoided.
Const
The second technique is to use the keyword const when defining a variable. When used the compiler will catch attempts to modify variables that have been declared const.
Const
The second technique is to use the keyword const when defining a variable. When used the compiler will catch attempts to modify variables that have been declared const.
const float pi = 3.1415;const int id_no = 12345;
|
There are two main advantages over the first technique.
First, the type of the constant is defined. “pi” is
float. “id_no” is int. This allows some type checking by the
compiler.&nbs p;
Second, these constants are variables with a definite scope. The scope of a variable relates to parts of your program in which it is defined. Some variables may exist only in certain functions or in certain blocks of code.
Second, these constants are variables with a definite scope. The scope of a variable relates to parts of your program in which it is defined. Some variables may exist only in certain functions or in certain blocks of code.
Ex : You may want to use “id_no” in one
function
and a completely unrelated “id_no” in your main program.
and a completely unrelated “id_no” in your main program.
Comments