C Fundamental Question and Answer for Interview

8. What is the difference between goto and long jmp( ) and setjmp()?
A goto statement implements a local jump of program execution, and the longjmp() and setjmp() functions implement a nonlocal, or far, jump of program execution. Generally, a jump in execution of any kind should be avoided because it is not considered good programming practice to use such statements as goto and longjmp in your program.

A goto statement simply bypasses code in your program and jumps to a predefined position. To use the goto statement, you give it a labeled position to jump to. This predefined position must be within the same function. You cannot implement gotos between functions. Here is an example of a goto statement:


void bad_programmers_function(void)
{
     int x;
     printf("Excuse me while I count to 5000...n");
     x = 1;
     while (1)
     {
          printf("%dn", x);
          if (x == 5000)
               goto all_done;
          else
               x = x + 1;
     }
all_done:
     printf("Whew! That wasn't so bad, was it?n");
}

This example could have been written much better, avoiding the use of a goto statement. Here is an example of an improved implementation:


void better_function(void)
{
     int x;
     printf("Excuse me while I count to 5000...n");
     for (x=1; x<=5000; x++)
          printf("%dn", x);
     printf("Whew! That wasn't so bad, was it?n");
}

As previously mentioned, the longjmp() and setjmp() functions implement a nonlocal goto. When your program calls setjmp(), the current state of your program is saved in a structure of type jmp_buf. Later, your program can call the longjmp() function to restore the program's state as it was when you called setjmp(). Unlike the goto statement, the longjmp() and setjmp() functions do not need to be implemented in the same function.

However, there is a major drawback to using these functions: your program, when restored to its previously saved state, will lose its references to any dynamically allocated memory between the longjmp() and the setjmp(). This means you will waste memory for every malloc() or calloc() you have implemented between your longjmp() and setjmp(), and your program will be horribly inefficient. It is highly recommended that you avoid using functions such as longjmp() and setjmp() because they, like the goto statement, are quite often an indication of poor programming practice.

Here is an example of the longjmp() and setjmp() functions:


#include 
#include 
jmp_buf saved_state;
void main(void);
void call_longjmp(void);
void main(void)
{
     int ret_code;
     printf("The current state of the program is being saved...n");
     ret_code = setjmp(saved_state);
     if (ret_code == 1)
     {
          printf("The longjmp function has been called.n");
          printf("The program's previous state has been restored.n");
          exit(0);
     }
     printf("I am about to call longjmp andn");
     printf("return to the previous program state...n");
     call_longjmp();
}
void call_longjmp(void)
{
     longjmp(saved_state, 1);
}

9. What is an lvalue?
An lvalue is an expression to which a value can be assigned. The lvalue expression is located on the left side of an assignment statement, whereas an rvalue is located on the right side of an assignment statement. Each assignment statement must have an lvalue and an rvalue. The lvalue expression must reference a storable variable in memory. It cannot be a constant. For instance, the following lines show a few examples of lvalues:


int x;
int* p_int;
x = 1;
*p_int = 5;

The variable x is an integer, which is a storable location in memory. Therefore, the statement x = 1 qualifies x to be an lvalue. Notice the second assignment statement, *p_int = 5. By using the * modifier to reference the area of memory that p_int points to, *p_int is qualified as an lvalue. In contrast, here are a few examples of what would not be considered lvalues:


#define CONST_VAL 10
int x;
/* example 1 */
1 = x;
/* example 2 */
CONST_VAL = 5;

In both statements, the left side of the statement evaluates to a constant value that cannot be changed because constants do not represent storable locations in memory. Therefore, these two assignment statements do not contain lvalues and will be flagged by your compiler as errors.

10. Can an array be an lvalue?
Is an array an expression to which we can assign a value? The answer to this question is no, because an array is composed of several separate array elements that cannot be treated as a whole for assignment purposes. The following statement is therefore illegal:

int x[5], y[5];

x = y;

You could, however, use a for loop to iterate through each element of the array and assign values individually, such as in this example:


