Skip to main content

C Interview Questions Automotive company interview

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
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  
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]);
}
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”);                        }
}
}}

18. Can a variable be both const and volatileYes. 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

  • constis used with a datatype declaration or definition to specify an unchanging value
    • Examples:
              const int five = 5;
              const double pi = 3.141593;
  • constobjects may not be changed
    • The following are illegal:
              const int five = 5;
              const double pi = 3.141593;
      
              pi = 3.2;
              five = 6;

volatile

  • volatilespecifies a variable whose value may be changed by processes outside the current program
  • One example of a volatileobject 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);
    }
    if iobuf had not been declared volatile, the compiler would notice that nothing happens inside the loop and thus eliminate the loop
  • const and volatile can be used together
    • An input-only buffer for an external device could be declared as const volatile (or volatile 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 is
const 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 this
struct 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;
}
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
The structure would be 16 bytes long.
re-written like this
struct example {
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
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
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.
#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 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.
Ex : You may want to use “id_no” in one function
and a completely unrelated “id_no” in your main program.


For more Automotive Interview Questions and CAN INTERVIEW QUESTIONS click on next page






Comments

Popular posts from this blog

CAN INTERVIEW QUESTIONS

Qualifiers and Modifier in C

RTOS Real time operating system interview questions

CAN INTERVIEW QUESTIONS 2

What is UDS protocol

Memory mapping in c

TOP IOT PLATFORMS