Interview Preparation Exam  >  Interview Preparation Notes  >  Placement Papers - Technical & HR Questions  >  Variables and Data Storage (Part - 2), C Programming Interview Questions

Variables and Data Storage (Part - 2), C Programming Interview Questions | Placement Papers - Technical & HR Questions - Interview Preparation PDF Download

8. When should the const modifier be used?

There are several reasons to use const pointers. First, it allows the compiler to catch errors in which code accidentally changes the value of a variable, as in

 

while (*str = 0) /* programmer meant to write *str != 0 */
{
    /* some code here */
    str++;
}

 

in which the = sign is a typographical error. Without the const in the declaration of str, the program would compile but not run properly.

Another reason is efficiency. The compiler might be able to make certain optimizations to the code generated if it knows that a variable will not be changed.

Any function parameter which points to data that is not modified by the function or by any function it calls should declare the pointer a pointer to const. Function parameters that are passed by value (rather than through a pointer) can be declared const if neither the function nor any function it calls modifies the data.

In practice, however, such parameters are usually declared const only if it might be more efficient for the compiler to access the data through a pointer than by copying it.


9. How reliable are floating-point comparisons?

Floating-point numbers are the "black art" of computer programming. One reason why this is so is that there is no optimal way to represent an arbitrary number. The Institute of Electrical and Electronic Engineers (IEEE) has developed a standard for the representation of floating-point numbers, but you cannot guarantee that every machine you use will conform to the standard.

Even if your machine does conform to the standard, there are deeper issues. It can be shown mathematically that there are an infinite number of "real" numbers between any two numbers. For the computer to distinguish between two numbers, the bits that represent them must differ. To represent an infinite number of different bit patterns would take an infinite number of bits. Because the computer must represent a large range of numbers in a small number of bits (usually 32 to 64 bits), it has to make approximate representations of most numbers.

Because floating-point numbers are so tricky to deal with, it's generally bad practice to compare a floating- point number for equality with anything. Inequalities are much safer. If, for instance, you want to step through a range of numbers in small increments, you might write this:

 

#include <stdio.h>
const float first = 0.0;
const float last = 70.0;
const float small = 0.007;
main()
{
        float   f;
        for (f = first; f != last && f < last + 1.0; f += small)
                ;
        printf("f is now %g\n", f);
}

 

However, rounding errors and small differences in the representation of the variable small might cause f to never be equal to last (it might go from being just under it to being just over it). Thus, the loop would go past the value last. The inequality f < last + 1.0 has been added to prevent the program from running on for a very long time if this happens. If you run this program and the value printed for f is 71 or more, this is what has happened.

A safer way to write this loop is to use the inequality f < last to test for the loop ending, as in this example:

 

float   f;
for (f = first; f < last; f += small)
        ;

 

You could even precompute the number of times the loop should be executed and use an integer to count iterations of the loop, as in this example:

 

float   f;
int     count = (last - first) / small;
for (f = first; count-- > 0; f += small)


10. How can you determine the maximum value that a numeric variable can hold?

The easiest way to find out how large or small a number that a particular type can hold is to use the values defined in the ANSI standard header file limits.h. This file contains many useful constants defining the values that can be held by various types, including these:

 

Value   Description
CHAR_BIT - Number of bits in a char
CHAR_MAX - Maximum decimal integer value of a char
CHAR_MIN - Minimum decimal integer value of a char
MB_LEN_MAX - Maximum number of bytes in a multibyte character
INT_MAX - Maximum decimal value of an int
INT_MIN - Minimum decimal value of an int
LONG_MAX - Maximum decimal value of a long
LONG_MIN - Minimum decimal value of a long
SCHAR_MAX - Maximum decimal integer value of a signed char
SCHAR_MIN - Minimum decimal integer value of a signed char
SHRT_MAX - Maximum decimal value of a short
SHRT_MIN - Minimum decimal value of a short
UCHAR_MAX - Maximum decimal integer value of unsigned char
UINT_MAX - Maximum decimal value of an unsigned integer
ULONG_MAX - Maximum decimal value of an unsigned long int
USHRT_MAX - Maximum decimal value of an unsigned short int

For integral types, on a machine that uses two's complement arithmetic (which is just about any machine you're likely to use), a signed type can hold numbers from -2(number of bits - 1) to +2(number of bits - 1) - 1.

An unsigned type can hold values from 0 to +2(number of bits)- 1. For instance, a 16-bit signed integer can hold numbers from -215(-32768) to +215 - 1 (32767).


11. Are there any problems with performing mathematical operations on different variable types?

C has three categories of built-in data types: pointer types, integral types, and floating-point types. Pointer types are the most restrictive in terms of the operations that can be performed on them. They are limited to

- subtraction of two pointers, valid only when both pointers point to elements in the same array. The result is the same as subtracting the integer subscripts corresponding to the two pointers.

+ addition of a pointer and an integral type. The result is a pointer that points to the element which would be selected by that integer.

Floating-point types consist of the built-in types float, double, and long double. Integral types consist of char, unsigned char, short, unsigned short, int, unsigned int, long, and unsigned long. All of these types can have the following arithmetic operations performed on them:

+ Addition

- Subtraction

* Multiplication

/ Division

Integral types also can have those four operations performed on them, as well as the following operations: %Modulo or remainder of division

<< Shift left

>> Shift right

& Bitwise AND operation

| Bitwise OR operation

^ Bitwise exclusive OR operation

! Logical negative operation

~ Bitwise "one's complement" operation

Although C permits "mixed mode" expressions (an arithmetic expression involving different types), it actually converts the types to be the same type before performing the operations (except for the case of pointer arithmetic described previously). The process of automatic type conversion is called "operator promotion."


