/* */ Click to Join Live Class with Shankar sir Call 9798158723

C-Interview Questions level 8


Q1) Graphics Building Mouse Cursors...
In text mode the mouse cursor appears as a block, whereas in graphics mode it appears as an arrow. If we wish we can change the graphics cursor to any other shape the way Windows does.
The mouse cursor in graphics mode occupies a 16 by 16 pixel box. By highlighting or dehighlighting some of the pixels in this box we can get the desired shape. For example, the following bit-pattern
can be used to generate the cursor which looks like an hour-glass.

      1111111111111111 0000000000000000 
      1000000000000001 0000000000000000 
      1111111111111111 0000000000000000 
      1000000000000001 0000000000000000 
      0100000000000010 1000000000000001 
      0010000000000100 1100000000000011 
      0000100000010000 1111000000001111 
      0000001001000000 1111110000111111 
      0000001001000000 1111110000111111 
      0000100000010000 1111000000001111 
      0010000000000100 1100000000000011 
      0100000000000010 1000000000000001 
      1000000000000001 0000000000000000 
      1111111111111111 0000000000000000 
      1000000000000001 0000000000000000 
      1111111111111111 0000000000000000
    

Answer:- Mouse pointer bitmap Screen Mask the one's in the mouse pointer bitmap indicate that the pixel would be drawn whereas the zeros indicate that the pixel would stand erased. It is important to note that the mouse pointer bit pattern is 32 bytes long. However, while actually writing a program to change the pointer shape we need a 64 byte bit-map. This provision is made to ensure that when the cursor reaches a position on the screen where something is already written or drawn only that portion should get overwritten which is to be occupied by the mouse cursor. Of the 64 bytes the first 32 bytes contain a bit mask which is first ANDed with the screen image, and then the second 32 bytes bit mask is XORed with the screen image. The following program changes the mouse cursor in graphics mode to resemble an hour glass.
      # include "graphics.h"
      # include "dos.h"

      union REGS i, o ;
      struct SREGS s ;

      int cursor[32] =  
      { 
      /* Hour-glass screen mask */
      0x0000, 0x0000, 0x0000, 0x0000,
      0x8001, 0xc003, 0xf00f, 0xfc3f,
      0xfc3f, 0xf00f, 0xc003, 0x8001,
      0x0000, 0x0000, 0x0000, 0x0000,
      /* The mouse pointer bitmap */
      0xffff, 0x8001, 0xffff, 0x8001,
      0x4002, 0x2004, 0x1008, 0x0240,
      0x0240, 0x0810, 0x2004, 0x4002,
      0x8001, 0xffff, 0x8001, 0xffff, 
      } ;
      main( )
      { 
      int gd = DETECT, gm ;
      initgraph ( &gd, &gm, "c:\\tc\\bgi" ) ;
      if ( initmouse( ) == -1 )
      { 
      closegraph( ) ;
      printf ( "\n Mouse not installed!" ) ; 
      exit( ) ; 
      } 
      gotoxy ( 10, 1 ) ; printf ( "Press any key to exit..." ) ;
      changecursor ( cursor ) ; showmouseptr( ) ;
      getch( ) ; 
      }

      initmouse( )
      { 
      i.x.ax = 0 ; int86 ( 0x33, &i, &o ) ;
      return ( o.x.ax == 0 ? -1 : 0 ) ; 
      }
      showmouseptr( )
      { 
      i.x.ax = 1 ; int86 ( 0x33, &i, &o ) ; 
      }
      changecursor ( int *shape )
      { 
      i.x.ax = 9 ; /* service number */
      i.x.bx = 0 ; /* actual cursor position from left */
      i.x.cx = 0 ; /* actual cursor position from top */
      i.x.dx = ( unsigned ) shape ; /* offset address of pointer image*/
      segread ( &s ) ; 
      s.es = s.ds ; /* segment address of pointer */
      int86x ( 0x33, &i, &i, &s ) ; 
      }

