Typical Programs in C/C++

 Sum of two large numbers

  • Difficulty Level : Medium
  •  Last Updated : 04 Jun, 2019

Given two numbers as strings. The numbers may be very large (may not fit in long long int), the task is to find sum of these two numbers.

Examples:

Input  : str1 = "3333311111111111", 
         str2 =   "44422222221111"
Output : 3377733333332222

Input  : str1 = "7777555511111111", 
         str2 =    "3332222221111"
Output : 7780887733332222
The idea is based on school mathematics. We traverse both strings from end, one by one add digits and keep track of carry. To simplify the process, we do following:
1) Reverse both strings.
2) Keep adding digits one by one from 0’th index (in reversed strings) to end of smaller string, append the sum % 10 to end of result and keep track of carry as sum/10.
3) Finally reverse the result.
// C++ program to find sum of two large numbers.
#include<bits/stdc++.h>
using namespace std;
  
// Function for finding sum of larger numbers
string findSum(string str1, string str2)
{
    // Before proceeding further, make sure length
    // of str2 is larger.
    if (str1.length() > str2.length())
        swap(str1, str2);
  
    // Take an empty string for storing result
    string str = "";
  
    // Calculate length of both string
    int n1 = str1.length(), n2 = str2.length();
  
    // Reverse both of strings
    reverse(str1.begin(), str1.end());
    reverse(str2.begin(), str2.end());
  
    int carry = 0;
    for (int i=0; i<n1; i++)
    {
        // Do school mathematics, compute sum of
        // current digits and carry
        int sum = ((str1[i]-'0')+(str2[i]-'0')+carry);
        str.push_back(sum%10 + '0');
  
        // Calculate carry for next step
        carry = sum/10;
    }
  
    // Add remaining digits of larger number
    for (int i=n1; i<n2; i++)
    {
        int sum = ((str2[i]-'0')+carry);
        str.push_back(sum%10 + '0');
        carry = sum/10;
    }
  
    // Add remaining carry
    if (carry)
        str.push_back(carry+'0');
  
    // reverse resultant string
    reverse(str.begin(), str.end());
  
    return str;
}
  
// Driver code
int main()
{
    string str1 = "12";
    string str2 = "198111";
    cout << findSum(str1, str2);
    return 0;
}

Output:
198123

Optimization:
We can avoid the first two string reverse operations by traversing them from end. Below is optimized solution.

Sum of two large Floating-point numbers

Given two very large floating-point numbers in form of large strings str1 and str2, the task is to add the given two numbers.

Example:

Input: str1 = “584506134.87368350839565308”, str2 = “30598657.0330473560587475634983”
Output: 615104791.9067308644544006434983

Input: str1 = “38.30”, str2 = “37.0983”
Output: 75.3983

Approach:

To find the addition of two large integers that can’t be stored in the inbuilt data type we will use an array to store the digits of the numbers and then perform the addition digit by digit starting from the LSB. 

Using this concept, we can also find the summation of large floating-point numbers.

Steps to add the two given floating-point numbers:

  1. Split both the given floating-point number in form of a string with respect to the decimal point to separate the fractional and integer part of the numbers.
  2. Add the fractional and integer part of the two numbers separately and forward the final carry part of fractional addition to integers part.
    For Example:
    str1 = "23.94" and str2 = "34.23"
    
    For fractional part:
    f1[] = {4, 9}
    f2[] = {3, 2}
    --------------
    Sum  = {7, 1, 1}
    Therefore, Carry = 1
    
    For Integer part:
    Carry = 1
    I1[] = {3, 2}
    I2[] = {4, 3}
    --------------
    Sum  = {8, 5}
    
  3. Concatenate the digits stored for integer and fractional part with a decimal ‘.’ to get the required sum two large floating point numbers.
    From Integer part = 58
    From fractional part = 17
    
    Sum = 58.17
    

Below is the implementation of the above approach:

// C++ program to find Sum of two
// large Floating-point numbers
  
#include <bits/stdc++.h>
using namespace std;
  
// Function to make fractional part
// with equal digits
void makeEqualAtFront(vector<int>& A,
                      vector<int>& B)
{
    int n = A.size();
    int m = B.size();
    int diff = abs(n - m);
  
    if (n < m) {
        for (int i = 0; i < diff; i++) {
            A.insert(A.begin(), 0);
        }
    }
    else {
        for (int i = 0; i < diff; i++) {
            B.insert(B.begin(), 0);
        }
    }
}
  
// Function to make Integral part
// with equal digits
void makeEqualAtback(vector<int>& A,
                     vector<int>& B)
{
    int n = A.size();
    int m = B.size();
    int diff = abs(n - m);
  
    if (n < m) {
        for (int i = 0; i < diff; i++) {
            A.push_back(0);
        }
    }
    else {
        for (int i = 0; i < diff; i++) {
            B.push_back(0);
        }
    }
}
  
// Function to add the given large
// floating point number string
void findSum(string s1, string s2)
{
  
    int i;
  
    // To store the integer and
    // fractional part of numbers
    vector<int> Ints1, Ints2;
    vector<int> Fracs1, Fracs2;
  
    // Separating integer and
    // fractional part of s1
    for (i = s1.length() - 1; i > -1; i--) {
  
        // If decimal occurs break
        if (s1[i] == '.') {
            break;
        }
        Fracs1.push_back(s1[i] - '0');
    }
  
    i--;
    for (; i > -1; i--) {
        Ints1.push_back(s1[i] - '0');
    }
  
    // Separating integer and
    // fractional part of s2
    for (i = s2.length() - 1; i > -1; i--) {
  
        // If decimal occurs break
        if (s2[i] == '.') {
            break;
        }
        Fracs2.push_back(s2[i] - '0');
    }
  
    i--;
    for (; i > -1; i--) {
        Ints2.push_back(s2[i] - '0');
    }
  
    // Making number of digits in
    // fractional and Integer
    // part equal
    makeEqualAtFront(Fracs1, Fracs2);
    makeEqualAtback(Ints1, Ints2);
  
    // Adding fractional parts of
    // s1 and s2
    int n = Fracs1.size();
    int m = Fracs2.size();
    i = 0;
    int carry = 0;
  
    while (i < n && i < m) {
  
        // Traverse the Fracs1[] and
        // Fracs2[] and add the digit
        // and store the carry
        int sum = Fracs1[i]
                  + Fracs2[i]
                  + carry;
  
        Fracs1[i] = sum % 10;
        carry = sum / 10;
        i++;
    }
  
    int N = Ints1.size();
    int M = Ints2.size();
    i = 0;
  
    // Adding integer part of
    // s1 and s2
    while (i < N && i < M) {
        int sum = Ints1[i]
                  + Ints2[i]
                  + carry;
        Ints1[i] = sum % 10;
        carry = sum / 10;
        i++;
    }
    if (carry != 0)
        Ints1.push_back(carry);
  
    // Print the result by appending
    // Integer and decimal part stored
    // in Ints1[] and Fracs1[]
    for (int i = Ints1.size() - 1; i > -1; i--) {
        cout << Ints1[i];
    }
    cout << '.';
    for (int i = Fracs1.size() - 1; i > -1; i--) {
        cout << Fracs1[i];
    }
}
  
// Driver Code
int main()
{
    string str1
        = "584506134.87368350839565308";
    string str2
        = "30598657.0330473560587475634983";
  
    findSum(str1, str2);
  
    return 0;
}
Output:
615104791.9067308644544006434983
Check if both halves of a string are Palindrome or not
  • Last Updated : 28 Jul, 2020

Given a string str, the task is to check whether the given string can be split into two halves, each of which is palindromic. If it is possible, print Yes. Otherwise, print No.
Examples: 
 

Input: str = “naan” 
Output: No 
Explanation: 
Since both halves “na” and “an” are not palindrome.
Input: momdad 
Output: Yes 
Explanation: 
Since both half “mom” and “dad” are palindromes. 

Approach: 
Follow the steps below to solve the problem: 
 

  • Iterate over the first ((N / 2) / 2 – 1) indices of the string.
  • Simultaneously check if both the halves are palindrome or not by the following two conditions: 
    • If S[i] is not equal to S[N/2 – 1 – i], then first half is not palindromic.
    • If S[N/2 + i] is not equal to S[N – 1 – i], then second half is not palindromic.
  • If none of the above condition is satidfied even once during the iteration, then both halves are palindromic. Print Yes.
  • Otherwise, print No.

Below is the implementation of the above approach: 

// C++ Program to check whether
// both halves of a string is
// Palindrome or not
#include <bits/stdc++.h>
using namespace std;
  
// Function to check if both halves
// of a string are palindrome or not
void checkPalindrome(string S)
{
    // Length of string
    int N = S.size();
  
    // Initialize both part as true
    bool first_half = true;
    bool second_half = true;
  
    int cnt = (N / 2) - 1;
  
    for (int i = 0; i < ((N / 2) / 2); i++) {
  
        // If first half is not palindrome
        if (S[i] != S[cnt]) {
            first_half = false;
            break;
        }
  
        // If second half is not palindrome
        if (S[N / 2 + i] != S[N / 2 + cnt]) {
            second_half - false;
            break;
        }
  
        cnt--;
    }
  
    // If both halves are Palindrome
    if (first_half && second_half) {
        cout << "Yes" << endl;
    }
    else {
        cout << "No" << endl;
    }
}
  
int main()
{
    string S = "momdad";
  
    checkPalindrome(S);
  
    return 0;
}
Output:
Yes

 

Time Complexity: O(N) 
Auxiliary Space: O(1)

Write a C program to reverse the words in a sentence in place.




That is, given a sentence like this


I am a good boy


The in place reverse would be


boy good a am I


 

yob doog  a   ma   I
<-> <--> <->  <-> <->

boy good a am I



Here is some C code to do the same ....


/*
  Algorithm..
  
  1. Reverse whole sentence first.
  2. Reverse each word individually.

  All the reversing happens in-place.
*/

#include <stdio.h>

void rev(char *l, char *r);


int main(int argc, char *argv[])
{
   char buf[] = "the world will go on forever";
   char *end, *x, *y;
      
   // Reverse the whole sentence first..
   for(end=buf; *end; end++);
   rev(buf,end-1); 


   // Now swap each word within sentence...
   x = buf-1; 
   y = buf; 

   while(x++ < end)
   {
      if(*x == '\0' || *x == ' ')
      {
        rev(y,x-1);
        y = x+1;
      }
   }   
   
   // Now print the final string....
   printf("%s\n",buf);
   
   return(0);
}