int i;
int x[5];
int y[5];
...
for (i=0; i<5; i++)
     x[i] = y[i]
...

Additionally, you might want to copy the whole array all at once. You can do so using a library function such as the memcpy() function, which is shown here:

memcpy ( x, y, sizeof ( y ) ) ;

It should be noted here that unlike arrays, structures can be treated as lvalues. Thus, you can assign one structure variable to another structure variable of the same type, such as this:


typedef struct t_name
{
     char last_name[25];
     char first_name[15];
     char middle_init[2];
} NAME;
...
NAME my_name, your_name;
...
your_name = my_name;
...

In the preceding example, the entire contents of the my_name structure were copied into the your_name structure. This is essentially the same as the following line:

memcpy ( your_name, my_name, sizeof ( your_name ) );

11. What is an rvalue?
rvalue can be defined as an expression that can be assigned to an lvalue. The rvalue appears on the right side of an assignment statement.

Unlike an lvalue, an rvalue can be a constant or an expression, as shown here:


int x, y;
x = 1;         /* 1 is an rvalue; x is an lvalue */
y = (x + 1);   /* (x + 1) is an rvalue; y is an lvalue */

An assignment statement must have both an lvalue and an rvalue. Therefore, the following statement would not compile because it is missing an rvalue:

int x; x = void_function_call () /* the function void_function_call() returns nothing */

If the function had returned an integer, it would be considered an rvalue because it evaluates into something that the lvalue, x, can store.

12. Is left-to-right or right-to-left order guaranteed for operator precedence?
The simple answer to this question is neither. The C language does not always evaluate left-to-right or right-to-left. Generally, function calls are evaluated first, followed by complex expressions and then simple expressions.

Additionally, most of today's popular C compilers often rearrange the order in which the expression is evaluated in order to get better optimized code. You therefore should always implicitly define your operator precedence by using parentheses.

For example, consider the following expression:

a = b + c/d / function_call() * 5,

The way this expression is to be evaluated is totally ambiguous, and you probably will not get the results you want. Instead, try writing it by using implicit operator precedence:

a = b + (((c/d) / function_call()) * 5)

Using this method, you can be assured that your expression will be evaluated properly and that the compiler will not rearrange operators for optimization purposes.

13. What is the difference between ++var and var++?
The ++ operator is called the increment operator. When the operator is placed before the variable (++var), the variable is incremented by 1 before it is used in the expression. When the operator is placed after the variable (var++), the expression is evaluated, and then the variable is incremented by 1.

The same holds true for the decrement operator (--). When the operator is placed before the variable, you are said to have a prefix operation. When the operator is placed after the variable, you are said to have a postfix operation.

For instance, consider the following example of postfix incrementation:


int x, y;
x = 1;
y = (x++ * 5);

In this example, postfix incrementation is used, and x is not incremented until after the evaluation of the expression is done. Therefore, y evaluates to 1 times 5, or 5. After the evaluation, x is incremented to 2.

Now look at an example using prefix incrementation:

int x, y;
x = 1;
y = (++x * 5);

This example is the same as the first one, except that this example uses prefix incrementation rather than postfix. Therefore, x is incremented before the expression is evaluated, making it 2. Hence, y evaluates to 2 times 5, or 10.

14. What does the modulus operator do?
The modulus operator (%) gives the remainder of two divided numbers. For instance, consider the following portion of code:

x = 15/7

If x were an integer, the resulting value of x would be 2. However, consider what would happen if you were to apply the modulus operator to the same equation:

x = 15%7

The result of this expression would be the remainder of 15 divided by 7, or 1. This is to say that 15 divided by 7 is 2 with a remainder of 1.

The modulus operator is commonly used to determine whether one number is evenly divisible into another. For instance, if you wanted to print every third letter of the alphabet, you would use the following code:


int x;
for (x=1; x<=26; x++)
     if ((x%3) == 0)
          printf("%c", x+64);

The preceding example would output the string "cfilorux", which represents every third letter in the alphabet.

User Answer

  1. Be the first one to express your answer.

C Fundamental Question and Answer for Interview