MOST POPULAR C PROGRAMMING FACTS
  • C Programming language Evolution: ALGO->BCPL->B->Tradition C-> K&R C-> ANSI C-> ANSI/ISO C-> C99.
  • It was developed & released around the year 1972.
  • One of the most interesting facts is that It was developed at Bell Laboratories in 1972 by Dennis Ritchie.
  • The first programming language for the computer was Plankalkul, but C language is seen as the first high-level programming language.
  • It’s really easy and fun to write a C program. You can check out How to write a C program in Linux.
  • Most popular Linux Kernel and most UNIX utilities, what you would call the operating system are written in C.
Evolution in C Programming Language: 

1. ALGOL (Internation Group)
2. BCPL (Martin Richards)
3. B (Thompson)
4. Tradition C
5. K&R C (Kernighan And Ritchie)
6. ANSI C (ANSI Committee)
7. ANSI/ISO C (ISO Committee)
8. C99 (Standardization Committee)

C is one of the most widely used programming languages of all time, and C compilers are available for the majority of available computer architectures and operating systems. And the reason behind its wide usage are some interesting facts that we will discuss here. We are going to discuss about two things:

  • Bitwise Operators
  • Macros And Preprocessors

Bitwise Operators in C

There are 6 types bitwise operators in C which will work at bit-level:

& (bitwise AND)  This operator will take two numbers as operand and does AND on every bit of two numbers. The result of AND is 1 only if both bits are 1.

| (bitwise OR)  This operator will take two numbers as operand and does OR on every bit of two numbers. The result of OR is 1 any of the two bits is 1.

^ (bitwise XOR) This operator will take two numbers as operand and does XOR on every bit of two numbers. The result of XOR is 1 if the two bits are different.

<< (left shift) This operator will take two numbers, left shifts the bits of the first operand, the second operand decides the number of places to shift.

>> (right shift) This operator will take two numbers, right shifts the bits of the first operand, the second operand decides the number of places to shift.

~ (bitwise NOT) This operator will take one number and inverts all bits of it.

Now look at the example which demonstrate the use of all bitwise operators

/* C Program to demonstrate use of bitwise operators */
#include<stdio.h>
int main()
{
    unsigned char a = 5, b = 9; // a = 4(00000101), b = 8(00001001)
    printf("a = %d, b = %d\n", a, b);
    printf("a&b = %d\n", a&b); // The result is 00000001
    printf("a|b = %d\n", a|b);  // The result is 00001101
    printf("a^b = %d\n", a^b); // The result is 00001100
    printf("~a = %d\n", a = ~a);   // The result is 11111010
    printf("b<<1 = %d\n", b<<1);  // The result is 00010010
    printf("b>>1 = %d\n", b>>1);  // The result is 00000100
    return 0;
}
Facts about bitwise operator:
  1. No negative number should be used for the left shift and right shift operators We will get an undefined result if we take negative operands for << and >>. For example, results of both -3 << 3 and 3 << -3 are undefined.
  2. The most useful operator from technical interview is  the bitwise XOR operator Wheniniagven set of numbers where all elements occur even number of times except one number, finds the odd occurring number, this problemcanbeefficientlysolvedbyjustdoingXOR of all numbers.
    // Function to return the only odd occurring element
    int findOdd(int arr[], int n){
       int res = 0, i;
       for (i = 0; i < n; i++)
         res ^= arr[i];
       return res;
    }
    int main(void) {
    int arr[] = {12, 12, 14, 90, 14, 14, 14};
    int n = sizeof(arr)/sizeof(arr[0]);
    printf (“The odd occurring element is %d “, findOdd(arr, n));
    return 0;
    }
    // Output: The odd occurring element is 90 
  3. Bitwise operators should not be interchanged with logical operators Because bitwise operators return an integer value and result of logical operators (&&, || and !) is either 0 or 1, Also the logical operators consider any non-zero operand as 1.
    int main()
    {
       int x = 2, y = 5;
       (x & y)? printf("True ") : printf("False ");
       (x && y)? printf("True ") : printf("False ");
       return 0;
    }
    // Output: False True
  4. The left-shift and right-shift operators are equivalent to multiplication and division by 2 respectively.
    As mentioned in point 1, it works only if the numbers are positive.