// Function to reverse a string in place...
void rev(char *l,char *r)
{
   char t;
   while(l<r) 
   { 
      t    = *l; 
      *l++ = *r; 
      *r-- = t; 
   }
}


 

 

2.

 

 

Write a C program generate permutations.




Iterative C program


#include <stdio.h>
#define SIZE 3
int main(char *argv[],int argc)
{
  char list[3]={'a','b','c'};
  int i,j,k;

  for(i=0;i<SIZE;i++)
    for(j=0;j<SIZE;j++)
      for(k=0;k<SIZE;k++)
        if(i!=j && j!=k && i!=k)
          printf("%c%c%c\n",list[i],list[j],list[k]);

  return(0);
}



Recursive C program


#include <stdio.h>
#define N  5


int main(char *argv[],int argc)
{
  char list[5]={'a','b','c','d','e'};
  permute(list,0,N);
  return(0);
}


void permute(char list[],int k, int m)
{
  int i;
  char temp;

  if(k==m)
  {
    /* PRINT A FROM k to m! */
    for(i=0;i<N;i++){printf("%c",list[i]);}
    printf("\n");
  }
  else
  {
     for(i=k;i<m;i++)
     {
        /* swap(a[i],a[m-1]); */ 
        temp=list[i];
        list[i]=list[m-1];
        list[m-1]=temp;

        permute(list,k,m-1);

        /* swap(a[m-1],a[i]); */ 

        temp=list[m-1];
        list[m-1]=list[i];
        list[i]=temp;
       }
  }
}

 

 

3.

 

Write a C program to calculate pow(x,n)?




There are again different methods to do this in C

Brute force C program


int pow(int x, int y)
{
  if(y == 1) return x ;
  return x * pow(x, y-1) ;
}


 

 

4.

 

Write a C program which does wildcard pattern matching algorithm




Here is an example C program...


#include<stdio.h>
#define TRUE 1
#define FALSE 0

int wildcard(char *string, char *pattern);

int main()
{
  char *string = "hereheroherr";
  char *pattern = "*hero*";

  if(wildcard(string, pattern)==TRUE)
  {
    printf("\nMatch Found!\n");
  }
  else 
  {
    printf("\nMatch not found!\n");
  }
  return(0);
}

int wildcard(char *string, char *pattern)
{
  while(*string) 
  {
     switch(*pattern)
     {
        case '*': do {++pattern;}while(*pattern == '*');
                  if(!*pattern) return(TRUE);
                  while(*string){if(wildcard(pattern,string++)==TRUE)return(TRUE);}
                  return(FALSE);
        default : if(*string!=*pattern)return(FALSE); break;
     }
     ++pattern;
     ++string;
   }

   while (*pattern == '*') ++pattern;
   return !*pattern;
}



 

 

 

 

 

 

6.

 

.

How to generate fibonacci numbers? How to find out if a given number is a fibonacci number or not? Write C programs to do both.




Lets first refresh ourselves with the Fibonacci sequence


1, 1, 2, 3, 5, 8, 13, 21, 34, 55, .....


Fibonacci numbers obey the following rule


F(n) = F(n-1) + F(n-2)




Here is an iterative way to generate fibonacci numbers and also return the nth number.


int fib(int n)
{
   int f[n+1];
   f[1] = f[2] = 1;
 
   printf("\nf[1] = %d", f[1]);
   printf("\nf[2] = %d", f[2]);

   for (int i = 3; i <= n; i++)
   {
       f[i] = f[i-1] + f[i-2];
       printf("\nf[%d] = [%d]",i,f[i]);
   }
   return f[n];
}





Here is a recursive way to generate fibonacci numbers.


int fib(int n)
{
  if (n <= 2) return 1
  else return fib(n-1) + fib(n-2)
}




 

.7.

 

What Little-Endian and Big-Endian? How can I determine whether a machine's byte order is big-endian or little endian? How can we convert from one to another?




First of all, Do you know what Little-Endian and Big-Endian mean?

Little Endian means that the lower order byte of the number is stored in memory at the lowest address, and the higher order byte is stored at the highest address. That is, the little end comes first.

For example, a 4 byte, 32-bit integer


    Byte3 Byte2 Byte1 Byte0


will be arranged in memory as follows:


    Base_Address+0   Byte0   
    Base_Address+1   Byte1   
    Base_Address+2   Byte2   
    Base_Address+3   Byte3   


Intel processors use "Little Endian" byte order.


"Big Endian" means that the higher order byte of the number is stored in memory at the lowest address, and the lower order byte at the highest address. The big end comes first.


  Base_Address+0   Byte3
  Base_Address+1   Byte2
  Base_Address+2   Byte1
  Base_Address+3   Byte0


Motorola, Solaris processors use "Big Endian" byte order.

In "Little Endian" form, code which picks up a 1, 2, 4, or longer byte number proceed in the same way for all formats. They first pick up the lowest order byte at offset 0 and proceed from there. Also, because of the 1:1 relationship between address offset and byte number (offset 0 is byte 0), multiple precision mathematic routines are easy to code. In "Big Endian" form, since the high-order byte comes first, the code can test whether the number is positive or negative by looking at the byte at offset zero. Its not required to know how long the number is, nor does the code have to skip over any bytes to find the byte containing the sign information. The numbers are also stored in the order in which they are printed out, so binary to decimal routines are particularly efficient.


Here is some code to determine what is the type of your machine


int num = 1;
if(*(char *)&num == 1)
{
  printf("\nLittle-Endian\n");
}
else 
{
  printf("Big-Endian\n");
}



And here is some code to convert from one Endian to another.



int myreversefunc(int num)
{
  int byte0, byte1, byte2, byte3;    

  byte0 = (num & x000000FF) >>  0 ;
  byte1 = (num & x0000FF00) >>  8 ;
  byte2 = (num & x00FF0000) >> 16 ;
  byte3 = (num & xFF000000) >> 24 ;

  return((byte0 << 24) | (byte1 << 16) | (byte2 << 8) | (byte3 << 0));
}



 

 

8.

 

Write C code to solve the Tower of Hanoi problem.




Here is an example C program to solve the Tower Of Hanoi problem...


main()
{
    towers_of_hanio(n,'L','R','C');
}

towers_of_hanio(int n, char from, char to, char temp)
{
    if(n>0)
    {
        tower_of_hanio(n-1, from, temp, to);
        printf("\nMove disk %d from %c to %c\n", n, from, to);
        tower_of_hanio(n-1, temp, to, from);
    }
}



 

 

8.

 

Write C code to return a string from a function




This is one of the most popular interview questions

This C program wont work!


char *myfunction(int n)
{
   char buffer[20];
   sprintf(buffer, "%d", n);
   return retbuf; 
}



This wont work either!


char *myfunc1()
{
  char temp[] = "string";
  return temp;
}


char *myfunc2()
{
   char temp[] = {'s', 't', 'r', 'i', 'n', 'g', '\0'};
   return temp;
}


int main()
{
    puts(myfunc1());
    puts(myfunc2());
}




The returned pointer should be to a static buffer (like static char buffer[20];), or to a buffer passed in by the caller function, or to memory obtained using malloc(), but not to a local array.

This will work


char *myfunc()
{
   char *temp = "string";
   return temp;
}

int main()
{
   puts(someFun());
}



So will this


calling_function()

  char *string; 
  return_string(&string); 
  printf(?\n[%s]\n?, string);
}

boolean return_string(char **mode_string /*Pointer to a pointer! */) 

   *string = (char *) malloc(100 * sizeof(char)); 
   DISCARD strcpy((char *)*string, (char *)?Something?); 
}

 

9.

 

Write a C program which produces its own source code as its output




This is one of the most famous interview questions

One of the famous C programs is...


char*s="char*s=%c%s%c;main(){printf(s,34,s,34);}";main(){printf(s,34,s,34);}


So how does it work?

It's not difficult to understand this program. In the following statement,


printf(f,34,f,34,10); 


the parameter "f" not only acts as the format string, but also as a value for the %s specifier. The ASCII value of double quotes is 34, and that of new-line is 10. With these fact ready, the solution is just a matter of tracing the program.


 

 

 

                  

10.

 

Write a C progam to convert from decimal to any base (binary, hex, oct etc...)




Here is some really
cool C code


#include <stdio.h>

int main()
{
  decimal_to_anybase(10, 2);
  decimal_to_anybase(255, 16);
  getch();
}

decimal_to_anybase(int n, int base)
{
  int i, m, digits[1000], flag;
  i=0;
  
  printf("\n\n[%d] converted to base [%d] : ", n, base);
  
  while(n)
  {
     m=n%base;
     digits[i]="0123456789abcdefghijklmnopqrstuvwxyz"[m];
     n=n/base;
     i++;
   }

   //Eliminate any leading zeroes
   for(i--;i>=0;i--)
   {
     if(!flag && digits[i]!='0')flag=1;
     if(flag)printf("%c",digits[i]);
   }   
}




 

 

11.

 

Write C code to check if an integer is a power of 2 or not in a single line?




Even this is one of the most frequently asked interview questions. I really dont know whats so great in it. Nevertheless, here is a C program

Method1


if(!(num & (num - 1)) && num)
{
  // Power of 2!
}


Method2


if(((~i+1)&i)==i)
{
 //Power of 2!
}


I leave it up to you to find out how these statements work.


 

 

12.

 

Find the maximum of three integers using the ternary operator.




Here is how you do it


max = ((a>b)?((a>c)?a:c):((b>c)?b:c));



Here is another way


max = ((a>b)?a:b)>c?((a>b)?a:b):c;




Here is some code to find the max of 4 numbers...

Method1


#include <stdio.h> 
#include <stdlib.h> 

#define max2(x,y)  ((x)>(y)?(x):(y)) 
#define max4(a,b,c,d)  max2(max2((a),(b)),max2((c),(d))) 

int main ( void ) 

    printf ( "Max: %d\n", max4(10,20,30,40)); 
    printf ( "Max: %d\n", max4(10,0,3,4)); 
    return EXIT_SUCCESS; 
}



