Title: C Programming — LearnCoachAssist

Q: What is a pointer in C?
A: A pointer is a variable that stores the memory address of another variable.
Declared using the * operator: int *ptr;
The address-of operator & retrieves a variable's address: ptr = &x;

Q: How do you dereference a pointer?
A: Dereferencing means accessing the value stored at the address a pointer holds.
Use the * operator: int val = *ptr;
This retrieves the value at the memory location pointed to by ptr.

Q: What is a NULL pointer?
A: A NULL pointer is a pointer that does not point to any valid memory location.
Assigned with: int *ptr = NULL;
Always check for NULL before dereferencing to avoid segmentation faults.

Q: What does malloc() do in C?
A: malloc() allocates a block of memory on the heap and returns a pointer to it.
Syntax: int *p = (int *)malloc(n * sizeof(int));
Returns NULL if allocation fails. Memory is uninitialized.

Q: What is the difference between malloc() and calloc()?
A: - malloc(size) allocates uninitialized memory of the given size
- calloc(count, size) allocates memory for an array of count elements, each of size bytes, and initializes all bits to zero

Q: Why must you call free() after malloc()?
A: Calling free(ptr) releases dynamically allocated memory back to the system.
Failing to free causes a memory leak, where allocated memory is never reclaimed.
After freeing, set the pointer to NULL to avoid dangling pointer issues.

Q: What is a dangling pointer?
A: A dangling pointer points to memory that has been freed or deallocated.
Dereferencing it causes undefined behavior.
Prevention: set pointers to NULL after calling free().

Q: What is a struct in C?
A: A struct (structure) groups related variables of different types under one name.
Example:
struct Point { int x; int y; };
Access members with the dot operator: p.x or arrow operator for pointers: ptr->x

Q: How do you dynamically allocate a struct?
A: Use malloc() with sizeof:
struct Point *p = (struct Point *)malloc(sizeof(struct Point));
Access members via the arrow operator: p->x = 10;
Remember to free(p) when done.

Q: What is a union in C?
A: A union is like a struct, but all members share the same memory location.
The size of a union equals the size of its largest member.
union Data { int i; float f; char c; };
Only one member can hold a value at a time.

Q: What is the difference between a struct and a union?
A: - Struct: each member has its own memory; size is the sum of all members (plus padding)
- Union: all members share the same memory; size equals the largest member
Structs store multiple values simultaneously; unions store only one at a time.

Q: What is an enum in C?
A: An enum (enumeration) defines a set of named integer constants.
enum Color { RED, GREEN, BLUE };
By default, values start at 0 and increment by 1.
You can assign custom values: enum Color { RED = 1, GREEN = 5 };

Q: How do you open a file in C?
A: Use fopen():
FILE *fp = fopen("data.txt", "r");
Common modes: "r" (read), "w" (write), "a" (append), "rb" (read binary).
Always check if fp == NULL before proceeding.

Q: How do you read from a file in C?
A: Common file reading functions:
- fgetc(fp) — reads one character
- fgets(buf, size, fp) — reads a line into a buffer
- fscanf(fp, format, ...) — formatted reading
- fread(buf, size, count, fp) — reads binary data

Q: How do you write to a file in C?
A: Common file writing functions:
- fputc(ch, fp) — writes one character
- fputs(str, fp) — writes a string
- fprintf(fp, format, ...) — formatted writing
- fwrite(buf, size, count, fp) — writes binary data

Q: Why is fclose() important?
A: fclose(fp) closes an open file and flushes any buffered data to disk.
Failing to close files can cause:
- Data loss from unflushed buffers
- Resource leaks (file descriptors are limited)

Q: What is the #include preprocessor directive?
A: #include inserts the contents of another file before compilation.
#include <stdio.h> — searches system directories
#include "myfile.h" — searches the current directory first, then system directories.

Q: What is #define used for?
A: #define creates macros — text substitutions performed before compilation.
Constants: #define PI 3.14159
Function-like macros: #define MAX(a,b) ((a) > (b) ? (a) : (b))
Always wrap macro parameters in parentheses to avoid precedence bugs.

Q: What are #ifdef and #ifndef?
A: These are conditional compilation directives:
#ifdef MACRO — compiles code if MACRO is defined
#ifndef MACRO — compiles code if MACRO is not defined
Commonly used for include guards in header files.

Q: What is an include guard?
A: An include guard prevents a header file from being included multiple times:
#ifndef MYHEADER_H
#define MYHEADER_H
// declarations
#endif
This avoids duplicate definition errors during compilation.

Q: How do you declare and initialize an array in C?
A: Arrays hold a fixed number of elements of the same type:
int arr[5] = {1, 2, 3, 4, 5};
Indexing starts at 0: arr[0] is the first element.
Arrays in C do not perform bounds checking automatically.

Q: What is the relationship between arrays and pointers in C?
A: An array name acts as a pointer to its first element.
int arr[5]; int *p = arr;
Pointer arithmetic: *(p + i) is equivalent to arr[i].
However, an array name is not a modifiable lvalue — you cannot reassign it.

Q: How are strings represented in C?
A: Strings in C are arrays of characters terminated by a null character '\0'.
char name[] = "Hello"; stores 6 bytes (5 chars + '\0').
The <string.h> library provides functions like strlen(), strcpy(), and strcmp().

Q: What does strlen() do and what should you watch out for?
A: strlen(str) returns the number of characters before the null terminator.
It does not count the '\0' itself.
Caution: if the string is not null-terminated, strlen() will read past the buffer causing undefined behavior.

Q: What is the difference between strcpy() and strncpy()?
A: - strcpy(dest, src) copies the entire source string — no bounds checking, risk of buffer overflow
- strncpy(dest, src, n) copies at most n characters — safer but may not null-terminate the result if src is longer than n

