-
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] ;
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. -
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?
Ans. 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 ) ;
} -
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 0×16 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, 0×16 ) ) ;
if ( * ( ( unsigned * ) MK_FP ( newpsp, 0×16 ) ) == 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 ) ;
}
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.
You Are Here: Home » C and C++, Technical Interview Questions » C C++ Programmer Technical test questions