Q2) Suppose there are three pegs labeled A, B and C. Four disks are placed on peg A. The bottom-most disk is largest, and disks go on decreasing in size with the topmost disk being smallest. The objective of the game is to move the disks from peg A to peg C, using peg B as an auxiliary peg. The rules of the game are as follows: Only one disk may be moved at a time, and it must be the top disk on one of the pegs. A larger disk should never be placed on the top of a smaller disk. Suppose we are to write a program to print out the sequence in which the disks should be moved such that all disks on peg A are finally transferred to peg C. Here it is... main( )
       main( )
      { 
      int n = 4 ;
      move ( n, 'A', 'B', 'C' ) ; 
      }

      move ( n, sp, ap, ep )
      int n ;
      char sp, ap, ep ;
      { 
      if ( n == 1 ) 
      printf ( "\n Move from %c to %c ", sp, ep ) ; 
      else
      { 
      move ( n - 1, sp, ep, ap ) ;
      move ( 1, sp, ' ', ep ) ;
      move ( n - 1, ap, sp, ep ) ; 
      } 
      } 
      And here is the output...

      Move from A to B
      Move from A to C
      Move from B to C
      Move from A to B
      Move from C to A
      Move from C to B
      Move from A to B
      Move from A to C
      Move from B to C
      Move from B to A
      Move from C to A
      Move from B to C
      Move from A to B
      Move from A to C
      Move from B to C

      

Answer:- This problem is the famous Towers of Hanoi problem, wherein three pegs are to be employed for transferring the disks with the given criteria. Here's how we go about it. We have three pegs: the starting peg, sp, the auxiliary peg ap, and the ending peg, ep, where the disks must finally be. First, using the ending peg as an auxiliary or supporting peg, we transfer all but the last disk to ap. Next the last disk is moved from sp to ep. Now, using sp as the supporting peg, all the disks are moved from ap to ep. ‘A’, B and C denote the three pegs. The recursive function move( ) is called with different combinations of these pegs as starting, auxiliary and ending pegs.
Q3) What would be the output of following program?
 struct syntax
      {
      int i ;
      float g ;
      char c ;
      }
      main( )
      {
      printf ( "I won't give you any error" ) ;
      }
 

Answer:- The above program would get compiled successfully and on execution it would print the message given in printf(). What strikes in the above code snippet is the structure syntax which is declared but not terminated with the statement terminator, the semicolon. The compiler would not give any error message for it, as it assumes that main( ) function have a return type of struct syntax and hence would successfully compile and execute the program.
Q4) How to get the memory size ?

Answer:- Consider the following program
 #include 
      #include 
      main( )
      {
      int memsize;
      memsize = biosmemory( ) ; 
      printf ( "RAM size = %dK\n",memsize ) ;
      return 0 ;
      }
 

The function biosmemory uses BIOS interrupt 0x12 to return the size of memory.

Q5) Float Format How does C compiler stores float values ?