Q: What are the bitwise operators in C?
A: C provides six bitwise operators:
- & — AND
- | — OR
- ^ — XOR
- ~ — NOT (complement)
- << — left shift
- >> — right shift
They operate on individual bits of integer types.

Q: How do you set a specific bit using bitwise operations?
A: To set bit n (make it 1):
value |= (1 << n);
Example: set bit 3 of x:
x = x | (1 << 3);
This turns on bit 3 without affecting other bits.

Q: How do you clear a specific bit using bitwise operations?
A: To clear bit n (make it 0):
value &= ~(1 << n);
The ~ inverts the mask so only bit n is 0, and the AND operation clears that bit while preserving all others.

Q: How do you toggle a specific bit?
A: To toggle (flip) bit n:
value ^= (1 << n);
XOR with 1 flips the bit: 0 becomes 1 and 1 becomes 0, while all other bits remain unchanged.

Q: What is a function pointer in C?
A: A function pointer stores the address of a function.
Declaration: int (*fptr)(int, int);
Assignment: fptr = &add;
Call: int result = fptr(3, 4);
Used for callbacks, strategy patterns, and dynamic dispatch.

Q: How are function pointers used as callback functions?
A: A callback is a function passed as an argument to another function via a function pointer:
void apply(int *arr, int n, int (*fn)(int)) {
  for (int i = 0; i < n; i++) arr[i] = fn(arr[i]);
}
This enables flexible, reusable code.

Q: What is a header file in C?
A: A header file (.h) contains declarations of functions, macros, types, and global variables.
It provides an interface so other source files can use these features without seeing the implementation.
Example: <stdio.h> declares printf(), scanf(), etc.

Q: What are the stages of the C compilation process?
A: The C compilation has four stages:
- Preprocessing: handles #include, #define, macros
- Compilation: translates C code to assembly
- Assembly: converts assembly to object code (.o files)
- Linking: combines object files and libraries into an executable

Q: What does the C preprocessor do?
A: The preprocessor runs before compilation and handles:
- #include — file inclusion
- #define — macro expansion
- #ifdef/#ifndef — conditional compilation
- #pragma — compiler-specific instructions
Output is pure C code with all directives resolved.

Q: What is the difference between a compiler and a linker?
A: - Compiler: translates each source file (.c) into an object file (.o) independently
- Linker: combines multiple object files and resolves external references (e.g., function calls across files) to produce the final executable

Q: What is sizeof() in C?
A: sizeof is a compile-time operator that returns the size in bytes of a type or variable.
sizeof(int) — size of an int (typically 4 bytes)
sizeof(arr) / sizeof(arr[0]) — common idiom to get array length
Note: for pointers, it returns the pointer size, not the pointed-to data size.

Q: What is pointer arithmetic in C?
A: Pointer arithmetic adjusts a pointer by multiples of the pointed-to type's size:
int *p = arr; p++; advances p by sizeof(int) bytes.
p + n points to the element n positions ahead.
Subtraction of two pointers gives the number of elements between them.

Q: What is a void pointer?
A: A void * is a generic pointer that can point to any data type.
It cannot be dereferenced directly — you must cast it first:
int *ip = (int *)void_ptr;
Used by functions like malloc() which return void * to remain type-agnostic.

Q: What is the difference between stack and heap memory?
A: - Stack: automatic allocation for local variables; fast; freed when function returns; limited size
- Heap: manual allocation via malloc()/free(); slower; persists until explicitly freed; larger but can fragment

Q: What is realloc() used for?
A: realloc(ptr, new_size) resizes a previously allocated memory block.
It may move the block to a new location and returns the new pointer.
int *p = realloc(old_p, 20 * sizeof(int));
Always assign to a temp pointer first to avoid losing the original if realloc fails.

Q: What is a segmentation fault?
A: A segmentation fault (segfault) occurs when a program accesses memory it is not allowed to.
Common causes:
- Dereferencing a NULL or dangling pointer
- Accessing memory out of array bounds
- Writing to read-only memory
- Stack overflow from deep recursion

Q: What is typedef used for in C?
A: typedef creates an alias for an existing type, improving readability:
typedef unsigned long ulong;
typedef struct { int x; int y; } Point;
After this, you can write Point p; instead of struct Point p;

Q: What is the volatile keyword in C?
A: volatile tells the compiler that a variable's value may change unexpectedly (e.g., by hardware or another thread).
volatile int sensor_val;
Prevents the compiler from optimizing away reads of the variable, ensuring it is always fetched from memory.

Q: What is the static keyword used for in C?
A: static has two uses:
- Inside a function: the variable retains its value between calls (persistent local)
- At file scope: limits the variable or function's visibility to the current file (internal linkage)

Q: What is the difference between pass by value and pass by reference in C?
A: C only supports pass by value. To simulate pass by reference, pass a pointer:
void swap(int *a, int *b) {
  int t = *a; *a = *b; *b = t;
}
Called as: swap(&x, &y);
The pointer itself is passed by value, but it allows modifying the original data.

Q: What is a double pointer (pointer to pointer)?
A: A double pointer stores the address of another pointer.
int **pp;
Used for:
- Modifying a pointer inside a function
- Dynamic 2D arrays
- Arrays of strings (char **argv)
Dereference twice to get the value: **pp.

Q: What does the const keyword do with pointers?
A: const with pointers has different meanings based on placement:
- const int *p — pointer to a constant int (cannot change the value)
- int *const p — constant pointer to an int (cannot change the address)
- const int *const p — both the pointer and value are constant

Q: How do you use fprintf() and fscanf()?
A: fprintf() writes formatted output to a file:
fprintf(fp, "Score: %d\n", score);
fscanf() reads formatted input from a file:
fscanf(fp, "%d %s", &age, name);
Both work like printf()/scanf() but take a FILE * as the first argument.