int main()
{
   int x = 19;
   printf ("x << 1 = %d\n", x << 1);
   printf ("x >> 1 = %d\n", x >> 1);
   return 0;
}
// Output: 38 9
  • We can check whether the number is odd or even with bitwise operators The number is odd if the value of expression (x & 1) would be non-zero, otherwise the value would be zero.
    // Note that the output of following program is compiler dependent
    int main()
    {
       unsigned int x = 1;
       printf("Signed Result %d \n", ~x);
       printf("Unsigned Result %ud \n", ~x);
       return 0;
    }
    /* Output:
    Signed Result -2
    Unsigned Result 4294967294d */
  • Carefully use the ~ operator The result of ~ operator can  be a negative number if the result is stored in a signed variable (assuming that the negative numbers are stored in 2’s complement form where leftmost bit is the sign bit) and result on a small number can be a big number if the result is stored in a unsigned variable.

 

Macros and Preprocessors in C

All lines that starting with # are processed by preporcessor which is a special program invoked by the compiler in a C program.That is a preprocessor takes a C program and produces another C program without any #.

Facts about Macros and Preprocessors in C
  1. When any constant is defined, the preprocessor produces a C program where the defined constant is searched and matching tokens are replaced with the given expression.Forexample here max is defined as 100.
    #include<stdio.h>
    #define max 100
    int main()
    {
        printf("max is %d", max); 
        return 0;
    }
    // Output: max is 100
    // Note that the max inside "" is not replaced

     

  2. The macros can take the function like arguments, which are not checked by data type.Here the following macro INCREMENT(x) can be used for x of any data type.
    #include <stdio.h>
    #define INCREMENT(x) ++x
    int main()
    {
        char *ptr = "SeeksQuiz";
        int x = 10;
        printf("%s  ", INCREMENT(ptr));
        printf("%d", INCREMENT(x));
        return 0;
    }
    // Output: eeksQuiz 11
  3. The macro arguments are not evaluated before macro expansion.
    #include <stdio.h>
    #define MULTIPLY(a, b) a*b
    int main()
    {
        // The macro is expended as 2 + 3 * 3 + 5, not as 5*8
        printf("%d", MULTIPLY(2+3, 3+5));
        return 0;
    }
    // Output: 16
  4. When we use include directive,  the contents of the included header file (after preprocessing) are copied to the current file. Angular brackets < and > instruct the preprocessor to look in the standard folder where all header files are held.  Double quotes and instruct the preprocessor to look into the current folder and if the file is not present in the current folder, then in standard folder of all header files.
  5. The tokens passed to macros can be linked using operator ## called Token-Pasting operator.
    #include <stdio.h>
    #define merge(a, b) a##b
    int main()
    {
        printf("%d ", merge(12, 34));
    }
    // Output: 1234
  6. By using ‘\’ macros can be written in multiple lines, but the last line doesn’t have ‘\’.
    #include <stdio.h>
    #define PRINT(i, limit) while (i < limit) \
                            { \
                                printf("Dexter "); \
                                i++; \
                            }
    int main()
    {
        int i = 0;
        PRINT(i, 3);
        return 0;
    }
    // Output: Dexter  Dexter  Dexter
  7. A token passed to macro can be converted to a string literal by using # before it.
    #include <stdio.h>
    #define get(a) #a
    int main()
    {
        // Dexter is changed to "Dexter"
        printf("%s", get(Dexter));
    }
    // Output: Dexter
  8.  Avoid the macros with arguments as they cause problems sometimes, and preference should be given to an Inline function as there is a type checking parameter evaluation in inline functions.
    #define square(x) x*x
    int main()
    {
      int x = 36/square(6); // Expended as 36/6*6
      printf("%d", x);
      return 0;
    }
    // Output: 36
    Also the program given in point 4 above can be corrected using inline functions.
inline int square(int x) { return x*x; }
int main()
{
  int x = 36/square(6);
  printf("%d", x);
  return 0;
}
// Output: 1
  • if-else directives are supported by preprocessors which are typically used for conditional compilation.
    int main()
    {
    #if VERBOSE >= 2
      printf("Trace Message");
    #endif
    }
  • A header file may be included more than one time directly or indirectly, this leads to problems of redeclaration of the same variables/functions. To avoid this problem, directives like defined, ifdef and ifndef are used.
  • There are some standard macros which can be used to print program file (__FILE__), Date of compilation (__DATE__), Time of compilation (__TIME__) and Line Number in C code (__LINE__)
    #include <stdio.h>
     
    int main()
    {
       printf("Current File :%s\n", __FILE__ );
       printf("Current Date :%s\n", __DATE__ );
       printf("Current Time :%s\n", __TIME__ );
       printf("Line Number :%d\n", __LINE__ );
       return 0;
    }
    /* Output:
    Current File :C:\Users\GfG\Downloads\deleteBST.c
    Current Date :Feb 15 2014
    Current Time :07:04:25
    Line Number :8 */