The greatest mistake you can make in life is to be continually fearing you will make one
Showing posts with label C Guide. Show all posts
Showing posts with label C Guide. Show all posts

Monday, 21 June 2010

String

String:
  • Strings are character array.
  • Character array are stored in contiguous memory location.
String Constant:
  • A string constant is a one-Dimensional array of characters terminated by a null character('\0').
  • Each character in the array occupies one byte of memory and the last character is always '\0'
    Example:
        char str[]={'C','P','U','\0'};
  • The terminating null character('\0') is very important in a string because its the only way for the compiler to know where the string ends.
Null Character('\0'):
  • '\0' is called null character.
  • '\0' and 0 are not same because both has different ASCII value. ASCII value of '\0' is 0 but ASCII value of 0 is 48.
String Initialization:
  • A string can be initialized without adding the terminating null character as,

        char str[]="WELCOME";

    Here C inserts the NULL character automatically.
Accessing Character array elements:
  • Similar to integer array we can access array elements  
    #include< stdio.h >
    int main()
    {
        int i=0;
        char str[]="WELCOME";
        while(str[i]!='\0')
        {
            printf("%c",str[i]);
            i++;
        }
        return;
    }
  • This can be done by using pointer also. Mentioning th name of the array we get the base address(zeroth element) of the array.
    #include< stdio.h >
    int main()
    {
        char str[]="WELCOME",*ptr;
        ptr=str;
        while(*ptr!='\0')
        {
            printf("%c",*ptr);
            ptr++;
        }
        return;
    }
Reading a string from a keyboard:
  • For reading and writting a string the format specifier is %s and no & is needed
    #include< stdio.h >
    int main()
    {
        char str[30];
        printf("Enter the string\n");
        scanf("%s",str);
        printf("String=%s",str);
        return;
    }

    The scanf() function fills in the character typed at keyboard into the str[] array until the blank space or enter key is hit. If blank space or enter key is hit scanf() automatically place a '\0' in the array.
  • Normally the %s will read a string upto a blank space. To read a muti word string untill the end of line including blank space use %[^\n]s
    #include< stdio.h >
    int main()
    {
        char str[100];
        printf("ENter the string\n");
        scanf("%[^\n]s", str);
        printf("String=%s",str);
        return;
    }

    Output:
    Enter the string
    welcome honey
    String=Welcome honey
  • Other way of reading multiword string is gets() and puts(). But it is dangerous to use gets() in programs so try to avoid gets()
    #include< stdio.h >
    int main()
    {
        char str[30];
        printf("Enter the string\n");
        gets(str);
        puts(str);
        return;
    }
Pointer and String:
  • A string can be stored in 2 forms

    1. char str[]="Welcome"; //here Welcome is stored in a location called str
    2. char *str="Welcome"; //here Welcome is stored in some other location i memory and assign the address of the string in a char pointer
  • The advantage of pointing a string using a character pointer is we cannot assign a string to another string but we can assign a char pointer to another char pointer
    #include< stdio.h >
    int main()
    {
        char str1[]="Welcome",str2[30];
        char *ptr1="Welcome",*ptr2;
        //str2=str1; //error
        ptr2=ptr1;
        printf("ptr2=%s",ptr2);
        return;
    }
  • Once a string has been defined it cannot be initialized to another set of characters but using char pointer we can redefine the string.
    #include< stdio.h >
    int main()
    {
        char str1[]="Welcome";
        char *ptr1="Welcome";
    //  str1="Good";//error
        ptr1="Good";
        printf("ptr1=%s",ptr1);
        return;
    }

     
Points to consider in Strings:
  1. The size of the string depends on the size of declaration not the number of characters in the string
    #include < stdio.h>
    void main()
    {
        char str[30]="Network programming";
        printf("%d",sizeof(str));
    }

    The output here is 30 not 1

Saturday, 29 May 2010

Pointers In C

Pointers:
     Pointer is a variable contain the address of another variable. If a variable contains address of another variable than it is said that first variable points to second. All operation perform on pointers are done through two operators '*' and '&'. '&' is a unary operator that returns a memory address of a variable. '*' is complement of '&' and return value stored at a memory location stored in a pointer. '*' can interpreted as statement "at address" while '&' can be interpreted as statement "address of".

Pointer Declaration:
      Declaring a pointer variable is quite similar to declaring a normal variable all you have to do is to insert a star '*' operator before it.General form of pointer declaration is -
datatype* name;
where datatype represent the type of data  to which pointer thinks it is pointing to.
Multiple pointers of similar type can be declared in one statement but make sure you use * before every one otherwise they will become a variable of that type.
Example:
    int *p;
    float *f1,*f2;
    char *ch;

Pointer Assignment: 
     Once we declare a pointer variable we must point it to a value by assigning the address of the variable 
Example:

    int *p;
    int x;
    p=&x;

     The value of one pointer can be asssigned to another pointer using assignment operator '=' . In this value of right hand side points to memory address of variable stored in left hand side pointer. As a result both pointers point to same memory location after this expression.
Pointer of similar type can be used in expression easily as shown below but for diffrent type pointers you need to type cast them as shown in next section.
#include < stdio.h >
int main ()
{
char ch = 'x';
char *c1, *c2;
c1 = &ch;
c2 = c1; // Pointer Assignement Taking Place
printf (" *c1 = %c And *c2 = %c", *c1,*c2); // Prints 'x' twice
return 0;
}

Pointer Conversion
       Before concept of pointer conversion you must understand the concept of a void pointer. Void pointer technically is a pointer which is pointing to the unknown. Void pointer has special property that it can be type casted into anyother pointer without any type casting though every other conversion needs an type casting. In dynamic memory allocation function such as malloc ( ) and calloc ( ) returns void pointer which can be easily converted to other types.
       Also there is a pointer called null pointer which seems like void pointer but is entirely diffrent. Null pointer is a pointer which points to nothing. Null pointer points to the base address of the CPU register and since register is not addressable usage of a null pointer will lead to crash or at minimum a segmentation fault.
       Also be careful while typecasting one pointer to another because even after type casting your pointer can point to anything but it will still think it is pointing to something of it declared type and have properties of the orignal type.
      Type conversion is a powerful feature but yet it may lead difficult to remove bugs and crashes,it may also lead to unexpected and unreliable results but program would compile succesfully.
Code below shows a type casting of one pointer into another -
#include < stdio.h >
int main ()
{
int x=10;
char *ch;
int *p;
p = &x;
ch = (char *) p; // Type Casting and Pointer Conversion
printf (" *ch = %c And *p = %d", *ch,*p); // Output maybe unexpected depending on the compiler.
return 0;
}

Pointer Expressions:
    Like normal variables pointer variable can be used in expressions.