Q: What is a memory leak in C?
A: A memory leak occurs when dynamically allocated memory is never freed.
Example:
int *p = malloc(100);
p = malloc(200); // first block is leaked!
Over time, leaked memory accumulates and can exhaust available memory. Use tools like Valgrind to detect leaks.

Q: What is the #pragma directive?
A: #pragma provides compiler-specific instructions that are not part of the C standard.
Common examples:
- #pragma once — alternative to include guards
- #pragma pack(1) — controls struct padding/alignment
Unrecognized pragmas are ignored by the compiler.

Q: What is the difference between ++i and i++?
A: Prefix ++i increments i first, then returns the new value.
Postfix i++ returns the original value, then increments i.
In simple statements the result is the same final value of i, but in expressions the order matters, e.g. arr[i++] = x vs arr[++i] = x.

Q: What is operator precedence in C?
A: Operator precedence determines the order in which operators are evaluated in an expression.
Highest precedence binds tightest: () > [] > -> > *///% > +/- > <</>> > </<=/>/>= > ==/!= > & > ^ > | > && > || > ?: > = > ,.
Use parentheses to make intent explicit.

Q: What is the associativity of assignment operators?
A: Assignment operators (=</ += -= *= /= %= <<= >>= &= ^= |=) are right-associative.
a = b = c = 5; is parsed as a = (b = (c = 5));, so c becomes 5, then b, then a.

Q: What is the ternary conditional operator?
A: The ternary operator ?: is C's only three-operand operator:
max = (a > b) ? a : b;
If the condition is true it evaluates and returns the second operand; otherwise the third.
Only the chosen operand is evaluated, making it usable as a short if-else.

Q: What is the comma operator in C?
A: The comma operator evaluates its left operand, discards the result, then evaluates and returns the right operand.
for (i = 0, j = 10; i < j; i++, j--)
It has the lowest precedence, so (a, b) + c means (a, b) + c, not a, (b + c).

Q: What is the difference between while and do-while loops?
A: - while tests the condition before each iteration; the body may run zero times
- do-while tests the condition after each iteration; the body runs at least once

Q: What does the switch statement do?
A: switch dispatches to one of several branches based on an integer expression (or enum, or char).
Each case label is a constant value; execution falls through to the next case unless you break.
The default branch runs when no case matches.

Q: What is a function prototype?
A: A function prototype declares a function's return type, name, and parameter types without providing its body:
int add(int a, int b);
It lets the compiler check call-site argument types and allows functions to be used before their definition.

Q: What does return do in a C function?
A: return expr; ends the function and yields expr as its value to the caller.
Functions returning void use return; with no value.
If a non-void function reaches its closing } without a return, the behavior is undefined if the caller uses the result.

Q: How does recursion work in C?
A: A function is recursive when it calls itself.
Each call creates a new stack frame with its own parameters and locals.
Every recursive function must have a base case that terminates recursion, otherwise the stack eventually overflows.

Q: What is the difference between recursion and iteration?
A: Recursion uses repeated function calls; each call adds a stack frame, so memory grows with depth.
Iteration uses loops; memory usage is constant.
Any recursion can be rewritten as iteration, but recursive code can be clearer for tree/graph traversal.

Q: What is the extern keyword?
A: extern declares that a variable or function is defined elsewhere (in this file or another).
extern int counter; in a header lets multiple source files share the same counter.
It does not allocate storage; only the definition does.

Q: What is the register keyword?
A: register suggests storing a local variable in a CPU register for faster access.
register int i;
Modern compilers ignore the hint; you also cannot apply the address-of operator & to a register variable.

Q: What is the difference between local and global variables?
A: Local variables are declared inside a function; they exist only while that function runs and are not visible outside.
Global variables are declared outside any function; they exist for the program's lifetime and are visible across functions (unless static).

Q: What is the lifetime of a variable?
A: Lifetime is the runtime interval during which storage exists.
- Automatic: from block entry to exit
- Static: entire program duration
- Dynamic: from malloc() to free()
- Thread: thread duration (_Thread_local)

Q: What is the const keyword used for with variables?
A: const int x = 5; declares x as a read-only variable.
Any attempt to modify x is a compile-time error.
It documents intent, enables compiler optimizations, and is required for pointers passed to functions like strchr().

Q: What are the basic data types in C?
A: - char — at least 8 bits, holds a character
- int — natural integer size for the platform
- float, double, long double — floating-point
- _Bool — boolean (0 or 1)
- Modifiers: signed, unsigned, short, long, long long

Q: What headers provide fixed-width integer types?
A: <stdint.h> provides int8_t, int16_t, int32_t, int64_t and unsigned variants.
<inttypes.h> adds portable printf/scanf format macros like PRId32, SCNu64.
Use them when the exact bit-width matters, e.g. binary file formats or network protocols.

Q: What are the limits of size_t and ptrdiff_t?
A: size_t is the unsigned type of sizeof and the result of strlen()/malloc(); it is at least 16 bits, typically 32 or 64.
ptrdiff_t is the signed type of the difference between two pointers; it can hold any pointer difference.
Both are defined in <stddef.h>.

Q: What is the difference between float, double, and long double?
A: - float: typically IEEE-754 single precision (32 bits, ~6-7 decimal digits)
- double: typically IEEE-754 double precision (64 bits, ~15-16 digits), the default for floating literals
- long double: 80-bit on x87, 128-bit on some platforms; exact precision varies

Q: What is integer promotion in C?
A: Integer promotion converts small integer types (char, short, bit-fields) to int (or unsigned int) before most operations.
This is why sizeof('A') is typically sizeof(int) rather than 1 in C.
It avoids arithmetic anomalies and is part of C's "usual arithmetic conversions".

