C Preprocessor Directives - Tricky MCQ
Tricky Questions on Preprocessor Directives
Basic Level (15 Questions)
When does preprocessor execute in compilation process?
Preprocessor runs before actual compilation. It performs text substitution, file inclusion, conditional compilation. Output is expanded source code for compiler. Can be invoked separately (gcc -E). Has no knowledge of C syntax.
What is the difference between #include "file" and #include <file>?
Quotes search current directory then system include paths. Angle brackets search system include paths only. Convention: quotes for project headers, brackets for standard/library headers. Exact search paths compiler-dependent.
What is macro vs function advantage?
Macros are text substitution - no runtime overhead. Generic - work with any type. Can use compile-time constants. Disadvantages: no type checking, multiple evaluation of arguments, harder to debug, code bloat.
What is the problem with #define SQUARE(x) x*x?
Macro arguments should be fully parenthesized. SQUARE(2+3) expands to 2+3*2+3 = 2+6+3 = 11 due to precedence. Correct: ((x)*(x)). Also need parentheses around whole macro if used in expression.
What is token pasting operator (##)?
Token pasting (##) concatenates tokens before evaluation. Useful for creating function/variable names programmatically. Example: #define DECLARE(type, name) type var_##name. Must result in valid token.
What is stringizing operator (#)?
Stringizing (#) converts macro argument to string literal. Example: #define STR(x) #x makes STR(hello) become "hello". Preserves whitespace and quotes. Useful for assertions, debugging, generating strings from code.
What is #pragma directive?
#pragma provides compiler-specific directives. Common pragmas: once (header guards), pack (structure alignment), warning (disable/enable). Behavior varies by compiler. Standardized pragmas in C99: STDC FP_CONTRACT, CX_LIMITED_RANGE.
What is #error directive?
#error stops compilation with custom message. Used to enforce constraints: required macros, platform checks, deprecated features. Similar to static_assert but at preprocessing stage. #warning gives warning without stopping.
What is #undef directive?
#undef removes macro definition. Needed before redefining macro (avoiding warning). Useful for macros local to header file. After #undef, #ifdef MACRO is false. Doesn't affect identical macro defined elsewhere.
What is #line directive?
#line changes line number (and optionally filename) for error reporting. Syntax: #line number "filename". Used by tools generating C code to map errors to original source. __LINE__ and __FILE__ reflect #line values.
What is defined() operator in #if?
defined(MACRO) returns 1 if MACRO defined, 0 otherwise. Used in #if expressions. Allows complex conditions: #if defined(A) && !defined(B). More flexible than #ifdef/#ifndef which only test single macro.
What is #if vs #ifdef difference?
#if evaluates constant integer expression. #ifdef tests if macro defined (regardless of value). #if defined(MACRO) identical to #ifdef MACRO. #if more flexible for complex conditions, version checks.
What is __VA_ARGS__ in variadic macros?
__VA_ARGS__ expands to variable arguments in macro. Syntax: #define LOG(fmt, ...) printf(fmt, __VA_ARGS__). C99 feature. Can be empty if no arguments passed (requires ##__VA_ARGS__ extension for GCC).
What are predefined macros like __LINE__, __FILE__?
Standard predefined macros: __LINE__ (current line), __FILE__ (filename), __DATE__ (compilation date), __TIME__ (compilation time). Compiler-specific: __STDC__, __cplusplus, __GNUC__, _WIN32. Useful for diagnostics, version checks.
What is header guard pattern?
Header guards prevent duplicate definitions when header included multiple times. Traditional: #ifndef UNIQUE_NAME / #define UNIQUE_NAME / #endif. #pragma once is compiler extension (faster, less error-prone). Unique name typically filename_UPPERCASE_H.
Hard Level (15 Questions)
`#define SQR(x) x*x` then `SQR(1+2)` expands to:
No parentheses: 1+2*1+2 due to macro text replacement.
`#define STR(x) #x` then `STR(hi)` produces:
# operator stringifies argument to "hi".
`#if 1/0` in `#if` expression:
#if evaluates constant expressions; division by zero is invalid in #if.
`__FILE__` and `__LINE__`:
They are macros expanding to filename string and line number int constant.
`#include <stdio.h>` vs `#include "stdio.h"`:
Standard specifies different search order for quoted vs angle includes.
`#ifdef X` when X defined as 0:
ifdef tests existence; macro defined to 0 is still defined.
`#define f() 42` vs `#define f 42`:
Function-like macro f() needs (); object-like f replaces token f.
`##` token pasting `a##b` with `a` token and `b` token:
## concatenates tokens into one preprocessing token if valid.
`#pragma once` is:
#pragma once is common extension; not required by ISO C.
Macro recursion `#define A A` expansion:
If macro name appears in its own replacement, it is not rescanned for further replacement.
`__COUNTER__` (GCC/Clang):
__COUNTER__ is extension assigning successive integers per use.
`#undef X` then `#ifdef X`:
#undef removes macro; ifdef fails afterward.
`#line` directive:
#line changes __LINE__ and __FILE__ reporting for following lines.
Stringify `STR(\n)` gives:
Argument is stringified before escape processing becomes "\n" literal in source.
`#if defined(X) + defined(Y)` when both defined:
defined returns 0 or 1; sum is 2 when both exist.