Wednesday, July 18, 2018

C Questions And Answers – 2018A18

There are many commonly asked questions regarding C programming language. Below are some collected such question-answer examples. The questions are usually related with Turbo C IDE in windows or GCC under Linux environment [not always].

For more such examples, click C_Q&A label.

 

-------------------------------------------------------------------------------------------------

 

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.

 

-------------------------------------------------------------------------------------------------

 

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 <dir.h>

#include <dos.h>

#include <stdio.h>

#include <string.h>

#include <stdlib.h>

 

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 it’s 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

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.

 

-------------------------------------------------------------------------------------------------

 

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 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.

 

-------------------------------------------------------------------------------------------------

 

…till next post, bye-bye & take care.

No comments:

Post a Comment