Q: What are the usual arithmetic conversions?
A: When two arithmetic operands differ, the smaller/integer-rank type is converted to the larger one:
- If either is long double, the other becomes long double
- Else if either is double...
- ...else if either is float...
- For integers, the smaller signed/unsigned rank is promoted to a common type that can represent both values

Q: What is the difference between printf() and fprintf()?
A: printf() writes to stdout.
fprintf(fp, ...) writes to a FILE * you provide.
fprintf(stdout, ...) behaves like printf; fprintf(stderr, "Error: %s\n", msg); writes to standard error, which is typically unbuffered.

Q: What do the printf width and precision specifiers do?
A: %5d reserves 5 characters of width, right-aligned by default; pad with spaces.
%-5d left-aligns.
%.3f limits precision — for floats, the number of digits after the decimal; for ints, the minimum digits printed.
%5.3f combines both.

Q: What is a format string vulnerability?
A: Passing user-controlled data as the format string, e.g. printf(user_input);, is a security bug.
An attacker can use %s, %x, or %n to read or write arbitrary memory.
Always pass user input as an argument: printf("%s", user_input);.

Q: What does the return value of printf() indicate?
A: printf() returns the number of characters written, or a negative value on error.
This lets you detect output errors: if (printf("Hello\n") < 0) { perror("printf"); }
Many codebases ignore this; checking it is good defensive practice.

Q: What is the difference between gets() and fgets()?
A: - gets(str) reads until newline and discards it; no size limit — removed from C11 because it is inherently unsafe
- fgets(str, sizeof(str), stdin) reads at most sizeof(str)-1 characters and null-terminates; safer
Always prefer fgets().

Q: What is the difference between strcmp() and strncmp()?
A: - strcmp(a, b) compares whole strings; returns 0 if equal
- strncmp(a, b, n) compares at most n characters; useful for prefixes (strncmp(s, "http://", 7))
Both use unsigned char comparisons.

Q: What does strchr() do and what does strrchr() do?
A: - strchr(str, c) returns a pointer to the first occurrence of c in str, or NULL
- strrchr(str, c) returns a pointer to the last occurrence of c in str
Both treat c as an unsigned char.

Q: What does strstr() do?
A: strstr(haystack, needle) searches haystack for the substring needle.
It returns a pointer to the start of the first match, or NULL if needle is not found.
For case-insensitive search use strcasestr() (POSIX, not standard C).

Q: What does strtok() do and why is it tricky?
A: strtok(str, delim) splits str into tokens separated by any character in delim.
On the first call, pass the string; subsequent calls pass NULL to continue.
It modifies the input by writing '\0' where delimiters were, and is not thread-safe; use strtok_r() for reentrancy.

Q: What does memcpy() do and how is it different from memmove()?
A: - memcpy(dest, src, n) copies n bytes; regions must not overlap (undefined behavior otherwise)
- memmove(dest, src, n) handles overlapping regions safely by using a temporary buffer
Prefer memmove if overlap is possible.

Q: What does memset() do?
A: memset(buf, value, n) sets the first n bytes of buf to value (interpreted as unsigned char).
memset(arr, 0, sizeof(arr)); zeroes an array.
It is the standard idiom for zeroing a struct or buffer.

Q: What is a variadic function?
A: A variadic function accepts a variable number of arguments, declared with ....
int printf(const char *fmt, ...);
To implement one you include <stdarg.h> and use va_list, va_start, va_arg, va_end.

Q: How do you write a variadic function?
A: int sum(int n, ...) {
  va_list args;
  va_start(args, n);
  int total = 0;
  for (int i = 0; i < n; i++)
    total += va_arg(args, int);
  va_end(args);
  return total;
}
Always call va_end once per va_start.

Q: What does atoi() do and what is wrong with it?
A: atoi(str) converts a decimal string to an int.
Problems: no error reporting (atoi("abc") returns 0 silently); behavior on overflow is undefined; no base support.
Prefer strtol(), which reports errors via errno and accepts a base.

Q: What does strtol() do?
A: strtol(str, &endptr, base) converts str to a long.
base may be 0 (auto-detect from 0x/0 prefix) or any value 2..36.
endptr points to the first character that was not part of the number; check it and errno for robust parsing.

Q: What does strtod() do?
A: strtod(str, &endptr) converts a string to a double.
It correctly handles whitespace, optional sign, decimal point, exponent, NaN, and infinity.
Like strtol, it sets endptr on failure; check it and errno for ERANGE.

Q: What is the difference between exit() and return in main?
A: - return n; in main returns the value to the runtime, which calls exit(n)
- exit(n) terminates the program from anywhere: runs atexit handlers, flushes and closes stdio buffers, then returns n to the host
exit(EXIT_SUCCESS) is portable; EXIT_FAILURE signals failure.

Q: What does abort() do?
A: abort() terminates the program abnormally.
It raises SIGABRT, which by default terminates the process without flushing buffers or running atexit handlers.
Used by assert() on failure; produces a core dump when enabled.

Q: How does the atexit() function work?
A: atexit(fn) registers fn (taking void, returning void) to run at normal program termination via exit().
Functions run in LIFO order (last registered, first called).
at_quick_exit registers a handler for _Exit() that bypasses atexit.

Q: What does assert() do?
A: assert(expr) from <assert.h> checks expr; if it is false, it prints the file/line and calls abort().
Defining NDEBUG before including <assert.h> disables all asserts — they compile to nothing.
Use asserts for internal invariants, not for validating external input.

Q: What is errno?
A: errno is a thread-local integer set by many standard library functions to indicate an error code.
Examples: ERANGE (numeric overflow), ENOMEM (out of memory), ENOENT (no such file).
You should set it to 0 before the call and check it afterward; it is only meaningful when the call signaled failure.

