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.
#include<bits/stdc++.h>
using namespace std;
string findSum(string str1, string str2)
{
if (str1.length() > str2.length())
swap(str1, str2);
string str = "" ;
int n1 = str1.length(), n2 = str2.length();
reverse(str1.begin(), str1.end());
reverse(str2.begin(), str2.end());
int carry = 0;
for ( int i=0; i<n1; i++)
{
int sum = ((str1[i]- '0' )+(str2[i]- '0' )+carry);
str.push_back(sum%10 + '0' );
carry = sum/10;
}
for ( int i=n1; i<n2; i++)
{
int sum = ((str2[i]- '0' )+carry);
str.push_back(sum%10 + '0' );
carry = sum/10;
}
if (carry)
str.push_back(carry+ '0' );
reverse(str.begin(), str.end());
return str;
}
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:
- 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.
- 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}
- 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:
#include <bits/stdc++.h>
using namespace std;
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);
}
}
}
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);
}
}
}
void findSum(string s1, string s2)
{
int i;
vector< int > Ints1, Ints2;
vector< int > Fracs1, Fracs2;
for (i = s1.length() - 1; i > -1; i--) {
if (s1[i] == '.' ) {
break ;
}
Fracs1.push_back(s1[i] - '0' );
}
i--;
for (; i > -1; i--) {
Ints1.push_back(s1[i] - '0' );
}
for (i = s2.length() - 1; i > -1; i--) {
if (s2[i] == '.' ) {
break ;
}
Fracs2.push_back(s2[i] - '0' );
}
i--;
for (; i > -1; i--) {
Ints2.push_back(s2[i] - '0' );
}
makeEqualAtFront(Fracs1, Fracs2);
makeEqualAtback(Ints1, Ints2);
int n = Fracs1.size();
int m = Fracs2.size();
i = 0;
int carry = 0;
while (i < n && i < m) {
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;
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);
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];
}
}
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
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:
#include <bits/stdc++.h>
using namespace std;
void checkPalindrome(string S)
{
int N = S.size();
bool first_half = true ;
bool second_half = true ;
int cnt = (N / 2) - 1;
for ( int i = 0; i < ((N / 2) / 2); i++) {
if (S[i] != S[cnt]) {
first_half = false ;
break ;
}
if (S[N / 2 + i] != S[N / 2 + cnt]) {
second_half - false ;
break ;
}
cnt--;
}
if (first_half && second_half) {
cout << "Yes" << endl;
}
else {
cout << "No" << endl;
}
}
int main()
{
string S = "momdad" ;
checkPalindrome(S);
return 0;
}
|
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
Post a Comment