Example:consider 2 integer pointers p1,p2
    int *p1,*p2;
    int x,y,z,k;
    p1=&x;
    p2=&y;
    z=*p1**p2;
    k=k+*p1;
    z=10**p2/*p1;



Pointer Arithmetic
     Pointer arithemetic is quite diffrent from normal arithemetic. Not all artihemetic operations are defined in pointers. You can increment them, decrement them, add and subtract integer values from them. You even can subtract two pointers.But you cannot add two pointers, mulitply, divide,modulus them. You can not also add or subtract values other than integer.

Address+Number=Address
Address-Number=Address
Address++=Address
Address--=Address
     Now consider a pointer X , its current value that address it is pointing to is 1000 (just assuming).We make another assumption about the size of the data types. Size of data type is machine dependent, for example int can be 2,4 byte depending upon the compiler.
Now if this X pointer is char type(assumed 1 Byte ) then X++ will have value 1001 and X-- will have value 999. Now if this X pointer is integer type (assumed 2 byte) then X++ will have value 1002 and X-- will have value 998. Again if this X pointer is float type (assumed 4 Byte) than X++ will have value 1004 and X-- will have value 996. Also if this X pointer is double type(assumed 8 Byte ) than X++ will have value 1008 and X-- will have value 992.
     when you increment a pointer of certain base type it increase it value in such a way that it points to next element of its base type. If you decrement a pointer its value decrease in such a way that it points to previous value of its base type.
      You can add or subtract any integer value, in such case value of pointer get increase and decrease by the product of the value to be added or subtract and size of the base type. Pointer of user defined types such as structures and union also increase by the quantity of thier bit values which can be determined using sizeof operator.

Pointer Comparison:
         Two pointers can be compared no matter where they point. Comparison can be done using <, >, =, <= and >= operators. Though it is not forcibly implied but comparison of two pointers become sensible only when they are related such as when they are pointing to element of same arrays.Comparison of two unrelated pointers is unpredictable and your code should not rely upon it. All comparison are generally done on basis of memory organization in the host machine.
Following C source code shows pointer comparison in C -
#include < stdio.h >
int main ()
{
int data[10],i;
int* p1,*p2;
for (i = 0; i <10;i++)
{
data[i] = i;
}
p1 = &data [1];
p2 = &data [2];
if (p1 > p2)
{
printf ("p1 is greater than p2\n");
}
else
{
printf ("p2 is greater than p1\n");
}
}
output:p2 is greater than p1

Sunday, 23 May 2010

Assertion in C

An assertion is a 'conditional check' placed in a program to indicate that the developer thinks that the condtion has to be always true, at that place, for the program to work properly after that point. If the condition happens to be false, the program execution would be abruptly abhorted.

For example:


    assert (y!=0); /* The developer thinks that y shouldn't be equal to 0. If not, the code would get abhorted */
    z=x/y;
Whenever the program gets aborted because of an assertion, a corefile would be dumped. The developer can analyze the corefile (using any debugger, for example 'GDB') and pin-point the exact location of the failure, and then figure out why something unexpected happened. Thus assertions can be very useful for the developer during the early stages of development of a program.

Note that assertions would be enabled only during development and early stages of testing; they would be normally disabled during final testing. Assertions would always be disbaled when the final product is delivered to the customer.

How assertions are different from error handling

Assertions should be used to document logically impossible situations and discover programming errors— if the "impossible" occurs, then something fundamental is clearly wrong. This is distinct from error handling: most error conditions are possible, although some may be extremely unlikely to occur in practice. Using assertions as a general-purpose error handling mechanism is unwise: assertions do not allow for recovery from errors; an assertion failure will normally halt the program's execution abruptly. Assertions also do not display a user-friendly error message.

What is assert()?
Assert() is a macro defined in assert.h, as follows
        void assert (int)
If the value (normally the result of the expression) passed is non-zero, nothing happens. However if the value happens to be zero, it prints an error message similar to the following one to STDERR and aborts the program

Assertion Failed: file line :

Normal uses
The simplest and most effective use of assertion is to specify and check input conditions to functions such as to check
  • Pointers are not null
  • Indexes and size values are non-negative and less than a known limit
Turning assertions off:
  • By default,ANCI C compilers generate code to check assertions at run time.
  • Assertion checking can be turned off by defining the NDEBUG flag to the compiler.
  • This can be done either by inserting #define NDEBUG in a heder file such as stdhdr.h or while compiling use the option -dNDEBUG as
          gcc -dNDEBUG filename

Example

#include <stdio.h>
#include <assert.h >
int main()
{
    int x,y,z;
    printf("Enter the 2 numbers to divide\n");
    scanf("%d %d",&x,&y);
    assert(y!=0);
    z=x/y;
    printf("x/y=%d\n",z);
    return;
}