Q: What does perror() do?
A: perror("prefix") prints "prefix: <system error message>" to stderr, using the current value of errno.
FILE *fp = fopen("x.txt", "r"); if (!fp) { perror("open"); exit(1); }
strerror(errno) returns the message string without printing.

Q: What is the difference between text mode and binary mode in fopen()?
A: - Text mode ("r", "w"): on Windows, \n is translated to \r\n on write and back on read; on Unix there is no difference
- Binary mode ("rb", "wb"): bytes are written/read exactly as-is, no translation
Always use binary mode for non-text files like images, archives, or structured records.

Q: How do you reposition the file pointer?
A: - fseek(fp, offset, SEEK_SET) — absolute position from start
- fseek(fp, offset, SEEK_CUR) — relative to current
- fseek(fp, offset, SEEK_END) — relative to end
- rewind(fp) — equivalent to fseek(fp, 0, SEEK_SET); clearerr(fp);
- ftell(fp) returns the current offset (or -1L on error)

Q: What does feof() actually indicate?
A: feof(fp) returns non-zero only after a read has attempted to go past EOF.
It does not predict EOF on the next read.
The idiomatic loop is: read first, then test the return value, then optionally use feof/ferror for diagnostics.

Q: What does fflush() do?
A: fflush(stdout) writes any buffered output to the underlying file or terminal, even without a newline.
fflush(NULL) flushes all output streams.
It is defined only for output streams (or update streams in write mode); behavior on input streams is implementation-defined.

Q: What is the volatile keyword in practice?
A: volatile int flag = 0; while another thread or ISR sets flag = 1;
Without volatile, the compiler may cache flag in a register and the loop never terminates.
Volatile is not enough for thread synchronization; pair it with atomic operations or memory barriers.

Q: What is the restrict keyword?
A: restrict on a pointer tells the compiler that no other pointer aliases the same object during the pointer's lifetime.
void copy(int *restrict dst, const int *restrict src, size_t n);
This permits aggressive optimizations like reordering or vectorizing loads/stores. Misuse (aliasing in violation) is undefined behavior.

Q: What is the inline keyword?
A: inline is a hint that a function should be expanded at the call site instead of emitted as a separate function.
It does not guarantee inlining; the compiler decides.
Since C99, an inline definition does not produce an external symbol on its own; you typically also provide an extern version.

Q: What does _Noreturn do?
A: _Noreturn (or the C11 attribute [[noreturn]]) marks a function that does not return to the caller.
_Noreturn void fatal(const char *msg);
Examples: exit(), abort(), longjmp()-terminated paths.
The compiler can then warn if code after the call is unreachable.

Q: What are designated initializers?
A: C99 allows initializing specific struct or array members by name:
struct Point p = { .x = 10, .y = 20 };
int arr[5] = { [3] = 7, [1] = 2 };
Unspecified members are zero-initialized; this is a clear way to set just a few fields.

Q: What is a compound literal?
A: A compound literal is an unnamed object created on the fly:
struct Point p = (struct Point){ .x = 1, .y = 2 };
draw_rect((Rect){0, 0, 100, 50});
Its lifetime is the enclosing block (automatic) or the program (if at file scope).

Q: What is a flexible array member?
A: A C99 feature: a struct's last member may be declared as an array of unspecified size.
struct Buf { size_t len; char data[]; };
You allocate sizeof(struct Buf) + n bytes via malloc; data then refers to a variable-length buffer.
Allows the array to share allocation with the header, no extra pointer.

Q: What is a bit-field?
A: A bit-field lets you specify the exact number of bits a struct member occupies:
struct Flags { unsigned int x : 1; unsigned int y : 3; unsigned int z : 4; };
Useful for compact storage or matching hardware registers.
Addressing a bit-field (&f.x) is not allowed.

Q: What are alignment and struct padding?
A: Each type has an alignment requirement; the compiler inserts padding bytes so that members fall on aligned addresses.
sizeof(struct { char a; int b; }) is typically 8, not 5, because int must start at a multiple of 4.
You can change alignment with #pragma pack or _Alignas.

Q: What is _Alignas?
A: _Alignas(N) is a C11 alignment specifier that forces an object to have at least N-byte alignment.
_Alignas(64) float buf[16]; aligns buf to a 64-byte cache line for SIMD or DMA.
The chosen alignment must not be weaker than the natural alignment of the type.

Q: What is endianness?
A: Endianness is the order in which bytes of a multibyte value are stored in memory.
- Little-endian: least significant byte first (x86, x86-64, ARM in most modes)
- Big-endian: most significant byte first (network byte order, some older CPUs)
Use htons/ntohs for portable network code.

Q: How do you detect endianness at runtime?
A: A common trick:
uint16_t x = 1;
int little_endian = *(uint8_t*)&x == 1;
If the first byte of x in memory is 1, the machine is little-endian.
This is fine for diagnostics but prefer fixed-width types and explicit byte ordering in protocols.

Q: What is undefined behavior (UB) in C?
A: Undefined behavior is anything the C standard does not specify — the compiler may do anything, including produce wrong results, crash, or work "by accident".
Examples: signed overflow, dereferencing a NULL/dangling pointer, out-of-bounds array access, reading an uninitialized variable, accessing memory through misaligned pointers, free()-ing the same pointer twice.
Modern compilers aggressively exploit UB for optimization, so UB bugs can be amplified.

Q: What is the difference between unspecified and implementation-defined behavior?
A: - Unspecified behavior: two or more outcomes are allowed; the compiler does not have to document which one
- Implementation-defined behavior: the compiler must choose and document one behavior
Both are well-formed programs; only undefined behavior lets the compiler do anything at all.

Q: What is sequence point ordering?
A: A sequence point is a point where all side effects of prior evaluations are complete and no side effects of future evaluations have occurred.
a = i++ + i++; has undefined behavior in C because i is modified and read between sequence points without sequencing.
Function calls, end of full expressions (;), and &&/||/,/?: introduce sequence points.