Method2


#include <stdio.h> 
#include <stdlib.h> 

int retMax(int i1, int i2, int i3, int i4) 

   return(((i1>i2)?i1:i2) > ((i3>i4)?i3:i4)? ((i1>i2)?i1:i2):((i3>i4)?i3:i4)); 


int main() 

   int val = 0 ; 

   val = retMax(10, 200, 10, 530); 
   val = retMax(9, 2, 5, 7); 

   return 0; 
}

 

 

13.

 

This is one of the very popular interview questions, so take a good look at it!.


myfunction(int *ptr)
{
  int myvar = 100;
  ptr = &myvar;
}

main()
{
 int *myptr;
 myfunction(myptr);

 //Use pointer myptr.

}


Do you think this works? It
does not!.


Arguments in C are passed by value. The called function changed the
passed copy of the pointer, and not the actual pointer.


There are two ways around this problem

Method1

Pass in the address of the pointer to the function (the function needs to accept a pointer-to-a-pointer).


calling_function()
{
  char *string;
  return_string(/* Pass the address of the pointer */&string);
  printf(?\n[%s]\n?, string);
}

boolean return_string(char **mode_string /*Pointer to a pointer! */)
{
  *string = (char *) malloc(100 * sizeof(char)); // Allocate memory to the pointer passed, not its copy.
  DISCARD strcpy((char *)*string, (char *)?Something?);
}



Method2

Make the function return the pointer.


char *myfunc()
{
  char *temp = "string";
  return temp;
}

int main()
{
  puts(myfunc());
}



 

17.

 

Write C code to dynamically allocate one, two and three dimensional arrays (using malloc())




Its pretty simple to do this in the C language if you know how to use C pointers. Here are some example C code snipptes....

One dimensional array

int *myarray = malloc(no_of_elements * sizeof(int));

//Access elements as myarray[i]




Two dimensional array

Method1


int **myarray = (int **)malloc(no_of_rows * sizeof(int *));
for(i = 0; i < no_of_rows; i++)
{
 myarray[i] = malloc(no_of_columns * sizeof(int));
}

// Access elements as myarray[i][j]



Method2 (keep the array's contents contiguous)


int **myarray = (int **)malloc(no_of_rows * sizeof(int *));
myarray[0] = malloc(no_of_rows * no_of_columns * sizeof(int));

for(i = 1; i < no_of_rows; i++)
  myarray[i] = myarray[0] + (i * no_of_columns);

// Access elements as myarray[i][j]


Method3


int *myarray = malloc(no_of_rows * no_of_columns * sizeof(int));

// Access elements using myarray[i * no_of_columns + j].



Three dimensional array

#define MAXX 3
#define MAXY 4
#define MAXZ 5

main()
{
    int ***p,i,j;
    p=(int ***) malloc(MAXX * sizeof(int ***));

    for(i=0;i<MAXX;i++)
    {
        p[i]=(int **)malloc(MAXY * sizeof(int *));
        for(j=0;j<MAXY;j++)
            p[i][j]=(int *)malloc(MAXZ * sizeof(int));
    }
        
    for(k=0;k<MAXZ;k++)
        for(i=0;i<MAXX;i++)
            for(j=0;j<MAXY;j++)
                p[i][j][k]=<something>;

}

 

 

18.

 

How would you find the size of structure without using sizeof()?


Try using pointers


struct MyStruct
{
  int i;
  int j;
};

int main()
{
   struct MyStruct *p=0;
   int size = ((char*)(p+1))-((char*)p);
   printf("\nSIZE : [%d]\nSIZE : [%d]\n", size);
   return 0;
}

 

19.

 


Write C code to implement the Binary Search algorithm.




Here is a C function


int binarySearch(int arr[],int size, int item)
{
   int left, right, middle;
   left  = 0;
   right = size-1;

   while(left<=right)
   {
      middle = ((left + right)/2);

      if(item == arr[middle])
      {
        return(middle);
      }
      
      if(item > arr[middle])
      {
        left  = middle+1;
      }
      else
      {
        right = middle-1;
      }
   }

   return(-1);
}


 

 

20.

 

How do you compare floating point numbers?




This is
Wrong!.


double a, b;

if(a == b)
{
  ...
}

The above code might not work always. Thats because of the way floating point numbers are stored.

A good way of comparing two floating point numbers is to have a accuracy threshold which is relative to the magnitude of the two floating point numbers being compared.

#include <math.h>
if(fabs(a - b) <= accurary_threshold * fabs(a))

here is a
lot of material on the net to know how floating point numbers can be compared. Got for it if you really want to understand.


Another way which
might work is something like this. I have not tested it!



int compareFloats(float f1, float f2)
{
  char *b1, *b2; 
  int i; 

  b1 = (char *)&f1; 
  b2 = (char *)&f2; 

  /* Assuming sizeof(float) is 4 bytes) */ 

  for (i = 0; i<4; i++, b1++, b2++) 
  {
    if (*b1 != *b2) 
    {
      return(NOT_EQUAL); /* You must have defined this before */ 
    }
  }

  return(EQUAL);
}



 

 

22.

 

How to swap the two nibbles in a byte ?




Try this


#include <stdio.h>

unsigned char swap_nibbles(unsigned char c)
{
  unsigned char temp1, temp2;
  temp1 = c & 0x0F;
  temp2 = c & 0xF0;
  temp1=temp1 << 4;
  temp2=temp2 >> 4;  

  return(temp2|temp1); //adding the bits
}

int main(void)
{
  char ch=0x34;
  printf("\nThe exchanged value is %x",swap_nibbles(ch));
  return 0;
}

 

 

23.

 

How to scan a string till we hit a new line using scanf()?

scanf("%[^\n]", address);


 

 

24.

 

How do you get the line numbers in C?




Use the following
Macros


__FILE__ Source file name (string constant) format "patx.c" 
__LINE__ Current source line number (integer) 
__DATE__ Date compiled (string constant)format "Dec 14 1985" 
__TIME__ Time compiled (string constant) format "15:24:26" 
__TIMESTAMP__ Compile date/time (string constant)format "Tue Nov 19 11:39:12 1997" 


Usage example


static char stamp[] =    "***\nmodule " __FILE__    "\ncompiled " __TIMESTAMP__     "\n***";

...

int main()
{   
   ...
   
   if ( (fp = fopen(fl,"r")) == NULL )   
   {      
      printf( "open failed, line %d\n%s\n",__LINE__, stamp );      
      exit( 4 );   
   }

   ...
}

And the output is something like

*** open failed, line 67 
******
module myfile.c 
compiled Mon Jan 15 11:15:56 1999 
*** 

 

 

25.

 

How to fast multiply a number by 7?




Try


(num<<3 - num)


This is same as


num*8 - num = num * (8-1) = num * 7



 

 

28.

 

Write code to round numbers




Use something like


(int)(num < 0 ? (num - 0.5) : (num + 0.5))



 

 

29.

Write a program to check if the stack grows up or down




Try noting down the address of a
local variable. Call another function with a local variable declared in it and check the address of that local variable and compare!.


#include <stdio.h>
#include <stdlib.h>

void stack(int *local1);

int main()
{
  int local1;
  stack(&local1);
  exit(0);
}

void stack(int *local1)
{
   int local2;
   printf("\nAddress of first  local : [%u]", local1);
   printf("\nAddress of second local : [%u]", &local2); 
   if(local1 < &local2)
   {
     printf("\nStack is growing downwards.\n");
   }
   else
   {
     printf("\nStack is growing upwards.\n");
   }
   printf("\n\n");
}



 

 

30.

 

Write a program to print numbers from 1 to 100 without using loops!




Another
"Yeah, I am a jerk, kick me! kind of a question. I simply dont know why they ask these questions.

Nevertheless, for the benefit of mankind...



Method1 (Using recursion)


void printUp(int startNumber, int endNumber) 

  if (startNumber > endNumber) 
    return; 
  
  printf("[%d]\n", startNumber++); 
  printUp(startNumber, endNumber); 
}


31.

 

How to reverse the bits in an interger?




Here are some ways to reverse the bits in an integer.

Method1


unsigned int num;            // Reverse the bits in this number.
unsigned int temp = num;     // temp will have the reversed bits of num.

int i;

for (i = (sizeof(num)*8-1); i; i--)
{  
 temp = temp | (num & 1);
 temp <<= 1;  
 num  >>= 1;
}

temp = temp | (num & 1);

 

 

32.

 

What is the difference between Merge Sort and Quick sort?




Both Merge-sort and Quick-sort have same time complexity i.e. O(nlogn). In merge sort the file a[1:n] was divided at its midpoint into sub-arrays which are independently sorted and later merged. Whereas, in quick sort the division into two sub-arrays is made so that the sorted sub-arrays do not need to be merged latter.


 

 

33.

 

Implement the bubble sort algorithm. How can it be improved? Write the code for selection sort, quick sort, insertion sort.




Here is the
Bubble sort algorithm


void bubble_sort(int a[], int n)
{
  int i, j, temp;

  for(j = 1; j < n; j++)
  {
     for(i = 0; i < (n - j); i++)
     {
        if(a[i] >= a[i + 1])
        {
           //Swap a[i], a[i+1]
        }
     }
  }
}


To improvise this basic algorithm, keep track of whether a particular pass results in any swap or not. If not, you can break out without wasting more cycles.


void bubble_sort(int a[], int n)
{
  int i, j, temp;
  int flag;
   
  for(j = 1; j < n; j++)
  {
     flag = 0;
     for(i = 0; i < (n - j); i++)
     {
        if(a[i] >= a[i + 1])
        {
           //Swap a[i], a[i+1]
           flag = 1;
        }
     }

     if(flag==0)break;
  }
}



This is the
selection sort algorithm


void selection_sort(int a[], int n)
{
   int i, j, small, pos, temp;

   for(i = 0; i < (n - 1); i++)
   {
      small = a[i];
      pos   = i;

      for(j = i + 1; j < n; j++)
      {
         if(a[j] < small)
         {
            small = a[j];
            pos   = j;
         }
      }
 
      temp   = a[pos];
      a[pos] = a[i];
      a[i]   = temp;
   }
}




Here is the Quick sort algorithm