Output:
Enter the 2 numbers to divide
3 0
Sample1: assert_test.c:8: main: Assertion `y!=0' failed.
Aborted

In this program if the user provides the value 0 for y, then assert() would get triggered and will abort the program.

Best Practice:
 The following code

    assert(y!=0);
    z=x/y;
can be better written along with a string indicating what is the reason for the assertion, as

    assert(y!=0 && "y (denominator) cannot be zero");
    z=x/y;

Question: Whats wrong with this code

#include <stdio.h>
#include <assert.h>

void main () {
    char *my_str[]={"This", "is", "a", "string"};
    int index=0;
    while (index<4) {
        assert (++index && my_str[index-1]);
        printf("%s ", my_str[index-1]);
    }
    printf("\n");
}

Answer: When the code is compiled with assertions deisabled, index variable wouldn't get incremented and would result in an infinite loop. So never write any real code within an assert statement

Tuesday, 18 May 2010

Command Line Arguments

  • Getting the arguments from command prompt in c is known as command line arguments.
  • Command line is that it consists of a sequence of words,typically separated by whitespace.Main program can receive these words as an array of strings,one word per string.
  • main function will accept 2 parameters ,argv and argc
  • argc will be the count of the number of strings given on the command line.
  • argv will be the array of the arguments.since each word is a string argv is an array of pointers to char
  • Example:
        int main(int argc,char *argv[]){......}
  • The strings at the command line are stored in memory and address of the first string is stored in argv[0],address of the second string is stored in argv[1] and so on.
  • we can give any name instead of argv and argc
    Example:
       main(int count,char*str[]){.....}
  • Example Program
    #include<stdio.h>
    int main(int argc,char *argv[])
    {
        int i;
        printf("Number of Arguments passed=%d\n",argc);
        for(i=0;i<argc;i++)
            printf("argv[%d]=%s\n",i,argv[i]);
        return;
    }
    Output
    sunitha@sunitha-laptop:~/ds$ ./a.out g h k
    Number of Arguments passed=4
    argv[0]=./a.out
    argv[1]=g
    argv[2]=h
    argv[3]=k
Command Line Arguments  in Unix/Linux:
  • Most Unix/Linux applications lets the user to specify command-line arguments (parameters) in 2 forms

      * Short options
      consist of a - character followed by a single alphanumeric character
      for example for listing the files the command is ls -l
      * Long options (common in GNU applications)
      consists of two - characters (--)  followed by a string made up of letters, numbers, and hyphens.
  • Either type of option may be followed by an argument.
  • A space separates a short option from its arguments
  • Either a space or an = separates a long option from an argument.
  • GNU C provides 2 functions, getopt and getopt_long to parse the command line args specified in the above format
  • getopt - supports parsing only short options
  • getopt_long - supports parsing of both short and long options.

    In this blog, we would focus only on getopt_long function to parse both long and short options.
 getopt_long()
  • This function is defined in getopt.h

    Syntax:
    -------
    int *getopt_long* (int argc, char *const *argv, const char *shortopts, const struct option *longopts, int *indexptr)

    Usage:
    ------
     while (getopt_long (argc, argv, shortopts, longopts, index) != -1) {
     }

    Parameters
    ----------
    argc and argv are the command line args passed to main
    shortopts:  This is a string containing all the valid short option characters
               An option character can be followed by
                   a colon (:)  to indicate that it takes a required argument. (or)
                   2 colon (::) to indicate that its argument is optional                                                              shortopts can begin with a + to indicate that getopts_long should stop processing on sensing an unexpected arg
               Ex: If we have have a program that can accept the short args -a and -b, the option string would be "ab"
                   Now, if we have to force a mandatory required parameter for -a, but not for -b, the option string would be "a:b::"

    longopts: This is a array of structure, containing the following fields
                   char * , int , int* , int
    long option string  - The command line parameter in long format
                       type                - one of no_argument, required_argument or optional_argument
                       flag                - Set it to null for non-flag parameters
                                             For flag, set it to the address of an int, which should get set when the flag is mentioned in the cli
                       val                 - For flags, set it to the default value the flag should be initialized to
                                             For non-flags, set it to a number, that would uniquely identify this option
               The array has to be terminated with all zeros

            

Wednesday, 17 March 2010

Common Problems with malloc() and calloc() functions in C

1) Forgetting to initialize the allocated memory (malloc only)
Example:
int p=malloc((sizeof (int))*5);
p[0]++;

Solution:
The memory allocated using malloc() function contains garbage values.Initialize malloc-ed memory using memset
int *p=malloc((sizeof (int))*5);
memset(p,0, (sizeof (int))*5);
p[0]++;

2) Using more memory than allocated memory
Example:
int *p=calloc((sizeof (int)),5);
p[5]++;

3. Forgetting to allocate memory for storing the null character when using strings
Example
char *p=calloc((sizeof (char)),5);
strcpy(p,"NULL?");

Solution:
char *p=calloc((sizeof (char)),6);
strcpy(p,"NULL?");

4. Referring de-allocated memory (free-d memory)
Example:
int *p=calloc((sizeof (int)),5);
for (i=0;i < 5;i++)
     p[i]=i;
free(p);
...
...
*p=1;

Solution:
The line "*p=1;" could trigger a "Segmentation fault" error during run-time. But not guarenteed always.
This can be hard to trouble-shoot; So it is always a good idea to re-initialize the pointer to NULL after freeing

int *p=calloc((sizeof (int)),5);
for (i=0;i<5;i++) p[i]=i;
free(p);
p=NULL;
...
...
*p=1; == > This always triggers a "Segmentation fault" error during run-time

5. Double free-ing for memory
int *p1,*p2;
p1=calloc((sizeof (int)),5);
p2=p1;
free(p1);
p1=NULL;
...
...
...
*p2=0; // Can result in "Segmentation fault";
free(p2); // Would trigget the runtime error "glibc detected ** ... : double free or corruption"

Both p1 and p2 were pointing to the same memory and called free for that memory
However p2 still points to the memory.

Soln: Remember to re-initialize the copied pointers also to NULL
int p1,*p2;
p1=calloc((sizeof (int)),5);
p2=p1;
free(p1);
p1=NULL;
p2=NULL;
...
...
...
*p2=0; // Will always result in "Segmentation fault";
free(p2); // Will always result in "Segmentation fault";

Hint: Whenever you see the error "Segmentation fault" or "glibc detected ** ... : double free or corruption" error in a "free" call, check if you have already called a free for the same memory

6. Freeing unallocated memory
Example:
The following works fine
#define NEED_MEM 1
int *p;
if (NEED_MEM) {
p=calloc((sizeof (int)),5);
}
...
...
free(p);

But the following doesn't
#define NEED_MEM 0
int *p;
if (NEED_MEM) {
p=calloc((sizeof (int)),5);
}
...
...
free(p); // May result in "Segmentation fault"

Solution:
Always initalize pointers to NULL
#define NEED_MEM 0
int *p=NULL;
if (NEED_MEM) {
p=calloc((sizeof (int)),5);
}
...
...
free(p); // No problem here... [Remember that there is nothing wrong in freeing NULL memory; free call would return without doing anything]

Tuesday, 16 March 2010

Memory Management in C

  • A memory is made up of large number of cells,where each cell is capable of storing one bit.
  • The cells may be organized as a set of addressable words,each word storing a sequence of bits
  • These addressable memory cells should be managed effectively to increase its utilization.This is called memory Management.
  • There are 2 types of memory allocations in C

    1. Static memory allocation or Compile time allocation
    2. Dynamic memory allocation or Run time allocation
1.Static or compile time memory allocation
  • The required memory is allocated to the variables at the beginning of the program.
  • The memory to be allocated is fixed and is determined by the compiler at the compile time itself.
  • Example:
        int a,b;
        float x,y[5];
    when the first statement int a,b; is compiled 4 bytes(integer occupies 4 bytes in a 32 bit machine) for each variable a,b will be allocated. The statement float x,y[5]; will allocate 4 bytes for the x variable and 20 bytes[5 element array, each has 4 bytes] for the array y.
Drawbacks:
  • If we declare more number of elements and we are using less number of elements then the memory allocated for the not used variables will be wasted.The unused memory cells are not made available to other applications.This leads to inefficient use of memory.
    Example
    main()
    {
        int x,y[10];
        x=10;
        printf("%d",x);
    }
    Here y[10] array is declared but not used in the program.So the memory allocated for the array is wasted.
  • If we try to assign values to 10 elements of an array whose size is declared as 5,then first 5 elements can be assigned and the other 5 elements cannot be assigned.
  • If we try to read 10 elements of an array whose size is declared as 5,then first 5 values are read from the array and the other 5 consequtive unknown random memory values will be read.
2.Dynamic or Runtime memory allocation
  • In most of the real time problems, we cannot predict the memory requirements.
  • It makes efficient use of memory by allocating the required amount of memory whenever is needed.
  • dynamic memory allocation does the job at run time.
  • C provides the following dynamic allocation and deallocation functions

    1. malloc()
    2. calloc()
    3. realloc()
    4. free()
    The allocation of memory is done using 3 functions malloc,calloc and realloc. deallocation is done by free function.
1.Allocating A Block of Memory-malloc()
  • The malloc() function is used to allocate a block of memory in bytes dynamically during execution.
  • This function requires the number of bytes to be allocated and returs the base address of the chunk of memory that it allocates.
  • The address returned by malloc() is always of the type void*.Hence it is required to convert in to a required data type using typecastng.
  • Syntax:
        ptr=(data-type *)malloc(block_size);
  • Example:Allocating memory space for n integers of int type
       p=(int *)malloc(n*sizeof(int));
  • The malloc() function allocates a block of contiguous bytes.The allocation can fail if the space in the heap is not sufficient to satisfy the request. If it fails,it returns a NULL pointer.So it is always better to check whether the memory allocation is successful or not before using the allocated memory.
    Example
              
    #include<stdio.h>
    int main()
    {
        int n,i,sum=0;
        int *p,*num;
        printf("Enter the number of elemets\n");
        scanf("%d",&n);
        //allocating memory space for n integers
        p=(int *)malloc(n*sizeof(int));
        //Checking whether the memory is allocated successfully or not
        if(p==NULL)
        {
            printf("Memory allocation is failed\n");
            exit(0);
        }
        //Reading the elements
        for(num=p,i=1;num<(p+n);num++,i++)
        {
            printf("\nEnter the %d element=",i);
            scanf("%d",num);
        }
        //Finding the sum of n elements
        for(num=p;num<(p+n);num++)
        {
            sum=sum+(*num);
        }
        printf(\nSum=%d",sum);
        return 0;
    }
    Output
    Enter the number of elemets
    5
    Enter the 1 element=4
    Enter the 2 element=5
    Enter the 3 element=6
    Enter the 4 element=7
    Enter the 5 element=8
    Sum=30
2.Allocating Multiple blocks of Memory-calloc()
  • calloc functionworks similar to malloc() function except it needs 2 arguments and calloc() function allocates multiple blocks of memory each of the same size and then sets all bytes to zero.
  • Syntax:
        ptr=(data-type *)calloc(n,sizeof(block_size));
    This will allocates contiguous space for 'n' blocks,each of size of block_size bytes.All bytes are initialized to zero and a pointer to the first byte of the allocated memory block is returned.
  • Example
        p=(int *)calloc(25,sizeof(int));
  • calloc(m,n) is equivalent to p=m*malloc(n);
Differnce between malloc() and calloc()
  1. malloc() allocates a bytes of memory but calloc() allocates blocks of memory.
  2. malloc() takes a single argument(memory required in bytes) but calloc() takes 2 arguments (number of variables to allocate memory,size in bytes of a single variable)
  3. The memory allocated using malloc() function contains garbage values,the memory allocated by calloc() function contains the value 0.
3.Resize the size of a Memory-realloc()
  • Sometimes we need to increase the memory space or we want to reduce the memory space.
  • To change the size of the allocated memory block realloc() function is used.
  • Syntax
        ptr=realloc(ptr,new_size);
    ptr- >pointer holding the starting address of the allocated memory block
    new_size=size in bytes
  • Example
        ptr=(int *)malloc(sizeof(int));
        ptr=(int *)realloc(ptr,sizeof(int));
4.Releasing the used memory-free()
  • free() function is used to deallocate the previously allocated memory using malloc() or calloc() function.
  • It is important to release the memory block for future use when it is not in use,using free() function.
  • Syntax
        free(ptr);
    ptr is a pointer to a memory block which has already been allocated.
  • Trying to release an invalid pointer may create problems and cause system crash.

Saturday, 13 March 2010

C Bitwise Operators

  • The programming languages are byte oriented but the hardware are bit oriented. so bit level operationas are very important when the program interact directly with the hardware.
  • C provides 6 bitwise operators for bit manipulation.
  • Bitwise operators can only applied to integral operands such as char,short,int and long. these may be signed or unsigned.
  • Bitwise operators can not be applied on floats and doubles.

Operator NameOperator Symbol
bitwise AND&
bitwise inclusive OR|
bitwise exclusive OR^
one's complement(unary)~
left shift<<
right shift>>

  • Consider the bit numbering scheme. The bits are numbered from zero onwards,increasing from right to left
  • for character,the bit representation
  •  For integer(considering int are 16 bits),the bit representation,
  • The bitwise operators are applied to binary numbers ie number consist of 0 and 1. so the integer or char(ascii value) number is converted to binary number for operation. 
  • The program for converting an decimal number to binary number is given below
  • Program to print the binary equivalent of a decimal number

    #include<stdio.h>
    int main()
    {
        int n;
        printf("Enter the number\n");
        scanf("%d",&n);
        dectobin(n);
        return 0;
    }
    int dectobin(int n)
    {
        if(n==0)
            return;
        else
        {
            dectobin(n/2);
            printf("%d",n%2);
        }
    }
1.Bitwise AND(&)
  • The & operator is a binary operator so it requires 2 operands.
  • When the AND operation is performed the 2 operands are compared on a bit by bit basis.So both the operands must be of the same data type (ie both the operands are char or int)
  • The rules for the &  operator is


    First bitSecond bitresult=First bit & Second bit
    00 0
    01 0
    10 0
    11 1
  • Example:
    consider a=10 and b=6. to find a&b
    8bit binary value of a-->0000 1010
    8bit binary value of b-->0000 0110
    result of a&b         -->0000 0010.
    Each bit of a is compared with the corresponding bit in b.If both the bits are set as 1 then the result is 1 else the result is set as 0.Thus the result of a&b=2.
    Note:The operation is performed on individual bits and the operation performed on one pair of bits is completely independent of the operation performed on the other pairs.
  • How to turn off a particular bit? The & operator is used to turn off a particular bit 
  • The & operator is often used to mask off some set of bits.The second operand is called an AND mask.ie using AND operator we can switch off a particular bit..To switch off the 3rd bit the AND mask is created with setting 0 on the 3rd bit and all remaining bit are set as 1.
    ie AND mask=1111 1011
    Example
    binay value of 12--> 0000 1100
    AND mask         --> 1111 1011
    Result           --> 0000 1000(3rd bit is now off)
  • The & operator also used to check whether a particular bit in a number is ON(1) or OFF(0).To find a particular bit for example 3rd bit is ON or OFF the AND mask is  23.ie 8. The binary value of 8 is 0000 1000.The original value is & with this AND mask. In the result if the 3rd bit is 1 then the 3rd bit in the original number is ON. if its 0 then the 3rd bit in the original number is OFF.
    Example:check whether 2nd bit of decimal number 6 is On or OFF.The AND mask is 22 ie 4(0000 0100)
    binary value of 6-->0000 0110
    AND mask         -->0000 0100
    Result is        -->0000 0100.
    The result has 3rd bit as 1. So the 3rd bit on the original number is set as ON.
2.Bitwise OR (or) Bitwise Inclusive OR( | )
  • The Bitwise OR operator is also a binary operator so it requires 2 operands.
  • The rules for the bitwise OR operator is
First bitSecond bitresult=First bit | Second bit
00 0
01 1
10 1
11 1
  • Example: consider a=10 and b=6. to find a | b
    8bit binary value of a-->0000 1010
    8bit binary value of b-->0000 0110
    result of a|b         -->0000 1110.
    Each bit of a is compared with the corresponding bit in b.If either of the bits are set as 1 then the result is 1.If both the bits are 0 then only the result is set as 0.Thus the result of a | b=13.
  • How to turn on a particular bit? Bitwise OR operator is used to turn bits ON. To turn ON a particular bittThe OR mask is created with the particular bit as 1.
    Example
    If you want to set 3rd bit as 1 in the given number 0000 0111(decimal 7) then the OR mask=23 ie 8(0000 1000). This OR mask is OR with original number 0000 0111.
    Binary value of 7-->0000 0111
    OR mask          -->0000 1000
    Result           -->0000 1111.
    if the bit is already ON the OR operation wont alter the value since 1 | 1=1. If the bit is off then the bit is set to ON.
3.Bitwise XOR (or) Bitwise Exclusive OR (^)
  • The XOR operator is a binary operator so it requires 2 operands.
  • The rules for the bitwise XOR is
First bitSecond bitresult=First bit ^ Second bit
00 0
01 1
10 1
11 0
  • XOR operator returns 1 only if one of the two bit is 1.else 0
    Example
    consider a=10 and b=6. to find a ^ b.
    8bit binary value of a-->0000 1010
    8bit binary value of b-->0000 0110

    result of a^b         -->0000 1100.
    Each bit of a is compared with the corresponding bit in b.If either of the bits are set as 1 then the result is 1.If both the bits are 0 or both the bits are 1 then the result is set as 0.Thus the result of a ^ b=12.
  • XOR operator is used to toggle a bit ON or OFF.ie if the bit is 1 set to 0 and if the bit is 0 set to 1.To toggle bits 2 and 3 the XOR mask is created by setting 1 to the 2 and 3rd bit ie 0000 1100. To toggle 0th bit the XOR mask is 0000 0001.
    Example
    To toggle first 4 bits in the decimal number 65
    The XOR mask for toggle 1st 4 bits=0000 1111
    binary value of 65      -->0100 0001
    XOR mask                -->0000 1111
    Result of 65 ^ XOR mask -->0100 1110
  • A number XORed with another number twice gives the original number.
    Exampleconsider a=20 b=10.
    binary value of a   -->0001 0100
    binary value of b   -->0000 1010
    result of a ^ b     -->0001 1110.
    now the result is again XOR with b.
    result of a^b      -->0001 1110
    binary value of b  -->0000 1010
    result of a^b^b    -->0001 0100(20 ie value of a) 
 4.Ones's Complement Operator(~)
  • The one's complement operator is a  unary operator. So it requires only one operand
  • In one's complement of a number, all 1's present in the number are changed to 0'sand all 0's are changed to 0's.
  • The rules are

    bit~bit
    01
    10
  • Example:if a =0000 0000(decimal 0) ones complement of a ie ~a= 1111 1111(decimal 255). If a=1111 1111 then ~a=0000 0000.
5.Left Shift Operator( << )
  • The left shift operator is a binary operator so it requires 2 operands namely left operand and right operand
  • Left shift operator shifts each bit in the left operand to the left. The number of places the bits are shifted depends on the right operand.
  • So A << 1 would shift all bits in A 1 place to the left.A << 3 shift all bits of A in 3 places to the left.
  • When the bits are shifted to the left ,blanks are created on the right.These blanks are always filled with zeros.
  • Example: if A=65 then binary value of A=0100 0001.
    A << 1-->1000 001*
    Now the blank is filled with 0. so A << 1 --> 1000 0010
    A << 2--> 0000 01** so A << 2-->0000 0100
    A << 3-->0000 1000
  • Note:

    1 left shifting is equivalent to multiplication of left operand by 2right operand (ie)A << B =A * 2B
    Example1:
    binary value of 32-->0010 0000
    32 << 1 -->0100 0000( decimal 64 ie 32*2)
    32 >> 2 --> 1000 0000(decimal 128 ie 32 * 22)
    Example2
    binary value of 25 -->0001 1001
    25 << 1 -->0011 0010(decimal 50 ie 25*2 )
    25 >> 2 -->0110 0100(decimal 100 ie 25*22)
 6.Right Shift Operator( >> )
  • The right shift operator is a binary operator so it requires 2 operands namely left operand and right operand
  • Right shift operator shifts each left operand to the right. The number of places the bits are shifted depends on the right operand.
  • So A >> 1 would shift all bits in A 1 place to the right.A >> 3 shift all bits of A in 3 places to the right.
  • When the bits are shifted to the right ,blanks are created on the left.These blanks are always filled with zeros.
  • Example: if A=65 then binary value of A=0100 0001.
      A >> 1-->0010 0000
      A >> 2-->0000 1000
      A >> 3-->0000 0001

  • Note:
  1. If the left operand is a multiple of 2 then shifting the operand to right is same as dividing it by 2right operand

    Example:
          binary value of 128-->1000 0000
          128 >> 1 -->0100 0000( decimal 64 ie 128/2)
          128 >> 2 --> 0010 0000(decimal 32 ie 128/22)
          128 >> 3 -->0001 0000(decimal 16 ie 128/23)
  2. If the left operand is not a multiple of 2 then shifting the operand to right is same as dividing it by 2right operand and ignoring the remainder.
      Example
      binary value of 25 -->0001 1001
      25 >> 1 -->0000 1100(decimal 12 ie 25/2 discarding remainder 1)
      25 >> 2 -->0000 0110(decimal 6 ie 25/4)

  • In A >> B, if B is negative the result is unpredictable.
  • In A >> B, if A is negative then A's left most bit ie sign bit is 1
  • Logical Shift and Arithmetic Shift:
  1. In Logical Shift the high order bits are filled with zero
  2. In Arithmetic shift, the high order bits are filled with the original sign bits. So if the sign bit is 1 then all bits are filled with 1's otherwise they are filled with 0's
  3. For unsigned data types logical shift is used,and for signed data types arithmetic shift is used.



    Friday, 12 March 2010

    C User defined data type

    User defined data type:
    1. typedef
    2. enum
    1.typedef
    • In C language a user can define an identifier that represents an existing data type.
    • The user defined datatype identifier can later be used to declare variables.
    • Syntax is
              typedef datatype identifier;
         
      here datatype represents the existing primary or derived data type. 
    •  Example:
          typedef int marks;
          Now marks can be used  to declare integer variables as
          marks mark1,mark2; 
    2.enum 
    • enum is short form of enumeration 
    • enumeration consists of a set of named integer constants. ie enumeration is a list of constant integer values.
    • Syntax for enum declaration is
              Enum identifier {value1,value2,.....value n}
    • The identifier is a user defined enumerated datatype which can be used to declare variables that have one of the values enclosed with in the braces.
    • after the declaration,we can define variables with this new type as
               enum identifier v1,v2,v3....vn
    • The enumerated variables v1,v2,v3...vn can have only one of the values value1,value2....value n.
    • Example
      declaration
      enum day {Monday,Tuesday,Wednesday,Thursday,Friday,Saturday};
      definition
      enum day day1,day2,day3;

      day1=Monday;
      day2=Friday;

      day3=Sunday;//Error because sunday is not in the enum definition
    • The first name in an enum has value 0, the next has value 1 and so on,unless explicit values are specified. 
    • In the above example Monday is assigned to 0,Tuesday is assigned to 1 and so on and Saturday is assigned to 5 internally.
    • we can also explicitly assign a value to the set
       
      enum day {Monday,Tuesday=0,Wednesday,Thursday,Friday,Saturday};
      The value 0 is associated with Monday by default.Then
      Tuesday is explicitly set to 0.so Wednesday is set to 1 and so on.
    Points to consider:
    1. To explicitly assign an integer value to a variable of an enumerated data type,use a type cast
         enum day today;
          today=(enum day)Monday;
      now today has value 0
    2.  In declaration, enum identifier {value1,value2,.....value n};value1,value2...valuen must all be distinct from each other and different from ordinary variable names,but the values need not be distinct.
          enum student1 {Anand=0,Ashok,Alex} Boys;
          enum student2 {skippy=0,slowvy,Ashok} Girls;
      The above code will create an compiler error because 'Ashok' is available in both the list.
    3. Enum is a set of named integer constant.so while printing the enum members the integer values only printed, Sometimes we need to print the actual name of the enum value for troubleshooting.But we cant directly print the enum members using %s.we can use switch statement or if-else to print the enum values.But if the enum consist of more than 100 constants then the program becomes very large. A simple strategy is to use array of strings(char *)
      Example program to print the enum data
    #include<stdio.h>
    enum new_enum{sunday,monday,tuesday,wednesday,thursday,friday,saturday};
    const char *enum_names[]={"sunday","monday","tuesday","wednesday","thursday","friday","saturday"};
    int main()
    {
        enum new_enum e1,e2;
        e1=sunday;
        e2=monday;
        printf("day1=%s\n",enum_names[e1]);
        printf("day2=%s\n",enum_names[e2]);
        return 0;
    }
    Output
    day1=sunday
    day2=monday

    Wednesday, 10 March 2010

    C Multidimensional Arrays

    1.Two-Dimensional Array
    • Two dimensional array is also called a matrix.
    • It is taken as an array of an array.
    • Logically the 2 dimensional array 3 X 2 is taken as
      1 2
      3 4
      5 6
      Here there are 3 arrays. one array in each row
    • The values are stored in a row major form. ie each row is stored first. The above array is stored as 1 2 3 4 5 6.
    • Each row array is represented as a[0],which consist of elements 1 and 2.a[1] consists of 3 and 4. and a[2] consists of 5 and 6
    • Each element of a[0] is accessed as a[0][0] and a[0][1].Thus the value of a[0][0]  is 1 and a[0][1] is 2.
    • In memory whether it is a one dimensional or a two dimensional array the array elements are stored in one continuous chain.
    • The size of the array is number of rows * number of columns
    Initializing a 2-Dimensional Array:
    • For initializing a 2-dimensional array we have to specify 2 dimensions. Number of rows and the Number of columns 
    • Ex1:
      int emp[4][3]={
         {1,100,'a'},
         {2,100,'a'},
         {3,98,'b'},
         {4,90,'c'}
      };
    • Ex2: int emp[4][3]={1,100,'a',2,100,'a',3,98,'b',4,90,'c'};
    • While initializing a 2-D array it is necessary to mention the second dimension ie the number of columns.The first dimension ie number of rows is optional.
    • Ex: int a[2][3]={1,2,3,4,5,6}; is same as int a[][3]={1,2,3,4,5,6}; are same. But int a[2][]={1,2,3,4,5,6}; and int a[][]={1,2,3,4,5,6} are wrong way of initializing.
    • In one dimensional arrays name[i] is same as *(name+i). Similarly in 2-dimensional array *(a[2]+1) is same as *(*(a+2)+1).This refer to the 2nd row 1st element.
    • Thus a[2][1] is same as *(a[2]+1) and *(*(a+2)+1)
    Example: passing 2 dimensional array to a function
    #include<stdio.h>
    main()
    {
        int i,j,a[3][2];
        for(i=0;i<3;i++)
        {
            for(j=0;j<2;j++)
            {
                a[i][j]=i;
            }
        }
        printArr(a,3,2);
        printArray(a,3,2);
        return 0;
    }
    void printArr(int a[][2],int m,int n)
    {
        int i,j;
        for(i=0;i<m;i++)
        {
            for(j=0;j<n;j++)
            {
                printf("a[%d][%d]=%d is in address=%8u\n",i,j,a[i][j],&a[i][j]);
            }
        }
    }
    void printArray(int (*a)[2],int m,int n)
    {
        int i,j;
        int *p;
        for(i=0;i<m;i++)
        {
            for(j=0;j<n;j++)
            {
                p=a+i;
                printf("a[%d][%d]=%d is in address=%p\n",i,j,*(p+j),(p+j));
            }
        }
    }
    Output
    a[0][0]=0 is in address=2609148672
    a[0][1]=0 is in address=2609148676
    a[1][0]=1 is in address=2609148680
    a[1][1]=1 is in address=2609148684
    a[2][0]=2 is in address=2609148688
    a[2][1]=2 is in address=2609148692
    a[0][0]=0 is in address=0x7fff9b847300
    a[0][1]=0 is in address=0x7fff9b847304
    a[1][0]=1 is in address=0x7fff9b847308
    a[1][1]=1 is in address=0x7fff9b84730c
    a[2][0]=2 is in address=0x7fff9b847310
    a[2][1]=2 is in address=0x7fff9b847314


    2.Three-Dimensional Array:
    • Each 3 dimensional array is taken as an array of two-dimensional array
    • Eg: int a[2][3][4]; is a 3 dimensional array. This array consist of 2 arrays of the size 3 X 4. Each array is referred as a[0] & a[1]. Thus a[0] consist of 12 elements and a[1] also consists of 12 elements.
    • The 3 X 4 two dimensional array is then taken as 3 arrays of the size 4.
    • When passing a array to the function,we have to specify the inner dimensions.so while passing this 3 dimension array we have to pass 3 and 4 as inner dimension.
    • The following 2 expressions refer to the same element in the 3 dimensional array
    1. a[1][2][3]
    2. *(*(*(arr+1)+2)+3)
    Example Program:
    #include<stdio.h>
    main()
    {
        int i,j,k,x=0,a[2][3][4];
        for(i=0;i<2;i++)
        {
            for(j=0;j<3;j++)
            {
                for(k=0;k<4;k++)
                {
                    a[i][j][k]=x;
                    x++;
                }
            }
        }
        printArr(a,2,3,4);
        return 0;
    }
    void printArr(int a[][3][4],int m,int n,int p)
    {
        int i,j,k;

        for(i=0;i<m;i++)
        {
            for(j=0;j<n;j++)
            {

                for(k=0;k<p;k++)
                {
                    printf("a[%d][%d][%d}=%d is in address=%8u\n",i,j,k,a[i][j][k],&a[i][j][k]);
                }
            }
        }
    }
    Output

    a[0][0][0}=0 is in address=544671328
    a[0][0][1}=1 is in address=544671332
    a[0][0][2}=2 is in address=544671336
    a[0][0][3}=3 is in address=544671340
    a[0][1][0}=4 is in address=544671344
    a[0][1][1}=5 is in address=544671348
    a[0][1][2}=6 is in address=544671352
    a[0][1][3}=7 is in address=544671356
    a[0][2][0}=8 is in address=544671360
    a[0][2][1}=9 is in address=544671364
    a[0][2][2}=10 is in address=544671368
    a[0][2][3}=11 is in address=544671372
    a[1][0][0}=12 is in address=544671376
    a[1][0][1}=13 is in address=544671380
    a[1][0][2}=14 is in address=544671384
    a[1][0][3}=15 is in address=544671388
    a[1][1][0}=16 is in address=544671392
    a[1][1][1}=17 is in address=544671396
    a[1][1][2}=18 is in address=544671400
    a[1][1][3}=19 is in address=544671404
    a[1][2][0}=20 is in address=544671408
    a[1][2][1}=21 is in address=544671412
    a[1][2][2}=22 is in address=544671416
    a[1][2][3}=23 is in address=544671420


    Monday, 8 March 2010

    C Array

    Introduction:
    • An Array is a collection of similar elements of same data type. The elements may be all int , or all float or all char.
    • we cant create a array with 5 integer element and 5 float elements. 
    • Array elements are referred by using subscript,the lowest subscript is always 0 and the highest subscript is size-1.The valid range of subscript is 0 to size-1
    • Refering an array element using an out of range subscript will produce an error
    • Array elements occupy consecutive memory locations and the size of each element is same.
    • The array name is a pointer constant.we can pass the array name to the function and manipulate array elements in the function.
    • An array is always processed element by element.
    • The array subscript has the highest precedence among all operators.Thus a[2]++,will increment the value at location 2 in the array
    Array Declaration:
            Like other variables array must be declared. The declaration will inform the compiler about the type of array(int,float or char) and the size(Number of elements) of array.
    Syntax:


    data type array_name[size];
    data type-> specifies the type of variable
    array_name -> name of the array variable
    size->number of elements in the array
    []->tell the compiler this variable is a array

    example:
    int a[10];
    float b[5];
    char name[10]; [The array of character is called string]


    Accessing Array elements:
    • All the array elements are numbered starting with 0. This number specifies the elements position in the array.The individual elements in the array can be accessed by specifying array name followed by the position of the element(index) in the array.
    Syntax:

    array_name[index];
    In a[10] array,
    a[0]->First element of the array.
    a[9]->last element of the array.


    • The other way of accessing array elements are:
    1. *(array_name+index)==>*(a+i)
    2. *(index+array_name)==>*(i+a)
    3. index[array_name]==>i[a]
    here i is the index

    Array Initialization:
    The initial value for the array can be given at the time of declaration.

    Syntax:



    data type array_name[size]={value list};

    Ex:

    int numbers[5]={10,20,30,40,50};
    int num[]={1,2,3,4,5,6};
    float count[5]={1.5,3.5,5.6,6.7,7.8};
    char vowels[6]={'a','e','i','o','u','/0'};

    Note:

    1. The characters must be enclosed in single quote(').
    2. The last character in a char array must be a NULL(/0) character.
    3. We can directly assign the string in to a character array.The string must be enclosed in double quotes("). eg: char vowels[6]="aeiou";
    4. When the value assigned to a character array is a string the compiler automatically supplies a NULL character but we need to specify one extra space for NULL character.
    Passing Array Elements to a Function

    • Array elements can be passed to a function by calling the function by value or by reference
    call by value:
    • In the call by value,the values of the array elements are passed to the function.
    • The address can be printed using %16lu(decimal digits) or %p(hexadecimal address)
    • Example:Program to pass array elements using call by value
     #include<stdio.h>
    void printArray(int a[]);
    main()
    {
        int i,a[5];
        for(i=0;i<5;i++)
        {
            a[i]=i;
        }
        printArray(a);
        return 0;
    }
    void printArray(int a[])
    {
        int i;
        for(i=0;i<5;i++)
        {
            printf("%d element is at address %16lu\n",a[i],&a[i]);
        }
    }
    Output
    0 element is at address  140737342687264
    1 element is at address  140737342687268
    2 element is at address  140737342687272
    3 element is at address  140737342687276
    4 element is at address  140737342687280
    • Since each array element is of the integer type,the difference between addresses is 4
    Call by reference:
    • The array members can be passed to the function using call by reference ie passing the base address of the array.Base address is the address of the zeroth element.
    • Each element of the array has a memory address.
    • Example program to passing an entire array using call by reference
    #include<stdio.h>
    void printArray(int *a);
    main()
    {
        int i,a[5];
        for(i=0;i<5;i++)
        {
            a[i]=i;
        }
        printArray(&a);
        return 0;
    }
    void printArray(int *a)
    {
        int i;
        for(i=0;i<5;i++)
        {
            printf("%d element is at address %p\n",*a,a);
            a++;
        }
    }
    output:
    0 element is at address 0x7ffffbd6ac50
    1 element is at address 0x7ffffbd6ac54
    2 element is at address 0x7ffffbd6ac58
    3 element is at address 0x7ffffbd6ac5c
    4 element is at address 0x7ffffbd6ac60
    MORE ABOUT ARRAYS

    Thursday, 4 March 2010

    C Preprocessor-3

    Operators in Preprocessor:
    • The 2 operators used in the preprocessor are
    1. #
    2. ##
    1.# operator:
    • If the macros are present with in the quoted strings it is not replaced by the #defined macro.
    • But if the macroname is preceded by a # then the macro is expanded into a quoted string with the parameter replaced by the actual argument
    • Eg:
      #include<stdio.h>
      #define toprint(expr) printf(#expr "=%d\n",expr)
      int main()
      {
          int x=10,y=5;
          toprint(x/y);
          return 0;
      }
      when toprint(x/y);is invoked the macro is expanded in to
          printf("x/y" "=%d\n",x/y);
      and the strings are concatenated.so the statement becomes
           printf("x/y=%d\n",x/y);
      The output of the above program is x/y=2
    • In the above example instead of #expr if only expr is present like
          #define toprint(expr) printf("expr=%d\n",expr) then the output is expr=2
    2.## operator:
    • The preprocessor operator ## provides a way to concatenate actual arguments during macro expansion.
    • If a parameter in the repacement text is adjacent to a ##, the parameter is replaced by the actual argument,the ## and surrounding white space are removed,and the result is rescanned
    • Example The macro Join concatenates its two arguments
          #define Join(front,back) front##back
      now Join(hello,100) resuts hello100.
    NULL Directive
    • # 
    • A preprocessor line of the form # has no effect 
    • if the line has only #symbol the preprocessor ignores the line


    Tuesday, 2 March 2010

    C Preprocessor-2

    2.File Inclusion(#include):
    • This directive allow one file to be included in another file.
    • Syntax is  #include "file name"
      This will insert the entire content of filename in to the source code.
    • It can be used in 2 cases


      1. If the program is very large then the code is divided in to several different files.These files are #included at the begining of main program file.
      2. Some functions or some macros are almost needed in all programs. These functions and macros are stored in a file and included in the program that are needed Eg header files
    • #include statement can be written in 2 ways


      1. #include "filename"
      2. #include < filename >
    • #include "filename.c" would look for the file filename.c in the current directory as well as the specified list of directories as mentioned in the include search path.
    • #include < filename.c > would look for the file filename.c in the specified list of directories only.
    3.Conditional Inclusion
    • It is possible to control preprocessing itself with conditional statements that are evaluated during preprocessing.
    • provides a way to include code selectively depending on the condition.
    • Some of the conditional directives are

      1. #if
      2. #elif
      3. #else
      4. #endif
      5. #ifdef
      6. #ifndef
      7. #undef
    1. #if directive


      • #if can be used to test whether an expression evaluates to a nonzero value or not.(the expression may not include sizeof,casts or enum constants)
      • The generalized form of the if directive is
        #if < condition >
        #endif
      • If the result of the expression is nonzero then subsequent lines upto #endif are compiled otherwise they are skipped.
      • example code to make sure that the contents of the file hdr.h are included only once
        #if !defined(HDR)
        #define HDR
        /* contents of hdr.h */
        #endif
      • The defined() predicate returns true if the symbol is defined otherwise it is false.
    2. #else directive


      • #else always comes with #if directive
      • It allow us to specify the action if the condition is not true
      • Eg: Suppose we have 3 files
        file1.h
        #define USD 1
        file2.h
        #define UKP 1
        file3
        #include < stdio.h >
        #include < file1.h >
        #if(defined(USD))
            #define rate 46
        #else
            #define rate 100
        #endif

        since USD is defined in file1.h,the currency rate is taken as 46.
    3. #elif directive


      • #elif allow us to take one action if there are multiple decision points.
      • #elif is similar to #else but it is followed by a condition.
      • Example: The following sequence tests the name SYSTEM to decide which version of a header to include

        #if SYSTEM == SYSV
            #define HDR "sysv.h"
        #elif SYSTEM == BSD
            #define HDR "bsd.h"
        #elif SYSTEM == MSDOS
           #define HDR "msdos.h"
        #else
            #define HDR "default.h"
        #endif
        #include HDR
    4. #ifdef directive


      • The ifdef directive makes substitution based on whether a particular identifier is defined.
      • If the identifier is defined,it returns true,otherwise it is false.
      • Eg: Suppose we have 3 files
        file1.h
        #define USD 1
        file2.h
        #define UKP 1
        file3
        #include < stdio.h >
        #include < file1.h >
        #ifdef USD
            #define rate 46
        #endif
        #ifdef UKP
            #define rate 100
        #endif
        The 1st ifdef directive checks whether the identifier USD is defined. since it is defined in file1.h the condition is true and the rate is defined as 46. The 2nd ifdef directive checks whether the identifier UKP is defined.Since file2.h is not included it is not defined so the condition is false and the directive is not processed.
        If instead of file1.h, if file2.h is included then the rate will be 100 
    5. #ifndef directive


      • #ifndef (means 'if not defined') works exactly opposite to #ifdef.
      • #ifndef is compleement of #ifdef. so when #ifdef returns true,#ifndef returns false
      • Eg: Suppose a function fun() is defined in a file 'file1.h'.and file1.h is #included in file2.h.now if we #include both 'file1.h' and 'file2.h' then the compiler flashes an error 'multiple declaration for fun'.To avoid this we can write the following code in the header file
        /*file1.h*/
        #ifndef file_h
            #define file_h
            fun()
            {
                /*code*/
            }
        #endif
      • first time the file 'file.h' gets included the preprocessor checks whether a macro called file_h has been defined or not.first time its not defined so the macro gets defined and the fun() get included.Next time we attempt to include the same file.the macro file_h already defined so the fun() is not included.
    6. #undef directive


      • #undef nullify the effect of the #define directive and specify a new value.
      • Ex:
        #include < stdio.h >
        #define SUM 40
        #undef SUM
        #define SUM 100
        int main()
        {
        printf("SUM=%d\n",SUM);
        }
        The 1st #define assign SUM as value 40 but the #undef indicate that the SUM definition no longer exist. The 2nd #define define SUM as 100. so the program will print the output as SUM=100.
    4.Miscellaneous Directives
    • There are 3 more preprocessor directives available but they are not very commonly used
    1. #pragma
    2. #error
    3. #line
    1.#pragma directive:
    • #pragma is a special purpose directive that can use to turn on or off certain features
    • pragmas vary from one compiler to another.
    1. #pragma startup and #pragma exit: These directives allow us to specify functions that are called upon program startup(before main()) or program exit(just before the program terminates)
      Ex:
      void myfun1();
      void myfun2();
      #pragma startup myfun1
      #pragma exit myfun2
      int main()
      {
          printf("I am in main\n");
          return 0;
      }
      void myfun1()
      {
          printf("I am in myfunction1");
      }
      void myfun2()
      {
          printf("I am in myfunction2");
      }
      Output
      I am in myfunction1
      I am in main
      I am in myfunction2
      Note1.The functions myfun1() and myfun2() will not receieve or return any value.
          2.If we want 2 functions to executed at startup then thier pragmas should be defined in the reverse order in which we want to call them
    2. #pragma warn: This directive tells the compiler whether or not we want to suppress a specific warning
      Eg:
      #pragma warn -rvl /*return value */
      #pragma warn -par /*parameter not used*/
      #pragma warn -rch /*unreachable code*/
      int fun1()
      {
          printf("I am in function1");
      }
      void fun2(int a)
      {
          printf("I am in function2");
      }
      int fun3()
      {
          int z=10;
          return z;
          z++;
      }
      int main()
      {
          fun1();
          fun2(100);
          fun3();
          return 0;
      }
      In the above example the function fun1() doesnt return a value. The parameter passed to the fun2() is not being used in fun2() and the control will never reach z++ in fun3()
      If we compile the program we should expect warnings indicating the above problem.
      But the program does not produce warnings because we have suppressed the warnings using #pragma.
       If we replace the '-' sign with the '+' sign in #pragma then the warnings will be shown in the compilation.
     3.#error directive:
    • The error directive is used to specify an error message for a specific situation
    •  The error message is generated by the preprocessor at compile time.
    • Eg:Suppose we have 3 files
      file1.h
      #define USD 1
      file2.h
      #define UKP 1
      file3
      #include < stdio.h >
       #include < file1.h >
      #if !defined(USD) || !defined(UKP)
         #error "ERROR: NO_CURRENCY rate is specified"
      #endif

      #if checks whether USD or UKP is defined.If both are not defined then the preprocessor displays an error
    4. #line Directive:
    • The #line directive allows us to define arbitrary line numbers for the source lines.
    • Normally the compiler counts lines starting at line number 1.using the #line directive,we can specify an arbitrary line number at any point.The compiler then uses that line number for subsequent counts.
    • #line is used to indicate line numbers which can be used for debugging
    • Eg:
       
      int main()
      {
           fun1();
        #line100
          fun2(100);
         printf("C FILE %s LINE %d\n",_FILE_,_LINE_);
         #line200
          fun3();
          return 0;
       }
       after fun1() the line number is assigned to 100. so fun2(100); is at line number 101 and then line number is modified to 200 before fun3();. so fun3(); is at line number 201.
    • _FILE_ and _LINE_ are the 2 special identifiers.Which indicate the file name of the source file and the current line number