Q: What is the difference between declaration and definition for variables?
A: Definition: int counter = 0; — creates the object and allocates storage.
Declaration: extern int counter; — asserts that the variable exists elsewhere.
At file scope you may have many declarations but exactly one definition; otherwise the linker reports multiple-definition errors.

Q: What is internal vs external linkage?
A: - External linkage: the name is visible across translation units (default for non-static globals and functions)
- Internal linkage: the name is visible only within its own source file (apply static at file scope)
This is how libraries hide implementation symbols.

Q: How does a multi-file C program compile and link?
A: Each .c file is compiled independently into a .o object file.
The linker then combines all object files plus libraries, resolving symbols: it matches each extern declaration to its single definition, errors on undefined or multiply-defined symbols, and produces the executable.

Q: What is a static library?
A: A static library (.a on Unix, .lib on Windows) is a bundle of object files linked into the executable at build time.
Created with ar rcs libfoo.a foo.o bar.o and used with -lfoo.
The result is a self-contained executable, but library updates require relinking.

Q: What is a shared library?
A: A shared library (.so on Linux, .dylib on macOS, .dll on Windows) is loaded by the dynamic linker at runtime and shared between processes.
Smaller binaries, easier updates, but introduces runtime symbol resolution.
On Linux, LD_LIBRARY_PATH influences where libraries are found.

Q: What is the difference between a macro and a function?
A: - Macro: textual substitution before compilation; no type checking; arguments can be evaluated multiple times; may cause code bloat
- Function: real call with argument types; called once, result computed once; supports recursion
For type-generic numeric operations, C11's _Generic or inline functions are usually safer than #define.

Q: What are common pitfalls of #define macros?
A: - Multiple evaluation: MAX(a++, b++) increments one operand twice
- Precedence bugs: #define SQR(x) x*x makes SQR(a+b) wrong — always parenthesize
- No type checking: type mismatches are silent
- Hard to debug: errors point at the expansion site
Use static inline functions or enum constants instead where possible.

Q: What is the token-pasting operator in macros?
A: The ## operator joins two tokens into one:
#define DECLARE(name) int my_##name = 0;
DECLARE(counter); expands to int my_counter = 0;
Useful for generating boilerplate; macro arguments are not expanded before pasting.

Q: What does #pragma once do?
A: #pragma once is a non-standard but widely supported directive that tells the compiler to include this header only once per translation unit.
It is a simpler alternative to traditional #ifndef include guards.
Both approaches are functionally equivalent for well-formed headers.

Q: What is the difference between #include and #include "file"?
A: - #include <file> searches implementation-defined system paths first (for standard library and compiler headers)
- #include "file" searches the current directory of the source file first, then falls back to the system paths
By convention, use <> for system headers and "" for project headers.

Q: What is the C standard library header ?
A: <math.h> declares mathematical functions: sin, cos, tan, asin, atan2, exp, log, log10, pow, sqrt, ceil, floor, fabs, fmod.
Most operate on double and set errno to ERANGE on overflow or EDOM for undefined results (e.g. sqrt(-1)).

Q: How does qsort() work?
A: qsort(base, nmemb, size, compar) sorts an array in place.
compar takes two const void * elements and returns negative/zero/positive like strcmp.
Example for ints:
int cmp(const void *a, const void *b) {
  return (*(int*)a - *(int*)b);
}
qsort(arr, n, sizeof(int), cmp);

Q: How does bsearch() work?
A: bsearch(key, base, nmemb, size, compar) does a binary search on a sorted array.
Returns a pointer to the matching element or NULL.
The comparator must impose the same ordering used by qsort on the array.

Q: What are the random-number functions in C?
A: - rand() returns a pseudo-random int in [0, RAND_MAX]
- srand(seed) seeds the generator; srand(time(NULL)) is a common idiom
- RAND_MAX is at least 32767
For better randomness use platform APIs or C11's <stdlib.h> random engines if available.
rand() % N is biased for large N; prefer rand() / (RAND_MAX + 1.0) * N.

Q: How do you measure elapsed time in C?
A: POSIX clock_gettime(CLOCK_MONOTONIC, &ts) gives nanosecond resolution and is not affected by wall-clock changes.
Subtract two struct timespec values to get the elapsed interval.
On Windows use QueryPerformanceCounter; clock() measures CPU time, which may be misleading for I/O-bound code.

Q: What is a thread-local variable?
A: _Thread_local int counter; (or thread_local in C23) gives each thread its own copy of the variable.
Useful for per-thread accumulators, error codes, or caches without locking.
Requires <threads.h> for the standard threading API (C11) or platform-specific thread APIs elsewhere.

Q: What does setjmp() and longjmp() do?
A: setjmp(env) saves the current execution state (including registers, stack pointer) into env and returns 0.
longjmp(env, val) later restores that state, making setjmp return val (non-zero).
This is C's non-local goto: it can jump out of deeply nested calls but skips destructors/cleanup, so it interacts poorly with RAII-like idioms.

Q: What is signal() used for?
A: signal(sig, handler) installs handler as the function called when the process receives signal sig.
Common signals: SIGINT (Ctrl-C), SIGTERM, SIGSEGV, SIGFPE, SIGALRM.
Inside a signal handler, only async-signal-safe functions may be called; printf is generally not safe.

Q: What does sigaction() improve over signal()?
A: sigaction() is the POSIX replacement for signal() with more control: explicit flags (SA_RESTART), a siginfo_t payload, and reliable semantics across platforms.
It also lets you block signals during the handler, avoiding re-entrancy issues.
Always prefer sigaction in portable Unix code.