int partition(int a[], int low, int high)
{
   int i, j, temp, key;

   key = a[low];
   i   = low + 1;
   j   = high;

   while(1)
   {
       while(i < high && key >= a[i])i++;
  
       while(key < a[j])j--;

       if(i < j)
       {
           temp = a[i];
           a[i] = a[j];
           a[j] = temp;
       }
       else
       {
           temp   = a[low];
           a[low] = a[j];
           a[j]   = temp;
           return(j); 
       }
   }
}


void quicksort(int a[], int low, int high)
{
   int j;
   
   if(low < high)
   {
       j = partition(a, low, high);
       quicksort(a, low, j - 1);
       quicksort(a, j + 1, high);
   }
}


int main()

  // Populate the array a
  quicksort(a, 0, n - 1);
}




Here is the Insertion sort algorithm


void insertion_sort(int a[], int n)
{
   int i, j, item;

   for(i = 0; i < n; i++)
   {
       item = a[i];
       j    = i - 1;
  
       while(j >=0 && item < a[j])
       {
          a[j + 1] = a[j];
          j--;
       }

       a[j + 1] = item;
   }
}

 

 

34.

 

What does *p++ do? Does it increment p or the value pointed by p?




The postfix "++" operator has higher precedence than prefix "*" operator. Thus, *p++ is same as *(p++); it increments the pointer p, and returns the value which p pointed to before p was incremented. If you want to increment the value pointed to by p, try (*p)++.


 

 

35.

 

What is a NULL pointer? How is it different from an unitialized pointer? How is a NULL pointer defined?




A
null pointer simply means "I am not allocated yet!" and "I am not pointing to anything yet!".

The C language definition states that for every available pointer type, there is a
special value which is called the null pointer. It is guaranteed to compare unequal to a pointer to any object or function.

A
null pointer is very different from an uninitialized pointer. A null pointer does not point to any object or function; but an uninitialized pointer can point anywhere.

There is usually a null pointer for each type of a pointer, and the internal values of these null pointers for different pointer types may be different, its up to the compiler. The & operator will never yield a null pointer, nor will a successful call to malloc() (malloc() does return a null pointer when it fails).


execl("/bin/ls", "ls", "-l", (char *)0);


In this call to execl(), the last argument has been explicitly casted to force the 0 to be treated as a pointer.

Also, if ptr is a pointer then


if(ptr){}


and


if(!ptr){}


are
perfectly valid.


How is NULL defined?, you may ask.

ANSI C allows the following definition


#define NULL ((void *)0)


NULL and 0 are interchangeable in pointer contexts.

Make sure you are able to distinguish between the following : the
null pointer, the internal representation of a null pointer, the null pointer constant (i.e, 0), the NULL macro, the ASCII null character (NUL), the null string ("").

 

 

36.

 

What is a null pointer assignment error?




This error means that the program has written, through a null (probably because its an uninitialized) pointer, to a location thats
invalid.

More to come....


 

 

 

37.

 

Does an array always get converted to a pointer? What is the difference between arr and &arr? How does one declare a pointer to an entire array?




Well,
not always.

In C, the array and pointer arithmetic is such that a pointer can be used to access an array or to simulate an array. What this means is whenever an array appears in an expression, the compiler automatically generates a pointer to the array's first element (i.e,
&a[0]).

There are
three exceptions to this rule


1. When the array is the operand of the sizeof() operator.
2. When using the & operator.
3. When the array is a string literal initializer for a character array.


Also, on a side note, the rule by which arrays decay into pointers is not applied recursively!. An array of arrays (i.e. a two-dimensional array in C) decays into a pointer to an array, not a pointer to a pointer.

If you are passing a two-dimensional array to a function:


int myarray[NO_OF_ROWS][NO_OF_COLUMNS];
myfunc(myarray);


then, the function's declaration must match:


void myfunc(int myarray[][NO_OF_COLUMNS])

or

void myfunc(int (*myarray)[NO_OF_COLUMNS])


Since the called function does not allocate space for the array, it does not need to know the overall size, so the number of rows, NO_OF_ROWS, can be omitted. The width of the array is still important, so the column dimension
NO_OF_COLUMNS must be present.




An array is never passed to a function, but a pointer to the first element of the array is passed to the function. Arrays are automatically allocated memory. They can't be relocated or resized later. Pointers must be assigned to allocated memory (by using (say) malloc), but pointers can be reassigned and made to point to other memory chunks.

So, whats the difference between
func(arr) and func(&arr)?

In C,
&arr yields a pointer to the entire array. On the other hand, a simple reference to arr returns a pointer to the first element of the array arr. Pointers to arrays (as in &arr) when subscripted or incremented, step over entire arrays, and are useful only when operating on arrays of arrays. Declaring a pointer to an entire array can be done like int (*arr)[N];, where N is the size of the array.

Also, note that
sizeof() will not report the size of an array when the array is a parameter to a function, simply because the compiler pretends that the array parameter was declared as a
pointer and sizeof reports the size of the pointer.


 

 

38.

 

Is the cast to malloc() required at all?




Before ANSI C introduced the
void * generic pointer, these casts were required because older compilers used to return a char pointer.


int *myarray;
myarray = (int *)malloc(no_of_elements * sizeof(int));


But, under ANSI Standard C, these casts are
no longer necessary as a void pointer can be assigned to any pointer. These casts are still required with C++, however.

39.

 

What does malloc() , calloc(), realloc(), free() do? What are the common problems with malloc()? Is there a way to find out how much memory a pointer was allocated?




malloc() is used to allocate memory. Its a memory manager.

calloc(m, n) is also used to allocate memory, just like malloc(). But in addition, it also zero fills the allocated memory area. The zero fill is all-bits-zero. calloc(m.n) is essentially equivalent to


p = malloc(m * n);
memset(p, 0, m * n);


The malloc() function allocates raw memory given a size in bytes. On the other hand, calloc() clears the requested memory to zeros before return a pointer to it. (It can also compute the request size given the size of the base data structure and the number of them desired.)


The most common source of problems with malloc() are


1. Writing more data to a malloc'ed region than it was allocated to hold.
2. malloc(strlen(string)) instead of (strlen(string) + 1).
3. Using pointers to memory that has been freed.
4. Freeing pointers twice.
5. Freeing pointers not obtained from malloc.
6. Trying to realloc a null pointer.



How does free() work?

Any memory allocated using malloc() realloc() must be freed using free(). In general, for every call to malloc(), there should be a corresponding call to free(). When you call free(), the memory pointed to by the passed pointer is freed. However, the value of the pointer in the caller remains unchanged. Its a good practice to set the pointer to NULL after freeing it to prevent accidental usage. The malloc()/free() implementation keeps track of the size of each block as it is allocated, so it is not required to remind it of the size when freeing it using free(). You can't use dynamically-allocated memory after you free it.

Is there a way to know how big an allocated block is?

Unfortunately there is no standard or portable way to know how big an allocated block is using the pointer to the block!. God knows why this was left out in C.


Is this a valid expression?


pointer = realloc(0, sizeof(int));


Yes, it is!


 

 

40.

 

What's the difference between const char *p, char * const p and const char * const p?





const char *p    -   This is a pointer to a constant char. One cannot change the value 
                     pointed at by p, but can change the pointer p itself.

                     *p = 'A' is illegal.
                     p  = "Hello" is legal.

                     Note that even char const *p is the same!

const * char p   -   This is a constant pointer to (non-const) char. One cannot change 
                     the pointer p, but can change the value pointed at by p.

                     *p = 'A' is legal.
                     p  = "Hello" is illegal.
 


const char * const p  -   This is a constant pointer to constant char! One cannot 
                          change the value pointed to by p nor the pointer.

                          *p = 'A' is illegal.
                          p  = "Hello" is also illegal.






To interpret these declarations, let us first consider the general form of declaration:


  [qualifier] [storage-class] type [*[*]..] [qualifier] ident ; 

                                or 

  [storage-class] [qualifier] type [*[*]..] [qualifier] ident ; 





where,


qualifier: 
            volatile 
            const 

storage-class:  

            auto            extern 
            static          register 

type: 
            void            char            short 
            int             long            float 
            double          signed          unsigned 
            enum-specifier 
            typedef-name 
            struct-or-union-specifier 




Both the forms are equivalent. Keywords in the brackets are optional. The simplest tip here is to notice the relative position of the `const' keyword with respect to the asterisk (*).


Note the following points:

 

  • If the `const' keyword is to the left of the asterisk, and is the only such keyword in the declaration, then object pointed by the pointer is constant, however, the pointer itself is variable. For example:


    const char * pcc; 
    char const * pcc; 

  • If the `const' keyword is to the right of the asterisk, and is the only such keyword in the declaration, then the object pointed by the pointer is variable, but the pointer is constant; i.e., the pointer, once initialized, will always point to the same object through out it's scope. For example:


    char * const cpc; 

  • If the `const' keyword is on both sides of the asterisk, the both the pointer and the pointed object are constant. For example:


    const char * const cpcc; 
    char const * const cpcc2; 





One can also follow the "nearness" principle; i.e.,

 

  • If the `const' keyword is nearest to the `type', then the object is constant. For example:


    char const * pcc; 

  • If the `const' keyword is nearest to the identifier, then the pointer is constant. For example:


    char * const cpc; 

  • If the `const' keyword is nearest, both to the identifier and the type, then both the pointer and the object are constant. For example:


    const char * const cpcc; 
    char const * const cpcc2; 

 

 

 

41.

 

What is a void pointer? Why can't we perform arithmetic on a void * pointer?




The void data type is used when no other data type is appropriate. A void pointer is a pointer that may point to any kind of object at all. It is used when a pointer must be specified but its type is unknown.

The compiler doesn't know the size of the pointed-to objects incase of a void * pointer. Before performing arithmetic, convert the pointer either to char * or to the pointer type you're trying to manipulate


 

 

42.

 

What do Segmentation fault, access violation, core dump and Bus error mean?




The
segmentation fault, core dump, bus error kind of errors usually mean that the program tried to access memory it shouldn't have.

Probable causes are overflow of local arrays; improper use of null pointers; corruption of the malloc() data structures; mismatched function arguments (specially variable argument functions like sprintf(), fprintf(), scanf(), printf()).

For example, the following code is a
sure shot way of inviting a segmentation fault in your program:


sprintf(buffer,
        "%s %d",
        "Hello");


So whats the difference between a bus error and a segmentation fault?

A bus error is a fatal failure in the execution of a machine language instruction resulting from the processor
detecting an anomalous condition on its bus.

Such conditions include:

- Invalid address alignment (accessing a multi-byte number at an odd address).
- Accessing a memory location outside its address space.
- Accessing a physical address that does not correspond to any device.
- Out-of-bounds array references.
- References through uninitialized or mangled pointers.


A bus error triggers a processor-level exception, which Unix translates into a "SIGBUS" signal,which if not caught, will terminate the current process. It looks like a SIGSEGV,
but the difference between the two is that SIGSEGV indicates an invalid access to valid memory, while SIGBUS indicates an access to an invalid address.

Bus errors mean different thing on different machines. On systems such as Sparcs a bus error occurs when you access memory that is not positioned correctly.

Maybe an example will help to understand how a bus error occurs


#include < stdlib.h> 
#include < stdio.h> 

int main(void) 

  char *c; 
  long int *i; 
  c = (char *) malloc(sizeof(char)); 
  c++; 
  i = (long int *)c; 
  printf("%ld", *i); 
  return 0; 



On Sparc machines long ints have to be at addresses that are multiples of four (because they are four bytes long), while chars do not (they are only one byte long so they can be put anywhere). The example code uses the char to create an invalid address, and assigns the long int to the invalid address. This causes a bus error when the long int is dereferenced.


A segfault occurs when a process tries to access memory that it is not allowed to, such as the memory at address 0 (where NULL usually points). It is easy to get a segfault, such as the following example, which dereferences NULL.



#include < stdio.h> 

int main(void) 

  char *p; 
  p = NULL; 
  putchar(*p); 
  return 0; 

 

 

43.

 

What is the difference between an array of pointers and a pointer to an array?




This is an array of pointers


int *p[10];


This is a pointer to a 10 element array


int (*p)[10];



 

 

44.

What is a memory leak?




Its an scenario where the program has lost a reference to an area in the memory. Its a programming term describing the loss of memory. This happens when the program allocates some memory but fails to return it to the system


 

 

45.

What are brk() and sbrk() used for? How are they different from malloc()?




brk() and sbrk() are the only calls of memory management in UNIX. For one value of the address, beyond the last logical data page of the process, the MMU generates a segmentation violation interrupt and UNIX kills the process. This address is known as the break address of a process. Addition of a logical page to the data space implies raising of the break address (by a multiple of a page size). Removal of an entry from the page translation table automatically lowers the break address.

brk()and sbrk() are systems calls for this process


char *brk(char *new_break);
char *sbrk(displacement)


Both calls return the old break address to the process. In brk(), the new break address desired needs to be specified as the parameter. In sbrk(), the displacement (+ve or -ve) is the difference between the new and the old break address. sbrk() is very similar to malloc() when it allocates memory (+ve displacement).

malloc() is really a memory manager and not a memory allocator since, brk/sbrk only can do memory allocations under UNIX. malloc() keeps track of occupied and free peices of memory. Each malloc request is expected to give consecutive bytes and hence malloc selects the smallest free pieces that satisfy a request. When free is called, any consecutive free pieces are coalesced into a large free piece. These is done to avoid fragmentation.

realloc() can be used only with a preallocated/malloced/realloced memory. realloc() will automatically allocate new memory and transfer maximum possible contents if the new space is not available. Hence the returned value of realloc must always be stored back into the old pointer itself.


 

 

46.

What is a dangling pointer? What are reference counters with respect to pointers?




A pointer which points to an object that no longer exists. Its a pointer referring to an area of memory that has been deallocated. Dereferencing such a pointer usually produces garbage.

Using reference counters which keep track of how many pointers are pointing to this memory location can prevent such issues. The reference counts are incremented when a new pointer starts to point to the memory location and decremented when they no longer need to point to that memory. When the reference count reaches zero, the memory can be safely freed. Also, once freed, the corresponding pointer must be set to NULL.


 

 

47.

 

Is *(*(p+i)+j) is equivalent to p[i][j]? Is num[i] == i[num] == *(num + i) == *(i + num)?

 

48.

What are near, far and huge pointers?




While working under
DOS only 1 mb of memory is accessible. Any of these memory locations are accessed using CPU registers. Under DOS, the CPU registers are only 16 bits long. Therefore, the minimum value present in a CPU register could be 0, and maximum 65,535. Then how do we access memory locations beyond 65535th byte? By using two registers (segment and offset) in conjunction. For this the total memory (1 mb) is divided into a number of units each comprising 65,536 (64 kb) locations. Each such unit is called a segment. Each segment always begins at a location number which is exactly divisible by 16. The segment register contains the address where a segment begins, whereas the offset register contains the offset of the data/code from where the segment begins. For example, let us consider the first byte in B block of video memory. The segment address of video memory is B0000h (20-bit address), whereas the offset value of the first byte in the upper 32K block of this segment is 8000h. Since 8000h is a 16-bit address it can be easily placed in the offset register, but how do we store the 20-bit address B0000h in a 16-bit segment register? For this out of B0000h only first four hex digits (16 bits) are stored in segment register. We can afford to do this because a segment address is always a multiple of 16 and hence always contains a 0 as the last digit. Therefore, the first byte in the upper 32K chunk of B block of video memory is referred using segment:offset format as B000h:8000h. Thus, the offset register works relative to segment register. Using both these, we can point to a specific location anywhere in the 1 mb address space.

Suppose we want to write a character `A' at location B000:8000. We must convert this address into a form which C understands. This is done by simply writing the segment and offset addresses side by side to obtain a 32 bit address. In our example this address would be 0xB0008000. Now whether C would support this 32 bit address or not depends upon the memory model in use. For example, if we are using a large data model (compact, large, huge) the above address is acceptable. This is because in these models all pointers to data are 32 bits long. As against this, if we are using a small data model (tiny, small, medium) the above address won't work since in these models each pointer is 16 bits long.

What if we are working in small data model and still want to access the first byte of the upper 32K chunk of B block of video memory? In such cases both Microsoft C and Turbo C provide a keyword called
far, which is used as shown below,

char far *s = 0XB0008000;

A far pointer is always treated as 32 bit pointer and contains both a segment address and an offset.

A huge pointer is also 32 bits long, again containing a segment address and an offset. However, there are a few differences between a far pointer and a huge pointer.

A near pointer is only 16 bits long, it uses the contents of CS register (if the pointer is pointing to code) or contents of DS register (if the pointer is pointing to data) for the segment part, whereas the offset part is stored in the 16-bit near pointer. Using near pointer limits your data/code to current 64 kb segment.


A far pointer (32 bit) contains the segment as well as the offset. By using far pointers we can have multiple code segments, which in turn allow you to have programs longer than 64 kb. Likewise, with far data pointers we can address more than 64 kb worth of data. However, while using far pointers some problems may crop up as is illustrated by the following program.


main( ) 

  char far *a = OX00000120; 
  char far *b = OX00100020; 
  char far *c = OX00120000; 

  if ( a == b ) 
    printf ( "Hello" ) ; 

  if ( a == c ) 
    printf ( "Hi" ) ; 

  if ( b == c ) 
    printf ( "Hello Hi" ) ; 

  if ( a > b && a > c && b > c ) 
    printf ( "Bye" ) ;  

}  



Note that all the 32 bit addresses stored in variables a, b, and c refer to the same memory location. This deduces from the method of obtaining the 20-bit physical address from the segment:offset pair. This is shown below.

 
00000  segment address left shifted by 4 bits  
0120   offset address  
--------  
00120 resultant 20 bit address 

 

00100  segment address left shifted by 4 bits  
0020   offset address  
--------  
00120 resultant 20 bit address 

 

00120  segment address left shifted by 4 bits  
0000   offset address  
--------  
00120 resultant 20 bit address 



Now if a, b and c refer to same location in memory we expect the first three ifs to be satisfied. However this doesn't happen. This is because while comparing the far pointers using == (and !=) the full 32-bit value is used and since the 32-bit values are different the ifs fail. The last if however gets satisfied, because while comparing using > (and >=, <, <= ) only the offset value is used for comparison. And the offset values of a, b and c are such that the last condition is satisfied.


These limitations are overcome if we use huge pointer instead of far pointers. Unlike far pointers huge pointers are `normalized' to avoid these problems. What is a normalized pointer? It is a 32- bit pointer which has as much of its value in the segment address as possible. Since a segment can start every 16 bytes, this means that the offset will only have a value from 0 to F.

How do we normalize a pointer? Simple. Convert it to its 20-bit address then use the the left 16 bits for the segment address and the right 4 bits for the offset address. For example, given the pointer 500D:9407, we convert it to the absolute address 594D7, which we then normalize to 594D:0007.

Huge pointers are always kept normalized. As a result, for any given memory address there is only one possible huge address - segment:offset pair for it. Run the above program using huge instead of far and now you would find that the first three ifs are satisfied, whereas the fourth fails. This is more logical than the result obtained while using far. But then there is a price to be paid for using huge pointers. Huge pointer arithmetic is done with calls to special subroutines. Because of this, huge pointer arithmetic is significantly slower than that of far or near pointers. The information presented is specific to DOS operating system only.


 

 

49.

What is the difference between malloc() and calloc()?




First lets look at the
prototypes of these two popular functions..


  #include <stdlib.h> 
  void *calloc(size_t n, size_t size); 
  void *malloc(size_t size); 


The two functions
malloc() and calloc() are functionally same in that they both allocate memory from a storage pool (generally called heap). Actually, the right thing to say is that these two functions are memory managers and not memory allocators. Memory allocation is done by OS specific routines (like brk() and sbrk()). But lets not get into that for now...