12. What is operator promotion?

If an operation is specified with operands of two different types, they are converted to the smallest type that can hold both values. The result has the same type as the two operands wind up having. To interpret the rules, read the following table from the top down, and stop at the first rule that applies.

 

If Either Operand Is   And the Other Is   Change Them To
long double - any other type - long double
double - any smaller type - double
float - any smaller type - float
unsigned long - any integral type - unsigned long
long - unsigned > LONG_MAX - long
long - any smaller type - long
unsigned - any signed type - unsigned

The following example code illustrates some cases of operator promotion. The variable f1 is set to 3/4. Because both 3 and 4 are integers, integer division is performed, and the result is the integer 0. The variablef2 is set to 3/4.0. Because 4.0 is a float, the number 3 is converted to a float as well, and the result is the float 0.75.

 

#include <stdio.h>
main()
{
    float f1 = 3 / 4;
    float f2 = 3 / 4.0;
    printf("3 / 4 == %g or %g depending on the type used.\n", f1, f2);
}


13. When should a type cast be used?

There are two situations in which to use a type cast. The first use is to change the type of an operand to an arithmetic operation so that the operation will be performed properly. The variable f1 is set to the result of dividing the integer i by the integer j. The result is 0, because integer division is used. The variable f2 is set to the result of dividing i by j as well. However, the (float) type cast causes i to be converted to a float. That in turn causes floating-point division to be used and gives the result 0.75.

 

#include <stdio.h>
main()
{
    int i = 3;
    int j = 4;
    float f1 = i / j;
    float f2 = (float) i / j;
    printf("3 / 4 == %g or %g depending on the type used.\n", f1, f2);
}

 

The second case is to cast pointer types to and from void * in order to interface with functions that expect or return void pointers. For example, the following line type casts the return value of the call to malloc() to be a pointer to a foo structure.

struct foo *p = (struct foo *) malloc(sizeof(struct foo));

 

14. When should a type cast not be used?

A type cast should not be used to override a const or volatile declaration. Overriding these type modifiers can cause the program to fail to run correctly.

A type cast should not be used to turn a pointer to one type of structure or data type into another. In the rare events in which this action is beneficial, using a union to hold the values makes the programmer's intentions clearer.


15. Is it acceptable to declare/define a variable in a C header?

global variable that must be accessed from more than one file can and should be declared in a header file. In addition, such a variable must be defined in one source file. Variables should not be defined in header files, because the header file can be included in multiple source files, which would cause multiple definitions of the variable.

The ANSI C standard will allow multiple external definitions, provided that there is only one initialization. But because there's really no advantage to using this feature, it's probably best to avoid it and maintain a higher level of portability.

"Global" variables that do not have to be accessed from more than one file should be declared static and should not appear in a header file.


16. What is the difference between declaring a variable and defining a variable?

Declaring a variable means describing its type to the compiler but not allocating any space for it. Defining a variable means declaring it and also allocating space to hold the variable. You can also initialize a variable at the time it is defined. Here is a declaration of a variable and a structure, and two variable definitions, one with initialization:

 

extern int decl1;  /* this is a declaration */
struct decl2 
{
    int member;
}; /* this just declares the type--no variable mentioned */
int     def1 = 8;      /* this is a definition */
int     def2;          /* this is a definition */

 

To put it another way, a declaration says to the compiler, "Somewhere in my program will be a variable with this name, and this is what type it is." A definition says, "Right here is this variable with this name and this type."

A variable can be declared many times, but it must be defined exactly once. For this reason, definitions do not belong in header files, where they might get #included into more than one place in your program.

 

17. Can static variables be declared in a header file?

You can't declare a static variable without defining it as well (this is because the storage class modifiers static and extern are mutually exclusive). A static variable can be defined in a header file, but this would cause each source file that included the header file to have its own private copy of the variable, which is probably not what was intended.


18. What is the benefit of using const for declaring constants?

The benefit of using the const keyword is that the compiler might be able to make optimizations based on the knowledge that the value of the variable will not change. In addition, the compiler will try to ensure that the values won't be changed inadvertently.

Of course, the same benefits apply to #defined constants. The reason to use const rather than #define to define a constant is that a const variable can be of any type (such as a struct, which can't be represented by a #defined constant). Also, because a const variable is a real variable, it has an address that can be used, if needed, and it resides in only one place in memory.

The document Variables and Data Storage (Part - 2), C Programming Interview Questions | Placement Papers - Technical & HR Questions - Interview Preparation is a part of the Interview Preparation Course Placement Papers - Technical & HR Questions.
All you need of Interview Preparation at this link: Interview Preparation
85 docs|57 tests

Top Courses for Interview Preparation

Explore Courses for Interview Preparation exam

Top Courses for Interview Preparation

Signup for Free!
Signup to see your scores go up within 7 days! Learn & Practice with 1000+ FREE Notes, Videos & Tests.
10M+ students study on EduRev
Related Searches

Viva Questions

,

Extra Questions

,

practice quizzes

,

Previous Year Questions with Solutions

,

video lectures

,

Variables and Data Storage (Part - 2)

,

pdf

,

Summary

,

C Programming Interview Questions | Placement Papers - Technical & HR Questions - Interview Preparation

,

C Programming Interview Questions | Placement Papers - Technical & HR Questions - Interview Preparation

,

Semester Notes

,

Free

,

study material

,

past year papers

,

mock tests for examination

,

Exam

,

C Programming Interview Questions | Placement Papers - Technical & HR Questions - Interview Preparation

,

Sample Paper

,

Variables and Data Storage (Part - 2)

,

ppt

,

MCQs

,

Variables and Data Storage (Part - 2)

,

Objective type Questions

,

Important questions

,

shortcuts and tricks

;