Q: What does the getopt() function do?
A: getopt(argc, argv, "hv") parses command-line options one at a time, supporting short flags like -h and -v.
optarg holds the argument to an option; optind is the index of the next argv element.
POSIX provides getopt_long for long options like --help.

Q: What is calloc() compared to malloc() in terms of overflow safety?
A: calloc(n, size) checks for n * size overflow before allocating.
malloc(n * size) with overflowing arguments produces a smaller-than-expected allocation, which is a classic security bug.
If you compute sizes yourself, test for overflow explicitly.

Q: How do you implement a dynamic array that grows on demand?
A: A common pattern:
int *a = NULL; size_t cap = 0, len = 0;
void push(int v) {
  if (len == cap) { cap = cap ? cap*2 : 4;
    a = realloc(a, cap * sizeof *a); }
  a[len++] = v;
}
Doubles capacity to keep amortized push O(1).

Q: What is a function with no return value?
A: Functions declared void perform actions without producing a value:
void print_int(int x) { printf("%d\n", x); }
Calling print_int(5); is a statement, not an expression.
Use return; (with no value) to exit early.

Q: What is the difference between C and C++?
A: - C has no classes, no templates, no exceptions, no references, no function overloading by default
- C++ adds OOP, generics, RAII, and a richer standard library
- C is typically compiled as C89/C99/C11/C17/C23; C++ has its own separate standards
Most valid C is also valid C++, but with different semantics for some constructs (e.g. 'A' is int in C, char in C++).

Q: What is the difference between fread() and fwrite()?
A: - fread(ptr, size, nmemb, fp) reads up to nmemb items of size bytes each into ptr; returns the number of full items read
- fwrite(ptr, size, nmemb, fp) writes items from ptr; returns the number of full items written
Both are typically used in binary mode.

Q: What is snprintf() and why is it preferred over sprintf()?
A: snprintf(buf, n, fmt, ...) writes at most n-1 characters to buf and always null-terminates.
sprintf(buf, fmt, ...) has no length limit and is a major source of buffer overflows.
Always use snprintf; the return value tells you the length that would have been written.

Q: What is strncpy() and what is the null-terminator pitfall?
A: strncpy(dst, src, n) copies up to n bytes from src.
If src is shorter than n, the remainder is padded with '\0'.
If src is longer or equal to n, the result is not null-terminated; you must add one yourself.
Modern code prefers snprintf or memcpy followed by manual termination.

Q: What is the difference between memcpy and strcpy semantics?
A: - memcpy copies exactly n bytes; works on any data including embedded '\0'; requires the destination region to be large enough
- strcpy copies until a '\0' in the source; assumes destination is large enough — overflow risk
Use memcpy when you know the length; use strcpy only when you actually need to copy a C string and the destination is provably large.

Q: What are the four storage durations in C?
A: - Automatic: block scope, lives until the block ends
- Static: the entire program; covers globals and static locals
- Allocated: malloc/calloc/realloc lifetime
- Thread: _Thread_local, lifetime of the containing thread

Q: What is the role of stdint.h versus inttypes.h?
A: <stdint.h> defines the exact-width integer types (int32_t, uint64_t, ...) and macros like INT32_MAX.
<inttypes.h> includes <stdint.h> and adds the PRI/SCN format macros for printf/scanf.
Use inttypes.h when you need both fixed-width types and portable format strings.

Q: What is the goto statement and when is it acceptable?
A: goto label; jumps to a label within the same function.
C's classic use case is breaking out of nested loops or jumping to centralized cleanup:
for (...) for (...) if (err) goto fail;
...
fail: cleanup(); return -1;
Avoid goto for forward jumps that skip initialization; this is undefined.

Q: What are the conversion specifiers for size_t and intmax_t in printf?
A: For portability:
- printf("%zu", x); prints a size_t
- printf("%zd", x); prints a signed ssize_t/ptrdiff_t
- printf("%" PRIu64 "\n", u); from <inttypes.h> prints a portable uint64_t
Using %d for size_t is a common bug on 64-bit systems.

Q: How does C's operator evaluation order work for function arguments?
A: Until C11 the order in which arguments to a function are evaluated was unspecified; foo(f(), g()) could call f or g first.
Since C11, every expression is sequenced: full expressions are sequenced after their operands, and operators like &&, ||, ,, and ?: introduce sequence points, so the left side is fully evaluated before the right.

Q: What is the difference between ++ on pointers and ++ on integers?
A: ++ on an int * advances the pointer by sizeof(int) bytes, not by 1.
This is why pointer arithmetic "just works" for arrays: p + n points to the n-th element regardless of element size.
Subtracting two pointers gives the number of elements between them, again scaled by sizeof(T).

Q: What is size_t and when should it be used?
A: size_t is the unsigned type used for sizes and counts — result of sizeof, arguments to malloc/strlen/memcpy, etc.
It is at least 16 bits and never smaller than int.
Loop counters or array indices that come from size_t should be compared with care, since they cannot be negative.

Q: What is the difference between static and dynamic linking?
A: - Static linking copies library code into the executable at build time; no dependency at runtime
- Dynamic linking leaves shared libraries external and resolves symbols at startup or on first use
Static gives a self-contained binary; dynamic gives smaller binaries and easier patching but introduces the DLL hell/SONAME compatibility concerns.

Q: What is a static initializer in C?
A: A static initializer sets the initial value of a global or static variable at load time.
int counter = 42;
It must be a constant expression in C; non-constant initializers (e.g. function calls) became possible only as designated dynamic initializers in a recent proposal, otherwise the variable is zero-initialized before main.

Q: What is the implicit conversion rule for function arguments?
A: Functions may be called with arguments whose types differ from the parameters; C performs default argument promotions.
In particular, float is promoted to double, and any integer smaller than int is promoted to int or unsigned int.
This is why C's variadic functions (via <stdarg.h>) use va_arg(ap, double) for floats.