Here are some differences between these two functions..

 

  • malloc() takes one argument, whereas calloc() takes two.
  • calloc() initializes all the bits in the allocated space to zero (this is all-bits-zero!, where as malloc() does not do this.
  • A call to calloc() is equivalent to a call to malloc() followed by one to memset().

    calloc(m, n) 

    is essentially equivalent to

    p = malloc(m * n);
    memset(p, 0, m * n);

    Using calloc(), we can carry out the functionality in a faster way than a combination of malloc() and memset() probably would. You will agree that one libray call is faster than two calls. Additionally, if provided by the native CPU, calloc() could be implementated by the CPU's
    "allocate-and-initialize-to-zero" instruction.
  • The reason for providing the "n" argument is that sometimes it is required to allocate a number ("n") of uniform objects of a particular size ("size"). Database application, for instance, will have such requirements. Proper planning for the values of "n" and "size" can lead to good memory utilization.

 

 

 

50.

 

Why is sizeof() an operator and not a function?




sizeof() is a compile time operator. To calculate the size of an object, we need the type information. This type information is available only at compile time. At the end of the compilation phase, the resulting object code doesn't have (or not required to have) the type information. Of course, type information can be stored to access it at run-time, but this results in bigger object code and less performance. And most of the time, we don't need it. All the runtime environments that support run time type identification (RTTI) will retain type information even after compilation phase. But, if something can be done in compilation time itself, why do it at run time?


On a side note, something like this is illegal...


printf("%u\n", sizeof(main)); 



This asks for the size of the main function, which is actually illegal:


6.5.3.4 The sizeof operator 
The sizeof operator shall not be applied to an expression that has function type.... 



 

 

51.

What is an opaque pointer?




A pointer is said to be
opaque if the definition of the type to which it points to is not included in the current translation unit. A translation unit is the result of merging an implementation file with all its headers and header files.


 

 

52.

 

How to declare a pointer to a function?




Use something like this


int myfunc(); // The function.
int (*fp)();  // Pointer to that function.

fp = myfunc;  // Assigning the address of the function to the pointer.

(*fp)();      // Calling the function.
fp();         // Another way to call the function.




So then,
what are function pointers, huh?

In simple words, a function pointer is a pointer variable which
holds the address of a function and can be used to invoke that function indirectly. Function pointers are useful when you want to invoke seperate functions based on different scenarios. In that case, you just pass in the pointer to the function you want to be invoked. Function pointers are used extensively when writing code with call back scenarios.

For example, when writing code involving a XML parser, you pass in the pointers to your event handling functions and whenever there is a specific event, the XML parser calls your functions through their pointers. This function whose pointer is passed is generally called as the
call back function.


Here is an implementation of a
Generic linked list which uses function pointers really well...



#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct list {
    void *data;
    struct list *next;
} List;

struct check {
    int i;
    char c;
    double d;
} chk[] = { { 1, 'a', 1.1 },
          { 2, 'b', 2.2 },
          { 3, 'c', 3.3 } };






// See how the print() function takes in a pointer to a function!
void print(List *, void (*)(void *));





void insert(List **, void *, unsigned int);
void printstr(void *);
void printint(void *);
void printchar(void *);
void printcomp(void *);

List *list1, *list2, *list3, *list4;

int main(void)
{
    char c[]    = { 'a', 'b', 'c', 'd' };
    int i[]     = { 1, 2, 3, 4 };
    char *str[] = { "hello1", "hello2", "hello3", "hello4" };

    list1 = list2 = list3 = list4 = NULL;

    insert(&list1, &c[0], sizeof(char));
    insert(&list1, &c[1], sizeof(char));
    insert(&list1, &c[2], sizeof(char));
    insert(&list1, &c[3], sizeof(char));

    insert(&list2, &i[0], sizeof(int));
    insert(&list2, &i[1], sizeof(int));
    insert(&list2, &i[2], sizeof(int));
    insert(&list2, &i[3], sizeof(int));

    insert(&list3, str[0], strlen(str[0])+1);
    insert(&list3, str[1], strlen(str[0])+1);
    insert(&list3, str[2], strlen(str[0])+1);
    insert(&list3, str[3], strlen(str[0])+1);

    insert(&list4, &chk[0], sizeof chk[0]);
    insert(&list4, &chk[1], sizeof chk[1]);
    insert(&list4, &chk[2], sizeof chk[2]);

    printf("Printing characters:");
    print(list1, printchar);
    printf(" : done\n\n");

    printf("Printing integers:");
    print(list2, printint);
    printf(" : done\n\n");

    printf("Printing strings:");
    print(list3, printstr);
    printf(" : done\n\n");

    printf("Printing composite:");
    print(list4, printcomp);
    printf(" : done\n");

    return 0;
}

void insert(List **p, void *data, unsigned int n)
{
    List *temp;
    int i;

    /* Error check is ignored */
    temp = malloc(sizeof(List));
    temp->data = malloc(n);
    for (i = 0; i < n; i++)
        *(char *)(temp->data + i) = *(char *)(data + i);
    temp->next = *p;
    *p = temp;
}

void print(List *p, void (*f)(void *))
{
    while (p) 
    {
        (*f)(p->data);
        p = p->next;
    }
}

void printstr(void *str)
{
    printf(" \"%s\"", (char *)str);
}

void printint(void *n)
{
    printf(" %d", *(int *)n);
}

void printchar(void *c)
{
    printf(" %c", *(char *)c);
}

void printcomp(void *comp)
{
    struct check temp = *(struct check *)comp;
    printf(" '%d:%c:%f", temp.i, temp.c, temp.d);
}


PREV

C Functions

NEXT

 

 

 

 

 

 




Does extern in a function declaration mean anything?


The
extern in a function's declaration is sometimes used to indicate that the function's definition is in some other source file, but there is no difference between

extern int function_name();

and

int function_name();


 

 

 

55.

How can I return multiple values from a function?




You can pass pointers to locations which the function being called can populate, or have the function return a structure containing the desired values, or use global variables.


56.

What is the purpose of a function prototype?




A function prototype tells the compiler to expect a given function to be used in a given way. That is, it tells the compiler the nature of the parameters passed to the function (the quantity, type and order) and the nature of the value returned by the function.


57.

 

How to declare an array of N pointers to functions returning pointers to functions returning pointers to characters?


Declare it this way


char *(*(*a[N])())(); 


Even this seems to be correct


 (char*(*fun_ptr)())(*func[n])();



 

 

58.

 

Can we declare a function that can return a pointer to a function of the same type?


We cannot do it directly. Either have the function return a generic function pointer, with casts to adjust the types as the pointers are passed around; or have it return a structure containing only a pointer to a function returning that structure.


 

59.

If I have the name of a function in the form of a string, how can I invoke that function?




Keep a table of names and their function pointers:


int myfunc1(), myfunc2();

struct 

  char *name; 
  int (*func_ptr)(); 
} func_table[] = {"myfunc1", myfunc1,
                  "myfunc2", myfunc2,};


Search the table for the name, and call via the associated function pointer.


 

 

58.

How do I pass a variable number of function pointers to a variable argument (va_arg) function?




Glad that you thought about doing something like this!

Here is some code


#include <stdarg.h>

main()
{
  int (*p1)();
  int (*p2)();
  int fun1(), fun2();

  p1 = fun1;
  p2 = fun2;
  display("Bye", p1, p2);
}

display(char *s,...)
{
  int (*pp1)(), (*pp2)();
  va_list ptr;
  typedef int (*f)(); //This typedef is  very important.

  va_start(ptr,s);

  pp1 = va_arg(ptr, f); // va_arg(ptr, int (*)()); would NOT have worked!
  pp2 = va_arg(ptr, f);

  (*pp1)();
  (*pp2)();

}

fun1()
{
  printf("\nHello!\n");
}

fun2()
{
  printf("\nHi!\n");
}



 

 

59.

Whats wrong with the expression a[i]=i++; ? Whats a sequence point?




Although its surprising that an expression like i=i+1; is
completely valid, something like a[i]=i++; is not. This is because all accesses to an element must be to change the value of that variable. In the statement a[i]=i++; , the access to i is not for itself, but for a[i] and so its invalid. On similar lines, i=i++; or i=++i; are invalid. If you want to increment the value of i, use i=i+1; or i+=1; or i++; or ++i; and not some combination.

A
sequence point is a state in time (just after the evaluation of a full expression, or at the ||, &&, ?:, or comma operators, or just before a call to a function) at which there are no side effects.

The
ANSI/ISO C Standard states that


Between the previous and next sequence point an object shall have its stored
value modified at most once by the evaluation of an expression. Furthermore, 
the prior value shall be accessed only to determine the value to be stored.


At each
sequence point, the side effects of all previous expressions will be completed. This is why you cannot rely on expressions such as a[i] = i++;, because there is no sequence point specified for the assignment, increment or index operators, you don't know when the effect of the increment on i occurs.

The sequence points laid down in the Standard are the following:

 

  • The point of calling a function, after evaluating its arguments.
  • The end of the first operand of the && operator.
  • The end of the first operand of the || operator.
  • The end of the first operand of the ?: conditional operator.
  • The end of the each operand of the comma operator.
  • Completing the evaluation of a full expression. They are the following:

 

    • Evaluating the initializer of an auto object.
    • The expression in an ?ordinary? statement?an expression followed by semicolon.
    • The controlling expressions in do, while, if, switch or for statements.
    • The other two expressions in a for statement.
    • The expression in a return statement.



60.

 

What are #pragmas?




