C INTERVIEW QUESTIONS Part 4

Question: What is the difference between a string copy (strcpy) and a memory copy (memcpy)? When should each be used?
Answer: The strcpy() function is designed to work exclusively with strings. It copies each byte of the source string to the destination string and stops when the terminating null character () has been moved. On the other hand, the memcpy () function is designed to work with any type of data. Because not all data ends with a null character, you must provide the memcpy () function with the number of bytes you want to copy from the source to the destination.

Question: How can you check to see whether a symbol is defined?
Answer: You can use the #ifdef and #ifndef preprocessor directives to check whether a symbol has been defined
(#ifdef) or whether it has not been defined (#ifndef).


Question: How do you override a defined macro?
Answer: You can use the #undef preprocessor directive to undefine (override) a previously defined macro.

Question: What is #line used for?
Answer: The #line preprocessor directive is used to reset the values of the _ _LINE_ _ and _ _FILE_ _ symbols,
respectively. This directive is commonly used in fourth-generation languages that generate C language source files.

Question: What is a pragma?
Answer :The #pragma preprocessor directive allows each compiler to implement compiler-specific features that can be turned on and off with the #pragma statement. For instance, your compiler might support a feature called loop optimization. This feature can be invoked as a command-line option or as a #pragma directive.

To implement this option using the #pragma directive, you would put the following line into your code:

#pragma loop_opt(on)

Conversely, you can turn off loop optimization by inserting the following line into your code:

Question: What are the standard predefined macros?
Answer: The ANSI C standard defines six predefined macros for use in the C language:

Macro Name Purpose
_ _LINE_ _ Inserts the current source code line number in your code.

_ _FILE_ _ Inserts the current source code filename in your code.

_ _DATE_ _ Inserts the current date of compilation in your code.

_ _TIME_ _ Inserts the current time of compilation in your code.

_ _STDC_ _ Is set to 1 if you are enforcing strict ANSI C conformity.

_ _cplusplus Is defined if you are compiling a C++ program

Question: How many levels deep can include files be nested?
Answer: Even though there is no limit to the number of levels of nested include files you can have, your compiler might run out of stack space while trying to include an inordinately high number of files. This number varies according to your hardware configuration and possibly your compiler.


Question: Can include files be nested?
Answer:Yes. Include files can be nested any number of times. As long as you use precautionary measures , you can avoid including the same file twice. In the past, nesting header files was seen as bad programming practice, because it complicates the dependency tracking function of the MAKE program and thus slows down compilation. Many of today’s popular compilers make up for this difficulty by implementing a concept called precompiled headers, in which all headers and associated dependencies are stored in
a precompiled state.

Many programmers like to create a custom header file that has #include statements for every header needed for each module. This is perfectly acceptable and can help avoid potential problems relating to #include files, such as accidentally omitting an #include file in a module.

Question: Can you define which header file to include at compile time?
Answer :Yes. This can be done by using the #if, #else, and #endif preprocessor directives. For example, certain
compilers use different names for header files. One such case is between Borland C++, which uses the header file alloc.h, and Microsoft C++, which uses the header file malloc.h. Both of these headers serve the same purpose, and each contains roughly the same definitions. If, however, you are writing a program that is to support Borland C++ and Microsoft C++, you must define which header to include at compile time. The following example shows how this can be done:


#ifdef _ _BORLANDC_ _
#include
#else
#include
#endif


Question: What is the difference between #include and #include “file”?
Answer : When writing your C program, you can include files in two ways. The first way is to surround the file you
want to include with the angled brackets <>. This method of inclusion tells the preprocessor to look for the file in the predefined default location. This predefined default location is often an INCLUDE environment variable that denotes the path to your include files. For instance, given the INCLUDE variable

INCLUDE=C:\COMPILER\INCLUDE;S:\SOURCE\HEADERS;

using the #include version of file inclusion, the compiler first checks the C:\COMPILER\INCLUDE
directory for the specified file. If the file is not found there, the compiler then checks the
S:\SOURCE\HEADERS directory. If the file is still not found, the preprocessor checks the current directory.

The second way to include files is to surround the file you want to include with double quotation marks. This method of inclusion tells the preprocessor to look for the file in the current directory first, then look for it in the predefined locations you have set up. Using the #include “file” version of file inclusion and applying it to the preceding example, the preprocessor first checks the current directory for the specified file. If the file is not found in the current directory, the C:COMPILERINCLUDE directory is searched. If the file is still not found, the preprocessor checks the S:SOURCEHEADERS directory.
The #include method of file inclusion is often used to include standard headers such as stdio.h or
stdlib.h. This is because these headers are rarely (if ever) modified, and they should always be read from your compiler’s standard include file directory.

The #include “file” method of file inclusion is often used to include nonstandard header files that you have created for use in your program. This is because these headers are often modified in the current directory, and you will want the preprocessor to use your newly modified version of the header rather than the older, unmodified version.

Question: Is it better to use a macro or a function?
Answer : The answer depends on the situation you are writing code for. Macros have the distinct advantage of being more efficient (and faster) than functions, because their corresponding code is inserted directly into your source code at the point where the macro is called. There is no overhead involved in using a macro like there is in placing a call to a function. However, macros are generally small and cannot handle large, complex coding constructs. A function is more suited for this type of situation. Additionally,
macros are expanded inline, which means that the code is replicated for each occurrence of a macro. Your code therefore could be somewhat larger when you use macros than if you were to use functions.

Thus, the choice between using a macro and using a function is one of deciding between the tradeoff of faster program speed versus smaller program size. Generally, you should use macros to replace small, repeatable code sections, and you should use functions for larger coding tasks that might require several lines of code.


Question: How are portions of a program disabled in demo versions?
Answer :If you are distributing a demo version of your program, the preprocessor can be used to enable or disable portions of your program. The following portion of code shows how this task is accomplished, using the preprocessor directives #if and #endif:

int save document(char* doc_name)
{
#if DEMO_VERSION
printf(“Sorry! You can’t save documents using the DEMO version of this program!n”);
return(0);
#endif
...

Question: What is the benefit of using an enum rather than a #define constant?
Answer :The use of an enumeration constant (enum) has many advantages over using the traditional symbolic constant style of #define. These advantages include a lower maintenance requirement, improved program readability, and better debugging capability.

1) The first advantage is that enumerated constants are generated automatically by the compiler. Conversely, symbolic constants must be manually assigned values by the programmer.

For instance, if you had an enumerated constant type for error codes that could occur in your program, your enum definition could look something like this:

enum Error_Code
{
OUT_OF_MEMORY,
INSUFFICIENT_DISK_SPACE,
LOGIC_ERROR,
FILE_NOT_FOUND
};

In the preceding example, OUT_OF_MEMORY is automatically assigned the value of 0 (zero) by the compiler because it appears first in the definition. The compiler then continues to automatically assign numbers to the enumerated constants, making INSUFFICIENT_DISK_SPACE equal to 1, LOGIC_ERROR equal to 2, and FILE_NOT_FOUND equal to 3, so on.

If you were to approach the same example by using symbolic constants, your code would look something like this:

#define OUT_OF_MEMORY 0
#define INSUFFICIENT_DISK_SPACE 1
#define LOGIC_ERROR 2
#define FILE_NOT_FOUND 3

values by the programmer. Each of the two methods arrives at the same result: four constants assigned numeric values to represent error codes. Consider the maintenance required, however, if you were to add two constants to represent the error codes DRIVE_NOT_READY and CORRUPT_FILE. Using the enumeration constant method, you simply would put these two constants anywhere in the enum definition. The compiler would generate two unique values for these constants. Using the symbolic constant method, you would have to manually assign two new numbers to these constants. Additionally, you would want to ensure that the numbers you assign to these constants are unique.


2) Another advantage of using the enumeration constant method is that your programs are more readable and thus can be understood better by others who might have to update your program later.