Q: What is the difference between struct copy and pointer-to-struct?
A: struct S a = b; performs a shallow copy of every member.
If a member is a pointer, only the pointer is copied, not the pointed-to data.
Using struct S *p = &b; shares the data — mutations through p are visible via b.
For structs with pointer members, copying usually needs an explicit "deep copy" function.

Q: What is the difference between bit-fields and bitwise operations?
A: Bit-fields declare named members with explicit widths inside a struct and let you access them as f.x.
Bitwise operations like value & (1 << n) manipulate raw bits of an integer.
Bit-fields are clearer but limit you to _Bool/integer types and disallow taking the address; bitwise ops work on any integer and can be optimized into wider words.

Q: What are the advantages of for portability?
A: int32_t is exactly 32 bits on every conforming implementation; int may be 16, 32, or some other width.
Use fixed-width types when serializing data, calling into a defined ABI, or implementing a protocol.
For arithmetic where the width is not critical, prefer int/unsigned, which the compiler can optimize for the target.

Q: What is the C standard and which versions exist?
A: The C standard is published by ISO/IEC JTC1/SC22/WG14. Versions include C89/C90, C95, C99, C11, C17, C23.
Major additions: C99 added // comments, <stdint.h>, VLAs, designated initializers, complex numbers.
C11 added _Alignas, _Generic, threads, atomics, and _Noreturn.
C23 refined many small issues and deprecated some legacy features.

Q: What is the relationship between #if, #elif, and #else?
A: #if evaluates a preprocessor expression (only integer literals, defined macros, and operators ==, !=, <, >, &&, ||, etc.):
#if defined(_WIN32)
// Windows code
#elif defined(__linux__)
// Linux code
#else
// fallback
#endif
#elif chains branches; #else is the catch-all.

Q: What is the difference between struct member access with . and ->?
A: - s.field accesses a member of a struct value
- p->field accesses a member through a struct pointer
Syntactically p->field is exactly equivalent to (*p).field.
Use -> on pointers to avoid a temporary copy and to clearly signal intent.

Q: How do you read a struct from a binary file portably?
A: Use fread(&record, sizeof record, 1, fp).
But beware: layout depends on padding and endianness, which differ across platforms.
For portable binary formats, serialize each field explicitly (e.g. fwrite(&v, sizeof v, 1, fp) on each integer) and define a fixed byte order.

Q: What is the difference between the address space of code, data, stack, and heap?
A: A typical C process has distinct regions:
- Text/code: the compiled program, read-only and executable
- Data: initialized globals and static variables
- BSS: uninitialized globals and statics (zero-initialized)
- Heap: grows up via malloc/free
- Stack: grows down with each function call

Q: What is the purpose of stddef.h?
A: <stddef.h> defines core typedefs and macros used throughout the standard library:
size_t, ptrdiff_t, NULL, offsetof.
It is included implicitly by many other headers but is safe to include directly whenever you need these fundamental types.

Q: What does offsetof() do?
A: offsetof(struct S, member) returns the byte offset of member within struct S as a size_t.
Useful for binary serialization, container_of-style macros, and asserting struct layout.
The standard guarantees that the first member has offset 0.

Q: What is the container_of pattern?
A: A macro that recovers the containing struct from a pointer to an embedded member:
#define container_of(ptr, type, member) \
  ((type *)((char *)(ptr) - offsetof(type, member)))
Widely used in the Linux kernel; lets intrusive linked lists carry any type via void * indirection.

Q: What are the stages of compilation in more detail?
A: - Preprocessing: cpp resolves directives, expands macros, includes headers, produces a translation unit
- Compilation: the compiler parses the TU, performs type checking, and emits assembly (.s)
- Assembly: as translates assembly into relocatable object code (.o)
- Linking: ld combines object files and libraries, resolves symbols, produces the final executable or shared object

Q: What does the const qualifier do on function parameters?
A: void print(const char *s); tells both compiler and readers that print will not modify the bytes pointed to by s.
This enables compiler optimizations, makes intent clear, and allows passing pointers to read-only data.
Combined with restrict, it gives the optimizer maximum freedom.

Q: What is the difference between macro constants and const variables?
A: - #define PI 3.14159 — text substitution before compilation; not a real object; cannot be debugged by name; respects no scope
- const double PI = 3.14159; — an actual read-only variable with a type; has scope; debugger can show it
Prefer const (or enum) where possible; macros for conditional compilation or token pasting.

Q: How do you make a portable Makefile rule for a C program?
A: CC = gcc
CFLAGS = -std=c11 -Wall -Wextra -O2
app: main.o util.o
  $(CC) -o $@ $^
%.o: %.c
  $(CC) $(CFLAGS) -c -o $@ $<
This compiles each .c to a .o with strict warnings, then links them into app.
Use -MMD -MP to auto-generate header dependencies.

Q: What is the difference between int main(void) and int main()?
A: - int main(void) explicitly states the function takes no parameters; calling it with arguments is a compile error
- int main() in C means the same in modern C; in C++ it means "takes any arguments"
In C, both declare a parameter-less function; the explicit void is clearer.

Q: What are the signatures of main?
A: Standard C defines two prototypes:
int main(void);
int main(int argc, char *argv[]);
Returning int from main is required; the value becomes the process exit status.

Q: What is the argc/argv convention?
A: argc is the count of command-line arguments, including the program name.
argv[0] is the program name; argv[1] ... argv[argc-1] are the user arguments.
By convention, argv[argc] is a NULL pointer so iteration can stop without checking argc.

Q: What is the difference between char *argv[] and char **argv?
A: They are equivalent in function parameters: both declare a pointer-to-pointer-to-char.
The array notation char *argv[] documents intent more clearly; char **argv is more abstract.
Inside main, you can index argv[i] like an array or use pointer arithmetic.