The directive provides a single, well-defined "escape hatch" which can be used for all sorts of (nonportable) implementation-specific controls and extensions: source listing control, structure packing, warning suppression (like lint's old /* NOTREACHED */ comments), etc.

For example


#pragma once


inside a header file is an extension implemented by some preprocessors to help make header files idempotent (to prevent a header file from included twice).


 

 

61.

 

Is ++i really faster than i = i + 1?




Anyone asking this question does not really know what he is talking about.

Any good compiler will and should generate identical code for ++i, i += 1, and i = i + 1. Compilers are meant to optimize code. The programmer should not be bother about such things. Also, it depends on the processor and compiler you are using. One needs to check the compiler's assembly language output, to see which one of the different approcahes are better, if at all.

Note that speed comes Good, well written algorithms and not from such

 

 

63.

 

What do lvalue and rvalue mean?




An
lvalue is an expression that could appear on the left-hand sign of an assignment (An object that has a location). An rvalue is any expression that has a value (and that can appear on the right-hand sign of an assignment).

The lvalue refers to the left-hand side of an assignment expression. It must always evaluate to a memory location. The rvalue represents the right-hand side of an assignment expression; it may have any meaningful combination of variables and constants.


 

 

64.

 

What is type checking?




The process by which the C compiler ensures that functions and operators use data of the appropriate type(s). This form of check helps ensure the semantic correctness of the program.


 

 

65.

 

Why can't you nest structure definitions?




This is a trick question!

You can nest structure definitions.


struct salary
{
    char empname[20];
    struct 
    {
        int dearness;
    }
    allowance;
}employee;



 

 

66.

What is a forward reference?




It is a reference to a variable or function before it is defined to the compiler. The cardinal rule of structured languages is that everything must be defined before it can be used. There are rare occasions where this is not possible. It is possible (and sometimes necessary) to define two functions in terms of each other. One will obey the cardinal rule while the other will need a forward declaration of the former in order to know of the former's existence.


67.

Can we use variables inside a switch statement? Can we use floating point numbers? Can we use expressions?




No

The only things that case be used inside a switch statement are
constants or enums. Anything else will give you a


constant expression required


error. That is something like this is
not valid


switch(i)
{
  case 1: // Something; 
          break;
  case j: // Something;
          break;
}



So is
this. You cannot switch() on strings


switch(i)
{
  case "string1" : // Something; 
                   break;
  case "string2" : // Something;
                   break;
}




This is
valid, however


switch(i)
{
  case 1:     // Something; 
              break;
  case 1*2+4: // Something;
              break;
}


This is also
valid, where t is an enum


switch(i)
{
  case 1: // Something; 
          break;
  case t: // Something;
          break;
}


Also note that the
default case does not require a break; if and only if its at the end of the switch() statement. Otherwise, even the default case requires a break;


 

68.

What is more efficient? A switch() or an if() else()?




Both are equally efficient. Usually the compiler implements them using jump instructions. But each of them has their own unique advantages.


 

69.

What is the difference between a deep copy and a shallow copy?




Deep copy involves using the contents of one object to create another instance of the same class. In a deep copy, the two objects may contain ht same information but the target object will have its own buffers and resources. the destruction of either object will not affect the remaining object. The overloaded assignment operator would create a deep copy of objects.

Shallow copy involves copying the contents of one object into another instance of the same class thus creating a mirror image. Owing to straight copying of references and pointers, the two objects will share the same externally contained contents of the other object to be unpredictable.
Using a copy constructor we simply copy the data values member by member. This method of copying is called shallow copy. If the object is a simple class, comprised of built in types and no pointers this would be acceptable. This function would use the values and the objects and its behavior would not be altered with a shallow copy, only the addresses of pointers that are members are copied and not the value the address is pointing to. The data values of the object would then be inadvertently altered by the function. When the function goes out of scope, the copy of the object with all its data is popped off the stack. If the object has any pointers a deep copy needs to be executed. With the deep copy of an object, memory is allocated for the object in free store and the elements pointed to are copied. A deep copy is used for objects that are returned from a function.


 

69.

If a is an array, is a++ valid?




No

You will get an error like


Error message : Lvalue required in function main.


Doing a++ is asking the compiler to change the
base address of the array. This the only thing that the compiler remembers about an array once its declared and it wont allow you to change the base address. If it allows, it would be unable to remember the beginning of the array.


 

70.

What is the difference between the declaration and the definition of a variable?



The
definition is the one that actually allocates space, and provides an initialization value, if any.

There can be many
declarations, but there must be exactly one definition. A definition tells the compiler to set aside storage for the variable. A declaration makes the variable known to parts of the program that may wish to use it. A variable might be defined and declared in the same statement.


 

71.

 

 

Do Global variables start out as zero?




Glad you asked!

Uninitialized variables declared with the "static" keyword are initialized to zero. Such variables are implicitly initialized to the null pointer if they are pointers, and to 0.0F if they are floating point numbers.

Local variables start out containing garbage, unless they are explicitly initialized.

Memory obtained with malloc() and realloc() is likely to contain junk, and must be initialized. Memory obtained with calloc() is all-bits-0, but
this is not necessarily useful for pointer or floating-point values (This is in contrast to Global pointers and Global floating point numbers, which s

 

 

72.

In C, const and volatile are type qualifiers. The const and volatile type qualifiers are completely independent. A common misconception is to imagine that somehow const is the opposite of volatile and vice versa. This is wrong. The keywords const and volatile can be applied to any declaration, including those of structures, unions, enumerated types or typedef names. Applying them to a declaration is called qualifying the declaration?that's why const and volatile are called type qualifiers, rather than type specifiers.

 

  • const means that something is not modifiable, so a data object that is declared with const as a part of its type specification must not be assigned to in any way during the run of a program. The main intention of introducing const objects was to allow them to be put into read-only store, and to permit compilers to do extra consistency checking in a program. Unless you defeat the intent by doing naughty things with pointers, a compiler is able to check that const objects are not modified explicitly by the user. It is very likely that the definition of the object will contain an initializer (otherwise, since you can't assign to it, how would it ever get a value?), but this is not always the case. For example, if you were accessing a hardware port at a fixed memory address and promised only to read from it, then it would be declared to be const but not initialized.
  • volatile tells the compiler that other programs will be modifying this variable in addition to the program being compiled. For example, an I/O device might need write directly into a program or data space. Meanwhile, the program itself may never directly access the memory area in question. In such a case, we would not want the compiler to optimize-out this data area that never seems to be used by the program, yet must exist for the program to function correctly in a larger context. It tells the compiler that the object is subject to sudden change for reasons which cannot be predicted from a study of the program itself, and forces every reference to such an object to be a genuine reference.
  • const volatile - Both constant and volatile.




73.

What does the typedef keyword do?




This keyword provides a
short-hand way to write variable declarations. It is not a true data typing mechanism, rather, it is syntactic "sugar coating".

For example


typedef struct node
{
  int value;
  struct node *next;
}mynode;


This can later be used to declare variables like this


mynode *ptr1;

and not by the lengthy expression

struct node *ptr1;



There are
three main reasons for using typedefs:

 

  • It makes the writing of complicated declarations a lot easier. This helps in eliminating a lot of clutter in the code.
  • It helps in achieving portability in programs. That is, if we use typedefs for data types that are machine dependent, only the typedefs need to change when the program is ported to a new platform.
  • It helps in providing better documentation for a program. For example, a node of a doubly linked list is better understood as ptrToList than just a pointer to a complicated structure.

 

 

74.

What is the difference between constants defined through #define and the constant keyword?




A constant is similar to a variable in the sense that it represents a memory location (or simply, a value). It is different from a normal variable, in that it cannot change it's value in the proram - it must stay for ever stay constant. In general, constants are a useful because they can prevent program bugs and logical errors(errors are explained later). Unintended modifications are prevented from occurring. The compiler will catch attempts to reassign new values to constants.

Constants may be defined using the preprocessor directive
#define. They may also be defined using the const keyword.

So whats the difference between these two?


#define ABC 5

and

const int abc = 5;



There are two main advantages of the second one over the first technique. First, the
type of the constant is defined. "pi" is float. This allows for some type checking by the compiler. Second, these constants are variables with a definite scope. The scope of a variable relates to parts of your program in which it is defined.

There is also one good use of the
important use of the const keyword. Suppose you want to make use of some structure data in some function. You will pass a pointer to that structure as argument to that function. But to make sure that your structure is readonly inside the function you can declare the structure argument as const in function prototype. This will prevent any accidental modification of the structure values inside the function.


 

76.

How are floating point numbers stored? Whats the IEEE format?




IEEE Standard 754 floating point is the most common representation today for real numbers on computers, including Intel-based PC's, Macintoshes, and most Unix platforms.

IEEE floating point numbers have three basic components: the sign, the exponent, and the mantissa. The mantissa is composed of the fraction and an implicit leading digit (explained below). The exponent base(2) is implicit and need not be stored.

The following figure shows the layout for single (32-bit) and double (64-bit) precision floating-point values. The number of bits for each field are shown (bit ranges are in square brackets):


                 Sign   Exponent   Fraction   Bias 
--------------------------------------------------
Single Precision 1 [31] 8 [30-23]  23 [22-00] 127 
Double Precision 1 [63] 11 [62-52] 52 [51-00] 1023 


The sign bit is as simple as it gets. 0 denotes a positive number; 1 denotes a negative number. Flipping the value of this bit flips the sign of the number.

The exponent field needs to represent both positive and negative exponents. To do this, a bias is added to the actual exponent in order to get the stored exponent. For IEEE single-precision floats, this value is 127. Thus, an exponent of zero means that 127 is stored in the exponent field. A stored value of 200 indicates an exponent of (200-127), or 73. For reasons discussed later, exponents of -127 (all 0s) and +128 (all 1s) are reserved for special numbers. For double precision, the exponent field is 11 bits, and has a bias of 1023.

The mantissa, also known as the significand, represents the precision bits of the number. It is composed of an implicit leading bit and the fraction bits. To find out the value of the implicit leading bit, consider that any number can be expressed in scientific notation in many different ways. For example, the number five can be represented as any of these:


        5.00 × 100
        0.05 × 10 ^ 2
        5000 × 10 ^ -3

In order to maximize the quantity of representable numbers, floating-point numbers are typically stored in normalized form. This basically puts the radix point after the first non-zero digit. In normalized form, five is represented as 5.0 × 100. A nice little optimization is available to us in base two, since the only possible non-zero digit is 1. Thus, we can just assume a leading digit of 1, and don't need to represent it explicitly. As a result, the mantissa has effectively 24 bits of resolution, by way of 23 fraction bits.

So, to sum up:


1. The sign bit is 0 for positive, 1 for negative. 
2. The exponent's base is two. 
3. The exponent field contains 127 plus the true exponent for single-precision, 
   or 1023 plus the true exponent for double precision. 
4. The first bit of the mantissa is typically assumed to be 1.f, where f is the 
   field of fraction bits. 



 

 

79.

Why do structures get padded? Why does sizeof() return a larger size?

Padding enables the CPU to access the members faster. If they are not aligned (say to word boundaries), then accessing them might take up more time. So the padding results in faster access. This is also required to ensure that alignment properties are preserved when an array of contiguous structures is allocated. Even if the structure is not part of an array, the end padding remains, so that sizeof() can always return a consistent size.


 

 

80.

Can we determine the offset of a field within a structure and directly access that element?




The offsetof() macro(<stddef.h>) does exactly this.

A probable implementation of the macro is


#define offsetof(type, mem) ((size_t)((char *)&((type *)0)->mem - (char *)(type *)0))


This can be used as follows. The offset of field a in struct mystruct is


offset_of_a = offsetof(struct mystruct, a)


If structpointer is a pointer to an instance of this structure, and field a is an int, its value can be set indirectly with


*(int *)((char *)structpointer + offset_of_a) = some_value;



 

 

81.

What are bit fields in structures?




To
avoid wastage of memory in structures, a group of bits can be packed together into an integer and its called a bit field.


struct tag-name
{
    data-type name1:bit-length;
    data-type name2:bit-length;
    ...
    ...
    data-type nameN:bit-length;
}


A real example


struct student;
{
   char name[30];
   unsigned sex:1;
   unsigned age:5;
   unsigned rollno:7;
   unsigned branch:2;   
};

struct student a[100];

scanf("%d", &sex);
a[i].sex=sex;


There are some
limitations with respect to these bit fields, however:


1. Cannot scanf() directly into bit fields.
2. Pointers cannot be used on bit fields to access them.
3. Cannot have an array of bit fields.


The main use of bitfields is either to allow tight packing of data or to be able to specify the fields within some externally produced data files. C gives no guarantee of the ordering of fields within machine words, so if you do use them for the latter reason, you program will not only be non-portable, it will be compiler-dependent too. The Standard says that fields are packed into ?storage units?, which are typically machine words. The packing order, and whether or not a bitfield may cross a storage unit boundary, are implementation defined. To force alignment to a storage unit boundary, a zero width field is used before the one that you want to have aligned. Be careful using them. It can require a surprising amount of run-time code to manipulate these things and you can end up using more space than they save. Bit fields do not have addresses?you can't have pointers to them or arrays of them.


 

82.

What is a union? Where does one use unions? What are the limitations of unions?




A
union is a variable type that can contain many different variables (like a structure), but only actually holds one of them at a time (not like a structure). This can save memory if you have a group of data where only one of the types is used at a time. The size of a union is equal to the size of it's largest data member. The C compiler allocates just enough space for the largest member. This is because only one member can be used at a time, so the size of the largest, is the most you will need. Here is an example:


union person
{
   int age;
   char name[100];
}person1;


The union above could be used to either store the
age or it could be used to hold the name of the person. There are cases when you would want one or the other, but not both (This is a bad example, but you get the point). To access the fields of a union, use the dot operator(.) just as you would for a structure. When a value is assigned to one member, the other member(s) get whipped out since they share the same memory. Using the example above, the precise time can be accessed like this:


person1.age;


In larger programs it may be difficult to keep track of which field is the currently used field. This is usually handled by using another variable to keep track of that. For example, you might use an integer called
field. When field equals one, the age is used. If field is two, then name is used. The C compiler does no more than work out what the biggest member in a union can be and allocates enough storage (appropriately aligned if neccessary). In particular, no checking is done to make sure that the right sort of use is made of the members. That is your task, and you'll soon find out if you get it wrong. The members of a union all start at the same address?there is guaranteed to be no padding in front of any of them.

ANSI Standard C allows an initializer for the
first member of a union. There is no standard way of initializing any other member (nor, under a pre-ANSI compiler, is there generally any way of initializing a union at all).

It is because of unions that structures cannot be compared for equality. The possibility that a structure might contain a union makes it hard to compare such structures; the compiler can't tell what the union currently contains and so wouldn't know how to compare the structures. This sounds a bit hard to swallow and isn't 100% true, most structures don't contain unions, but there is also a philosophical issue at stake about just what is meant by "equality" when applied to structures. Anyhow, the union business gives the Standard a good excuse to avoid the issue by not supporting structure comparison.


 

 

84.

How should we write a multi-statement macro?




This is the
correct way to write a multi statement macro.


#define MYMACRO(argument1, argument2) do { \
                                 stament1; \
                                 stament2; \
                                } while(1) /* no trailing semicolon */



 

 

85.

 

What is the token pasting operator and stringizing operator in C?




Token pasting operator

ANSI has introduced a well-defined token-pasting operator, ##, which can be used like this:


#define f(g,g2) g##g2

main()
{
  int var12=100;
  printf("%d",f(var,12));
}


O/P

100 





Stringizing operator


#define sum(xy) printf(#xy " = %f\n",xy);

main()
{
    sum(a+b); // As good as printf("a+b = %f\n", a+b);
}





So what does the message
"warning: macro replacement within a string literal" mean?


#define TRACE(var, fmt) printf("TRACE: var = fmt\n", var)
TRACE(i, %d);

gets expanded as

printf("TRACE: i = %d\n", i);


In other words, macro parameters were expanded even inside string literals and character constants. Macro expansion is *not* defined in this way by K&R or by Standard C. When you do want to turn macro arguments into
strings, you can use the new # preprocessing operator, along with string literal concatenation:


#define TRACE(var, fmt) printf("TRACE: " #var " = " #fmt "\n", var)



See and try to understand this
special application of the strigizing operator!


#define Str(x) #x
#define Xstr(x) Str(x)
#define OP plus

char *opname = Xstr(OP); //This code sets opname to "plus" rather than "OP".



Here are some more examples

Example1


Define a macro DEBUG such that the following program

int main()
{
  int x=4;
  float a = 3.14;
  char ch = 'A';

  DEBUG(x, %d);
  DEBUG(a, %f);
  DEBUG(ch, %c);
}

outputs

DEBUG: x=4
DEBUG: y=3.140000
DEBUG: ch=A

The macro would be 

#define DEBUG(var, fmt) printf("DEBUG:" #var "=" #fmt "\n", var);


Example2


Write a macro PRINT for the following program

int main()
{
 int x=4, y=4, z=5; 
 int a=1, b=2, c=3;
 PRINT(x,y,z); 
 PRINT(a,b,c);
}

such that it outputs

x=4 y=4 z=5
a=1 b=2 c=3

Here is a macro that will do this

#define PRINT(v1,v2,v3) printf("\n" #v1 "=%d" #v2 "=%d" #v3 "=%d", v1, v2, v3);



 

 

88.

 

What should go in header files? How to prevent a header file being included twice? Whats wrong with including more headers?




Generally, a header (.h) file should have (A header file
need not have a .h extension, its just a convention):


1. Macro definitions (preprocessor #defines).
2. Structure, union, and enumeration declarations.
3. Any typedef declarations.
4. External function declarations.
5. Global variable declarations.


Put declarations / definitions in header files if they will be shared between several other files. If some of the definitions / declarations should be kept private to some .c file, don;t add it to the header files.

How does one prevent a header file from included
twice?. Including header files twice can lead to multiple-definition errors.

There are two methods to prevent a header file from included twice

Method1


#ifndef HEADER_FILE
  #define HEADER_FILE
  ...header file contents...
#endif


Method2

A line like


#pragma once


inside a header file is an extension implemented by some preprocessors to help make header files idempotent (to prevent a header file from included twice).


So, what's the difference between #include <> and #include "" ?

The
<> syntax is typically used with Standard or system-supplied headers, while "" is typically used for a program's own header files. Headers named with <> syntax are searched for in one or more standard places. Header files named with font class=announcelink>"" syntax are first searched for in the "current directory," then (if not found) in the same standard places.


What happens if you include unwanted headers?
You will end up increasing the size of your executables!


 

 

89.

How to check if a file is a binary file or an ascii file?




Here is some sample C code. The idea is to check the bytes in the file to see if they are ASCII or not...


#include <stdio.h>

int main(int argc, char *argv[])
{
 unsigned char ch;
 FILE *file;
 int binaryFile = FALSE;

 file = fopen(<FILE_PATH>, "rb");            // Open in Binary mode for the first time.


 while((fread(&ch, 1, 1, file) == 1) && (binaryFile == FALSE))
 {
    if(ch < 9 || ch == 11 || (ch > 13 && ch < 32) || ch == 255)
    {
       binaryFile = 1;                  
    }
 }

 fclose(file);   

 if(binaryFile)
    file = fopen(<FILE_PATH>, "rb");   
 else                                  
    file = fopen(<FILE_PATH>, "r");


 if(binaryFile)
 {
    while(fread(&ch, 1, 1, file) == 1)  
    {
      // Do whatever you want here with the binary file byte...
    }
 }
 else                               
 {
    while(fread(&ch, 1, 1, file) == 1) 
    {
      // This is ASCII data, can easily print it!
      putchar(ch);          
    }
 }

 fclose(file);

 return(0); 
}

 

 

90.

What is the difference between char *a and char a[]?




There is a
lot of difference!


char a[] = "string";
char *a = "string";


The declaration char a[] asks for space for 7 characters
and see that its known by the name "a". In contrast, the declaration char *a, asks for a place that holds a pointer, to be known by the name "a". This pointer "a" can point anywhere. In this case its pointing to an anonymous array of 7 characters, which does have any name in particular. Its just present in memory with a pointer keeping track of its location.



char a[] = "string";

   +----+----+----+----+----+----+------+
a: | s  |  t |  r |  i |  n |  g | '\0' |
   +----+----+----+----+----+----+------+
   a[0] a[1] a[2] a[3] a[4] a[5] a[6]


char *a = "string";

+-----+          +---+---+---+---+---+---+------+
|  a: | *======> | s | t | r | i | n | g | '\0' |
+-----+          +---+---+---+---+---+---+------+
Pointer          Anonymous array



It is curcial to know that
a[3] generates different code depending on whether a is an array or a pointer. When the compiler sees the expression a[3] and if a is an array, it starts at the location "a", goes three elements past it, and returns the character there. When it sees the expression a[3] and if a is a pointer, it starts at the location "a", gets the pointer value there, adds 3 to the pointer value, and gets the character pointed to by that value.

If
a is an array, a[3] is three places past a. If a is a pointer, then a[3] is three places past the memory location pointed to by a. In the example above, both a[3] and a[3] return the same character, but the way they do it is different!

Doing something like this would be
illegal.


char *p = "hello, world!";
p[0] = 'H';



 







Comments

Popular posts from this blog

Kth most frequent Character in a given String

[16 Feb 2020] Given an array where every element occurs three times, except one element which occurs only once.

[17 Feb 2020] Given an array of integers, find the nearest smaller number for every element such that the smaller element is on left side.