C Aptitude Interview Questions Explained Series-1

Declarations & Initializations:


Q How do you decide that integer type to use?


A: If you might need large values (above 32,767 or below -32,767), use long.
Otherwise, if space is very important (i.e. if there are large arrays or many
structures), use short. Otherwise, use int. If well-defined overflow
characteristics are important & negative values are not, or if you want to
steer clear of sign- extension problems when manipulating bits or bytes, use one
of the corresponding unsigned types. (Beware when mixing signed & unsigned
values in expressions, though.) Although character types (especially unsigned
char) can be used as "tiny" integers, doing so is sometimes more trouble than
it's worth, due to unpredictable sign extension & increased code
size.


A similar space/time tradeoff applies when deciding between float &
double. None of the above rules apply if the address of a variable is taken
& must have a particular type. If for some reason you need to declare
something with an *exact* size (usually the only good reason for doing so is
when attempting to conform to some externally-imposed storage layout, but see
question 20.5), be sure to encapsulate the choice behind an appropriate
typedef.


Q What should the 64-bit type on a machine that can support
it?



A: The forthcoming revision to the C Standard (C9X) specifies type long long
as effectively being at least 64 bits, & this type has been implemented by a
number of compilers for some time. (Others have implemented extensions such as
__longlong.) On the other hand, there's no theoretical reason why a compiler
couldn't implement type short int as 16, int as 32, & long int as 64 bits,
& some compilers do indeed choose this arrangement.


Q What is the best way to declare & define global variables &
functions?



A: First, though there can be many "declarations" (& in many translation
units) of a single "global" (strictly speaking, "external") variable or
function, there must be exactly one "definition". (The definition is the
declaration that actually allocates space, & provides an initialization
value, if any.) The best arrangement is to place each definition in some
relevant .c file, with an external declaration in a header (".h") file, that is
#included wherever the declaration is needed. The .c file containing the
definition should also #include the same header file, so that the compiler can
check that the definition matches the declarations. This rule promotes a high
degree of portability: it is consistent with the requirements of the ANSI C
Standard, & is also consistent with most pre-ANSI compilers & linkers.
(Unix compilers & linkers typically use a "common model" that allows
multiple definitions, as long as at most one is initialized; this behavior is
mentioned as a "common extension" by the ANSI Standard, no pun intended. A few
very odd systems may require an explicit initializer to distinguish a definition
from an external declaration.) It is possible to use preprocessor tricks to
arrange that a line like DEFINE(int, i); need only be entered once in one header
file, & turned into a definition or a declaration depending on the setting
of some macro, but it's not clear if this is worth the trouble. It's especially
important to put global declarations in header files if you want the compiler to
catch inconsistent declarations for you. In particular, never place a prototype
for an external function in a .c file: it wouldn't generally be checked for
consistency with the definition, & an incompatible prototype is worse than
useless.


Q What does extern mean in a function declaration?


A: It can be used as a stylistic hint to indicate that the function's
definition is probably in another source file, but there is no formal difference
between extern int f(); & int f();


Q What is the auto keyword good for?


A: Nothing; it's archaic.


Q Define a linked list & tried typedef struct { char *item;
NODEPTR next; } *NODEPTR; but the compiler give to error messages. Can't a
structure in C contain a pointer to itself?



A: Structures in C can certainly contain pointers to themselves; the
discussion & example in section 6.5 of K&R make this clear. The problem
with the NODEPTR example is that the typedef has not been defined at the point
where the "next" field is declared. To fix this code, first give the structure a
tag ("struct node"). Then, declare the "next" field as a simple "struct node *",
or disentangle the typedef declaration from the structure definition, or both.
One corrected version would be struct node { char *item; struct node *next; };
typedef struct node *NODEPTR; & there are at least three other equivalently
correct ways of arranging it. A similar problem, with a similar solution, can
arise when attempting to declare a pair of typedef'ed mutually referential
structures.


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



A: The first part of this question can be answered in at least three
ways:


1. char *(*(*a[N])())(); 2. Build the declaration up incrementally, using
typedefs: typedef char *pc; /* pointer to char */ typedef pc fpc(); /* function
returning pointer to char */ typedef fpc *pfpc; /* pointer to above */ typedef
pfpc fpfpc(); /* function returning... */ typedef fpfpc *pfpfpc; /* pointer
to... */ pfpfpc a[N]; /* array of... */ 3. Use the cdecl program, that turns
English into C & vice versa: cdecl> declare a as array of pointer to
function returning pointer to function returning pointer to char char
*(*(*a[])())() cdecl can also explain complicated declarations, help with casts,
& indicate that set of parentheses the arguments go in (for complicated
function definitions, like the one above). Any good book on C should explain how
to read these complicated C declarations "inside out" to understand them
("declaration mimics use"). The pointer-to-function declarations in the examples
above have not included parameter type information. When the parameters have
complicated types, declarations can *really* get messy.


Q How you declare a function that can return a pointer to a function
of the same type? you will building a state machine with one function for each
state, each of that returns a pointer to the function for the next state. But
you can't find a way to declare the functions.



A: We can't quite do it directly. Either have the function return a generic
function pointer, with some judicious 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.


Q Some compiler is complaining about an invalid redeclaration of a
function, but you only define it once & call it once.



A: Functions that are called without a declaration in scope (perhaps because
the first call precedes the function's definition) are assumed to be declared as
returning int (& without any argument type information), leading to
discrepancies if the function is later declared or defined Or. Non-int functions
must be declared before they are called. Another possible source of this problem
is that the function has the same name as another one declared in some header
file.


Q. What is the right declaration for main()? Is void main()
correct?



A: But no, it's not correct


Q. What are you allowed to assume about the initial values of
variables that are not explicitly initialized? If global variables start out as
"zero", is that good enough for null pointers & floating-point
zeroes?



A: Uninitialized variables with "static" duration (that is, those declared
outside of functions, & those declared with the storage class static), are
guaranteed to start out as zero, as if the programmer had typed "= 0".
Therefore, such variables are implicitly initialized to the null pointer (of the
correct type; see also section 5) if they are pointers, & to 0.0 if they are
floating-point. Variables with "automatic" duration (i.e. local variables
without the static storage class) start out containing garbage, unless they are
explicitly initialized. (Nothing useful can be predicted about the garbage.)
Dynamically-allocated memory obtained with malloc() & realloc() is also
likely to contain garbage, & must be initialized by the calling program, as
appropriate. Memory obtained with calloc() is all-bits-0, but this is not
necessarily useful for pointer or floating-point values

No comments:

Post a Comment