Answer:- In C, the float values are stored in a mantissa and exponent form. While writing a number we specify the exponent part in the form of base 10. But, in case of C compiler, the exponent for floats is stored in the form of base 2. Obviously, because, computer stores the numbers in binary form. The C compiler follows an IEEE standard to store a float. The IEEE format expresses a floating-point number in a binary form known as `normalized' form. Normalization involves adjusting the exponent so that the "binary point" (the binary analog of the decimal point) in the mantissa always lies to the right of most significant nonzero digit. In binary representation, this means that the most significant digit of the mantissa is always a 1. This property of the normalized representation is exploited by the IEEE format when storing the mantissa. Let us consider an example of generating the normalized form of a floating point number. Suppose we want to represent the decimal number 5.375. Its binary equivalent can be obtained as shown below: 2 | 5 .375 x 2 = 0.750 0 |------ .750 x 2 = 1.500 1 2 | 2 1 .500 x 2 = 1.000 1 |------ 2 | 1 0 |------ | 0 1 Writing remainders in reverse writing whole parts in the same order we get 101 order in which they are obtained we get 011 thus the binary equivalent of 5.375 would be 101.011. The normalized form of this binary number is obtained by adjusting the exponent until the decimal point is to the right of most significant 1. In this case the result is 1.01011 x 22. The IEEE format for floating point storage uses a sign bit, a mantissa and an exponent for representing the power of 2. The sign bit denotes the sign of the number: a 0 represents a positive value and a 1 denotes a negative value. The mantissa is represented in binary. Converting the floating-point number to its normalized form results in a mantissa whose most significant digit is always 1. The IEEE format takes advantage of this by not storing this bit at all. The exponent is an integer stored in unsigned binary format after adding a positive integer bias. This ensures that the stored exponent is always positive. The value of the bias is 127 for floats and 1023 for doubles. Thus, 1.01011 x 22 is represented as shown below: --- --------------- ---------------------------------------------- | 0 | 100 0000 1 | 010 1100 0000 0000 0000 0000 | --- ---------------- --------------------------------------------- sign bit exponent- mantissa stored in normalized form obtained after adding a bias 127 to exponent 2 Data Structures Which is the best sorting method? Ans: There is no sorting method that is universally superior to all others. The programmer must carefully examine the problem and the desired results before deciding the particular sorting method. Some of the sorting methods are given below: Bubble sort : When a file containing records is to be sorted then Bubble sort is the best sorting method when sorting by address is used. Bsort : It can be recommended if the input to the file is known to be nearly sorted. Meansort : It can be recommended only for input known to be very nearly sorted. Quick Sort : In the virtual memory environment, where pages of data are constantly being swapped back and forth between external and internal storage. In practical situations, quick sort is often the fastest available because of its low overhead and its average behavior. Heap sort : Generally used for sorting of complete binary tree. Simple insertion sort and straight selection sort : Both are more efficient than bubble sort. Selection sort is recommended for small files when records are large and for reverse situation insertion sort is recommended. The heap sort and quick sort are both more efficient than insertion or selection for large number of data. Shell sort : It is recommended for moderately sized files of several hundred elements. Radix sort : It is reasonably efficient if the number of digits in the keys is not too large.
Q6) Calculating Wasted Bytes On Disk When a file gets stored on the disk, at a time DOS allocates one cluster for it. A cluster is nothing but a group of sectors. However, since all file sizes cannot be expected to be a multiple of 512 bytes, when a file gets stored often part of the cluster remains unoccupied. This space goes waste unless the file size grows to occupy these wasted bytes. The following program finds out how much space is wasted for all files in all the directories of the current drive.
      #include 
      #include 
      #include 
      #include 
      #include 
      unsigned bytes_per_cluster ;
      unsigned long wasted_bytes ;
      unsigned long num_files = 0 ;
      main( )
      { 
      int ptr = 0, flag = 0, first = 0 ;
      struct ffblk f[50] ;
      struct dfree free ;
      /* get cluster information and calculate bytes per cluster */
      getdfree ( 0, &free ) ;
      bytes_per_cluster = free.df_bsec * free.df_sclus ;
      chdir ( "\\" ) ;
      /* check out files in root directory first */
      cal_waste( ) ;
      /* loop until all directories scanned */
      while ( ptr != -1 )
      { 
      /* should I do a findfirst or a findnext? */
      if ( first == 0 ) 
      flag = findfirst ( "*.*", &f[ptr], FA_DIREC ) ; 
      else 
      flag = findnext ( &f[ptr] ) ; 
      while ( flag == 0 )
      { 
      /* make sure its a directory and skip over . & .. entries */
      if ( f[ptr].ff_attrib == FA_DIREC && f[ptr].ff_name[0] != '.' )
      { 
      flag = chdir ( f[ptr].ff_name ) ; /* try changing directories */
      if ( flag == 0 ) /* did change dir work? */
      { 
      cal_waste( ) ;
      first = 0 ; /* set for findfirst on next pass */
      break ; 
      } 
      } 
      flag = findnext ( &f[ptr] ) ; /* search for more dirs */ 
      } 
      if ( flag != 0 || ptr == 49 ) /* didn't find any more dirs */
      { 
      ptr-- ;
      chdir ( ".." ) ; /* go back one level */
      first = 1 ; /* set to findnext on next pass */ 
      }
      else 
      ptr++ ; 
      } 
      printf ( "There are %lu bytes wasted in %lu files.\n", wasted_bytes, 
      num_files ) ; 
      }
      cal_waste( )
      { 
      int flag = 0 ;
      long full_cluster ;
      struct ffblk ff ;
      /* look for all file types */
      flag = findfirst ( "*.*", &ff, FA_RDONLY | FA_HIDDEN | FA_SYSTEM | FA_ARCH 
      ) ;
      while ( flag == 0 )
      { 
      num_files++ ;
      full_cluster = ff.ff_fsize / bytes_per_cluster * bytes_per_cluster ;
      wasted_bytes += bytes_per_cluster - ( ff.ff_fsize - full_cluster ) ;
      flag = findnext ( &ff ) ; 
      } 	
      }

Data Structures

Polish Notation

Answer:- The method of writing all operators either before their operation, or after them, is called Polish notation, in honor of its discoverer, the Polish mathematician Jan Lukasiewicz. When the operators are written before their operands, it is called the prefix form. When the operators come after their operands. It is called the postfix form, or, sometimes reverse Polish form or suffix form. In this context, it is customary to use the coined phrase infix form to denote the usual custom of writing binary operators between their operands. For example, the expression A + B becomes +AB in prefix form and AB+ in postfix form. In the expression A + B x C, the multiplication is done first, so we convert it first, obtaining first A + ( BCx ) and then ABCx+ in postfix form. The prefix form of this expression is +A x BC. The prefix and postfix forms are not related by taking mirror images or other such simple transformation. Also all parentheses have been omitted in the Polish forms.
Q7) The Longjmp And Setjmp The C programming language does not let you nest functions. You cannot write a function definition inside another function definition, as in:
      int fun1( ) 
      {  
      int fun2() /* such nesting of functions is not allowed */ 
      {  
      ..... 
      } 
      }
Because of this restriction it is not possible to hide function names inside a hierarchy. As a result all the functions that you declare within a program are visible to each other. This of course is not a major drawback since one can limit visibility by grouping functions within separate C source files that belong to different logical units of the program. C does, however, suffer in another way because of this design decision. It provides no easy way to transfer control out of a function except by returning to the expression that called the function. For the vast majority of function calls, that is a desirable limitation. You want the discipline of nested function calls and returns to help you understand flow of control through a program. Nevertheless, on some occasions that discipline is too restrictive. The program is sometimes easier to write, and to understand, if you can jump out of one or more function invocations at a single stroke. You want to bypass the normal function returns and transfer control to somewhere in an earlier function invocation. For example, you may want to return to execute some code for error recovery no matter where an error is detected in your application. The setjmp and the longjmp functions provide the tools to accomplish this. The setjmp function saves the "state" or the "context" of the process and the longjmp uses the saved context to revert to a previous point in the program. What is the context of the process? In general, the context of a process refers to information that enables you to reconstruct exactly the way the process is at a particular point in its flow of execution. In C program the relevant information includes quantities such as values of SP, SS, FLAGS, CS, IP, BP, DI, ES, SI and DS registers. To save this information Turbo C uses the following structure, which is defined, in the header file 'setjmp.h'. typedef struct
      
      { 
      unsigned j_sp ;
      unsigned j_ss ;
      unsigned j_flag ;
      unsigned j_cs ;
      unsigned j_ip ;
      unsigned j_bp ;
      unsigned j_di ;
      unsigned j_es ;
      unsigned j_si ;
      unsigned j_ds ; 
      } jmp_buf[1] ; 
      

Answer:- This is a system-dependent data type because different systems might require different amounts of information to capture the context of a process. In Turbo C, jmp_buf is simply an array of ten 2-byte integers. To understand the mechanics of setjmp and longjmp, look at the following code fragment.
      
      #include "setjmp.h"
      jmp_buf buf ;
      main( )
      { 
      if ( setjmp ( buf ) == 0 ) 
      process( ) ; 
      else 
      handle_error( ) ; /* executed when longjmp is called */ 
      }
      process( )
      { 
      int flag = 0 ;
      /* some processing is done here */
      /* if an error occurs during processing flag is set up */ 
      if ( flag )  
      longjmp ( buf, 1 ) ; 
      }
Upon entry to setjmp the stack contains the address of the buffer buf and the address of the if statement in the main function, to which setjmp will return. The setjmp function copies this return address as well as the current values of registers, SP, SS, FLAGS, BP, DI, ES, SI and DS, into the buffer buf. Then setjmp returns with a zero. In this case, the if statement is satisfied and the process( ) function is called. If something goes wrong in process( ) (indicated by the flag variable), we call longjmp with two arguments: the first is the buffer that contains the context to which we will return. When the stack reverts back to this saved state, and the return statement in longjmp is executed, it will be as if we were returning from the call to setjmp, which originally saved the buffer buf. The second argument to longjmp specifies the return value to be used during this return. It should be other than zero so that in the if statement we can tell whether the return is induced by a longjmp. The setjmp/longjmp combination enables you to jump unconditionally from one C function to another without using the conventional return statements. Essentially, setjmp marks the destination of the jump and longjmp is a non-local goto that executes the jump. Data Structures Comparison Trees... The comparison trees also called decision tree or search tree of an algorithm, is obtained by tracing through the actions of the algorithm, representing each comparison of keys by a vertex of the tree (which we draw as a circle). Inside the circle we put the index of the key against which we are comparing the target key. Branches (lines) drawn down from the circle represent the possible outcomes of the comparison and are labeled accordingly. When the algorithm terminates, we put either F (for failure) or the location where the target is found at the end of the appropriate branch, which we call a leaf, and draw as a square. Leaves are also sometimes called end vertices or external vertices of the tree. The remaining vertices are called the internal vertices of the tree. The comparison tree for sequential search is especially simple.
Q8) Suppose we have a floating-point number with higher precision say 12.126487687 and we wish it to be printed with only precision up to two decimal places. How can I do this?

Answer:- This can achieved through the use of suppression char '*' in the format string of printf( ) which is shown in the following program.
     main( )
      {
      int  p  =  2 ;
      float  n  =  12.126487687 ;
      printf ( "%.*f",p, n ) ;
      }
      
Q9) Spawning All programs that we execute from DOS prompt can be thought of as children of COMMAND.COM. Thus, the program that we execute is a child process, whereas COMMAND.COM running in memory is its parent. The process of a parent process giving birth to a child process is known as 'spawning'. If the spawned program so desires, it may in turn spawn children of its own, which then execute and return control to their parent. Who is the parent of COMMAND.COM? COMMAND.COM itself. We can trace the ancestors of our program using the field Parent Process ID (PID) present at offset 0x16 in the Program Segment Prefix (PSP). To trace this ancestry our program should first locate its PSP, extract the parent process ID from it and then use this to find PSP of the parent. This process can be repeated till we reach COMMAND.COM (process ID of COMMAND.COM is its own PSP), the father of all processes. Here is a program which achieves this...
      /* SPAWN.C */
      #include "dos.h"

      unsigned oldpsp, newpsp, far *eb_seg, i ;
      char far *eb_ptr ;

      main( )
      { 
      oldpsp = _psp ;

      while ( 1 )
      { 
      printf ( "\n" ) ;
      printname ( oldpsp ) ;
      printf ( " spawned by " ) ;

      newpsp = * ( ( unsigned far * ) MK_FP ( oldpsp, 0x16 ) ) ;

      if ( * ( ( unsigned * ) MK_FP ( newpsp, 0x16 ) ) == newpsp ) 
      break ; 
      else 
      oldpsp = newpsp ; 

      printname ( newpsp ) ; 
      } 

      printf ( "%-20s (%04X)", "COMMAND.COM", newpsp ) ; 
      }

      printname ( unsigned lpsp )
      { 
      char drive[5], dir[68], name[13], ext[5] ;

      eb_seg = ( unsigned far * ) MK_FP ( lpsp, 0x2C ) ;
      eb_ptr = MK_FP ( *eb_seg, 0 ) ;

      i = 0 ;
      while ( 1 )
      { 
      if ( eb_ptr[i] == 0 )
      { 
      if ( eb_ptr[i + 1] == 0 && eb_ptr[i + 2] == 1 )
      { 
      i += 4 ;
      break ; 
      } 
      }
      i++ ; 
      }

      fnsplit ( eb_ptr + i, drive, dir, name, ext ) ;
      strcat ( name, ext ) ;
      printf ( "%-20s (%04X)", name, oldpsp ) ; 
      } 
      

Answer:- On running the program from within TC the output obtained is shown below. SPWAN.EXE (58A9) spawned by TC.EXE (0672) TC.EXE (0672) spawned by COMMAND.COM (05B8). The program simply copies its own process ID in the variable oldpsp and then uses it to extract its own filename from its environment block. This is done by the function printname( ). The value in oldpsp is then used to retrieve the parent's PID in newpsp. From there the program loops reporting the values of oldpsp, newpsp and the corresponding file names until the program reaches COMMAND.COM. The printname( ) function first locates the environment block of the program and then extracts the file name from the environment block. The fnsplit( ) function has been used to eliminate the path present prior to the file name. Do not run the program from command line since it would give you only one level of ancestry. Data Structures Choosing the data structures to be used for information retrieval. For problems of information retrieval, consider the size, number, and location of the records along with the type and structure of the keys while choosing the data structures to be used. For small records, high-speed internal memory will be used, and binary search trees will likely prove adequate. For information retrieval from disk files, methods employing multiway branching, such as trees, B-trees , and hash tables, will usually be superior. Tries are particularly suited to applications where the keys are structured as a sequence of symbols and where the set of keys is relatively dense in the set of all possible keys. For other applications, methods that treat the key as a single unit will often prove superior. B-trees, together with various generalization and extensions, can be usefully applied to many problems concerned with external information retrieval.
Q10) Variably Dimensioned Arrays While dealing with Scientific or Engineering problems one is often required to make use of multi-dimensioned array. However, when it comes to passing multidimensional arrays to a function C is found wanting. This is because the C compiler wants to know the size of all but the first dimension of any array passed to a function. For instance, we can define a function compute ( int n, float x[] ), but not compute ( int n, x[][]). Thus, C can deal with variably dimensioned 1-D arrays, but when an array has more than one dimension, the C compiler has to know the size of the last dimensions expressed as a constant. This problem has long been recognized, and some of the solutions that are often used are: Declare the arrays in the functions to be big enough to tackle all possible situations. This can lead to a wastage of lot of precious memory in most cases. Another solution is to construct multiple-dimension array as an array of pointers. For example, a matrix (2-D array) of floats can be declared as a 1-D array of float pointers, with each element pointing to an array of floats. The problem with this method is that the calling function has to define all arrays in this fashion. This means that any other computations done on the arrays must take this special structure into account. Another easy solution, though seldom used, exists. This is based on the following method: Pass the array to the function as though it is a pointer to an array of floats (or the appropriate data type) , no matter how many dimensions the array actually has, along with the dimensions of the array. Reference individual array elements as offsets from this pointer. Write your algorithm so that array elements are accessed in storage order. The following program for multiplying two matrices illustrates this procedure.
      # define M 3
      # define N 2
      # define P 4

      float a[M][N], b[N][P], c[M][P] ;
      void mulmat ( int, int, int, float*, float*, float* ) ;

      main( )
      {
      int i, j ;
      for ( i = 0 ; i < M ; i++ )
      for ( j = 0 ; j < N ; j++ )
      a[i][j] = i + j ;

      for ( i = 0 ; i < N ; i++ )
      for ( j = 0 ; j < P ; j++ )
      b[i][j] = i + j ;

      mulmat ( M, N, P, a, b, c ) ;
      for ( i = 0 ; i < M ; i++ )
      {
      printf ( "\n" ) ;
      for ( j = 0 ; j < P ; j++ )
      printf ( "%f\t", c[i][j] ) ;
      }
      }

      void mulmat ( int m, int n, int p, float *a, float *b, float *c )
      {
      float *ptrtob, *ptrtoc ;
      int i, j, k, nc ;

      /* set all elements of matrix c to 0 */
      for ( i = 0 ; i < m * p ; i++ )
      *( c + i ) = 0 ;

      for ( i = 0 ; i < m ; i++ )
      {
      ptrtob = b ;
      for ( k = 0 ; k < n ; k++ )
      {
      ptrtoc = c ;

      for ( j = 0 ; j < p ; j++ )
      *ptrtoc++ += *a * *ptrtob++ ;
      a++ ;
      }
      c += p ;
      }
      }
       

Answer:- We know that C stores array elements in a row-major order. Hence to ensure that the elements are accessed in the storage order the above program uses a variation of the normal matrix-multiplication procedure. The pseudo code for this is given below:
      for i = 1 to m 
      for j = 1 to p
      c[i][j] = 0
      end
      for k = 1 to n
      for j = 1 to p
      c[i][j] = c[i][j] + a[i][k] * b[k][j]
      end
      end
      end