|
1. What is a const pointer? The access modifier keyword const is a promise the programmer makes to the compiler that the value of a variable will not be changed after it is initialized. The compiler will enforce that promise as best it can by not enabling the programmer to write code which modifies a variable that has been declared const. A const pointer, or more correctly, a pointer to const, is a pointer which points to data that is const (constant, or unchanging). A pointer to const is declared by putting the word const at the beginning of the pointer declaration. This declares a pointer which points to data that can’t be modified. The pointer itself can be modified. The following example illustrates some legal and illegal uses of a const pointer: const char *str = hello; char c = *str /* legal */ str++; /* legal */ *str = ‘a’; /* illegal */ str[1] = ‘b’; /* illegal */
2. 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. 3. What are advantages and disadvantages of external storage class? Advantages of external storage class - Persistent storage of a variable retains the latest value
- The value is globally available
Disadvantages of external storage class - The storage for an external variable exists even when the variable is not needed
- The side effect may produce surprising output
- Modification of the program is difficult
- Generality of a program is affected
4. Sample Code char * strdup (const char * s) { char * buf; int len; assert(s != NULL); len = strlen(s); buf = (char *) calloc(len + 1, sizeof(char)); memcpy(buf, s, len); return buf; } The proposed implementation of strdup() above contains a runtime error that may NOT appear consistently with each invocation. Which one of the following accurately describes this error? - The arguments to calloc() do not cause enough memory to be allocated for storing the contents of s.
- If memory is scarce, calloc() may fail and return NULL. The code does not anticipate this condition.
- memcpy() may corrupt data if used to copy ASCII strings.
- buf is never NUL-terminated, and therefore cannot be used by C library functions affecting strings.
- The function returns a pointer to dynamic memory. This practice should be avoided and always constitutes a memory leak.
5. Is it acceptable to declare/define a variable in a C header? A 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. 6. Sample Code struct node { int id; int length; struct node * next; struct node * prev; unsigned char data [1]; } Consider struct node, an aggregate type defined above. Which one of the following might explain the declaration of its peculiar member data? - There is no difference between character unsigned char data and array unsigned char data [1], since each allocates only a single byte. Identical operations can be performed on both quantities. The choice was one of preference.
- The programmer is declaring a bit field called data, which consists of only a single bit. struct node probably represents some hardware device.
- data is probably used in conjunction with length and malloc() to create objects of variable size. struct node is essentially a header for an object of indeterminate size.
- The information provided by the definition of struct node is insufficient to formulate a guess about the purpose of the member data or its strange declaration.
- Clearly the programmer has made a typo. If the programmer had intended to allocate only a single byte, he or she would have declared data as unsigned char data instead.
7. How can you determine the maximum value that a numeric variable can hold? 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. 8. Sample Code void zero_array (char a [20]) { size_t size; assert(a); size = sizeof(a); memset(a, 0, size); } The function zero_array(), defined above, contains an error. Which one of the following describes it? - The result of sizeof(a) may be subject to a type cast that causes size to acquire an erroneous value. The call to memset() will probably clear the wrong number of bytes.
- The assert() macro may incorrectly cause the code to abort if the host machine uses a constant other than zero (0) to represent the null pointer natively.
- Standard C does not permit programmers to declare the size of the leftmost dimension of an array parameter. The compiler should print an error when parsing zero_array().
- sizeof(a) evaluates to sizeof(char *) rather than twenty (20). The call to memset() will probably clear the wrong number of bytes.
- The second and third arguments to memset() are probably reversed.
9. What is the difference between goto and longjmp() 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. 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. 10. /* sys/cdef.h */ #if defined(__STDC__) || defined(__cplusplus) #define __P(protos) protos #else #define __P(protos) () #endif /* stdio.h */ #include div_t div __P((int, int)); The code above comes from header files for the FreeBSD implementation of the C library. What is the primary purpose of the __P() macro? - The __P() macro provides backward compatibility for K&R C compilers, which do not recognize Standard C prototypes.
- The __P() macro provides forward compatibility for C++ compilers, which do not recognize Standard C prototypes.
- The __P() macro serves primarily to differentiate library functions from application-specific functions.
- The __P() macro has no function, and merely obfuscates library function declarations. It should be removed from further releases of the C library.
- Identifiers that begin with two underscores are reserved for C library implementations. It is impossible to determine the purpose of the macro from the context given.
|