3) A third advantage to using enumeration constants is that some symbolic debuggers can print the value of an enumeration constant. Conversely, most symbolic debuggers cannot print the value of a symbolic constant. This can be an enormous help in debugging your program, because if your program is stopped at a line that uses an enum, you can simply inspect that constant and instantly know its value. On the other hand, because most debuggers cannot print #define values, you would most likely have to search for that value by manually looking it up in a header file.

Question: What is the benefit of using #define to declare a constant?
Answer: Using the #define method of declaring a constant enables you to declare a constant in one place and use it throughout your program. This helps make your programs more maintainable, because you need to maintain only the #define statement and not several instances of individual constants throughout your program.

For instance, if your program used the value of pi (approximately 3.14159) several times, you might want to declare a constant for pi as follows:

#define PI 3.14159

Using the #define method of declaring a constant is probably the most familiar way of declaring constants to traditional C programmers. Besides being the most common method of declaring constants, it also takes up the least memory. Constants defined in this manner are simply placed directly into your source code, with no variable space allocated in memory. Unfortunately, this is one reason why most debuggers cannot inspect constants created using the #define method.

Question :Can a file other than a .h file be included with #include?
Answer : The preprocessor will include whatever file you specify in your #include statement. Therefore, if you have the line

#include

in your program, the file macros.inc will be included in your precompiled program. It is, however, unusual programming practice to put any file that does not have a .h or .hpp extension in an #include statement.

You should always put a .h extension on any of your C files you are going to include. This method makes it easier for you and others to identify which files are being used for preprocessing purposes. For instance, someone modifying or debugging your program might not know to look at the macros.inc file for macro definitions. That person might try in vain by searching all files with .h extensions and come up empty. If your file had been named macros.h, the search would have included the macros.h file, and the searcher would have been able to see what macros you defined in it.

Question: How can you avoid including a header more than once?
Answer : One easy technique to avoid multiple inclusions of the same header is to use the #ifndef and #define
preprocessor directives. When you create a header for your program, you can #define a symbolic name that is unique to that header. You can use the conditional preprocessor directive named #ifndef to check whether that symbolic name has already been assigned. If it is assigned, you should not include the header, because it has already been preprocessed. If it is not defined, you should define it to avoid any further inclusions of the header. The following header illustrates this technique:

#ifndef _FILENAME_H
#define _FILENAME_H
#define VER_NUM “1.00.00”
#define REL_DATE “08/01/94”
#if _ _WINDOWS_ _
#define OS_VER “WINDOWS”
#else
#define OS_VER “DOS”
#endif
#endif

When the preprocessor encounters this header, it first checks to see whether _FILENAME_H has been defined. If it hasn’t been defined, the header has not been included yet, and the _FILENAME_H symbolic name is defined. Then, the rest of the header is parsed until the last #endif is encountered, signaling the end of the conditional #ifndef _FILENAME_H statement. Substitute the actual name of the header file for “FILENAME” in the preceding example to make it applicable for your programs.