Menu

Earn Premium with Referrals

Invite your friends and earn Premium rewards through our referral program.

See how it works and start inviting friends.

Top 25 - Part 2
C

Top 25 - Part 2

Practice the remaining 10 questions from the top 25 C programming interview questions.

1. What does setjmp and longjmp provide in C programming?

Answer: Non-local jumps — a low-level exception-handling mechanism that can jump back across multiple function call frames.

setjmp and longjmp (from <setjmp.h>) are the C way to “throw” across stack frames without normal returns:

  • setjmp(buf) saves the execution environment (stack pointer, instruction pointer, registers, signal mask) into a jmp_buf, and returns 0 the first time.
  • longjmp(buf, value) restores that saved environment and jumps execution back to the setjmp point — unwinding any intermediate function frames.
jmp_buf env;
if (setjmp(env) == 0) {
    do_work();          // deep call chain
} else {
    // longjmp landed here, value != 0
    handle_error();
}

It’s the classic error-handling idiom for C (used by old C codebases for exception-like flow). Caveats: local automatic variables modified between setjmp and longjmp may be indeterminate unless volatile; you can’t jump past the frame containing setjmp (that frame must still be alive). The interview answer: non-local jumps for exception-style control flow across function call frames.

2. What is the difference between passing arguments by value versus passing by reference (using pointers) in C?

Answer: C only supports pass-by-value. To let a function modify a caller’s variable, you pass a copy of its address (a pointer), then dereference inside the function.

Every C argument is passed by value — a copy. When you pass a pointer, you’re passing a copy of the address value, not the variable itself:

void swap(int *a, int *b) {
    int t = *a; *a = *b; *b = t;   // dereference to reach caller's memory
}
int x = 1, y = 2;
swap(&x, &y);   // passes copies of addresses; swap modifies x,y via deref

The function can then reach into the caller’s memory through the address it received. That’s “pass by reference” in C in effect — but mechanically it’s still pass-by-value of a pointer.

(The one nuance: a const pointer int *const p would make the pointer itself immutable, but that’s separate.) The interview answer: C passes everything by value; pointers pass a copy of the address, letting functions modify caller variables via dereferencing.

3. What is the value of an uninitialized local automatic variable inside a function body?

Answer: Indeterminate (garbage) — whatever bit pattern happens to be in that stack memory; reading it before writing is undefined behavior.

Local (automatic) variables are not zero-initialized. They contain whatever leftover data occupied that stack location — stale values from previous frames. The exact content is unpredictable.

void f() {
    int x;          // indeterminate value
    printf("%d", x);  // UB: reading an uninitialized variable
}

Reading an uninitialized automatic variable is undefined behavior in C (for non-character types; even char uninitialized reads are UB before assignment in practice, though unsigned char/char are special-cased for indeterminate reads). The value is never guaranteed to be 0, NULL, or anything specific.

Contrast: static and global variables are zero-initialized. The interview answer: indeterminate garbage from the stack; reading before assignment is undefined behavior.

4. What does the enum feature in C construct?

Answer: A user-defined enumeration type: named integer constants that improve readability.

enum creates a set of named constants:

enum Color { RED, GREEN, BLUE };
enum Color c = GREEN;   // c == 1

By default the constants start at 0 and increment by 1 (RED=0, GREEN=1, BLUE=2), but you can assign explicit values:

enum Status { OK = 0, ERROR = -1, TIMEOUT = 2 };

Benefits: self-documenting code (names instead of magic numbers), and the compiler can warn about unhandled cases. In C, enum values are just ints underneath — they interconvert freely with integers (unlike C++ where the rules are stricter). The interview answer: an enumeration of named integer constants for readability and type-organization.

5. What is the function of fflush(stdout)?

Answer: It forces pending buffered output in the stdout stream to be written out immediately to the terminal or file.

Standard I/O (stdio) buffers output for efficiency — printf writes into an internal buffer that’s flushed periodically (on newline for line-buffered terminals, on buffer-full, or at program exit). fflush(stdout) forces any buffered bytes out right now.

When you need it:

  • Interleaving printf with fprintf(stderr, ...) (stderr is unbuffered, so ordering matters) — flush stdout so the relative order is correct.
  • Prompting for input before reading: printf("Enter: "); fflush(stdout); ensures the prompt appears before the user types.
  • Crash-prone programs: flush critical output so it survives an abnormal exit.

The interview answer: fflush(stdout) pushes buffered stdout data to the underlying output immediately.

6. What is the result of applying sizeof to a dereferenced pointer sizeof(*ptr) where double *ptr;?

Answer: The size of double8 bytes on standard systems.

*ptr has type double, so sizeof(*ptr) is sizeof(double) = 8 bytes. The clever part: sizeof does not evaluate its operand. It only needs the type of the expression, which is known at compile time. So ptr needn’t be initialized or even valid — sizeof(*ptr) never dereferences anything at runtime; there’s no crash.

This is a widely used idiom because it’s type-robust: sizeof(*ptr) stays correct even if you change ptr’s type. Compare sizeof(ptr) which would be the pointer size (8 on 64-bit). The interview answer: sizeof(*ptr) = sizeof(double) = 8, computed without evaluating the dereference.

7. What problem arises from using strcpy(dest, src) without prior length verification?

Answer: A buffer overflow — if src is longer than dest’s capacity, strcpy writes past the end, corrupting surrounding memory.

strcpy(dest, src) copies bytes until it hits a '\0' in src — with no bounds checking on dest. If src is longer than the destination buffer can hold:

  • Bytes spill past dest’s end into adjacent memory — other variables, saved return addresses, heap metadata.
  • Result: crashes, subtle data corruption, or security exploits (stack-smashing attacks that overwrite the return address).
char buf[10];
strcpy(buf, "this string is way too long");   // overflow

Safe alternatives: strncpy (bounded, but doesn’t guarantee null-termination) or better, snprintf(dest, size, "%s", src) which always null-terminates and respects the size. The interview answer: unbounded copy → stack/heap buffer overflow, memory corruption, and potential code-execution exploits.

8. What is the evaluation result of bitwise operation 5 & 3 in binary (0101 & 0011)?

Answer: 1.

Bitwise AND (&) compares bits positionally — each output bit is 1 only if both input bits are 1:

  0101   (5)
& 0011   (3)
------
  0001   (1)

So 5 & 3 = 1. (& is bitwise AND; don’t confuse with &&, the logical AND.) The interview answer: 0001₂ = 1.

9. What does the union structure do in C?

Answer: All members share the same memory location, sized to fit the largest member — only one member is meaningful at a time.

A union overlays all its members at the same starting address:

union Value {
    int i;
    float f;
    char bytes[4];
};
// sizeof(union Value) == 4 (largest member)

Writing to one member overwrites the shared storage — reading a different member reinterprets those same bytes. The union is big enough for its largest member (plus alignment padding).

Use cases:

  • Type punning — inspect the bytes of a float as an int (with care; strict aliasing rules apply, and C23’s typeof punning via unions became legal).
  • Memory savings — when a variable is one-of-several types, a union uses max-size instead of the sum.
  • Variant/tagged structures — a union plus a discriminant enum field telling which member is active.

The responsibility: track which member is currently valid — there’s no automatic check. The interview answer: a single shared memory region sized for the largest member; only one field can safely hold a value at a time.

10. What is the effect of invoking abort() in a C program?

Answer: It raises SIGABRT and terminates the process immediately — without running atexit() handlers or flushing stdio buffers.

abort() (from <stdlib.h>) abnormally terminates the program:

  • Raises the SIGABRT signal; unless caught, the process dies immediately.
  • Skips atexit() cleanup handlers (the functions registered to run at normal exit).
  • Skips stdio buffer flushing — buffered output may be lost.

It’s the hard-kill for catastrophic/uncorrectable states where cleanup can’t be trusted. Compare exit(code), which does run atexit handlers and flushes streams before terminating normally. The interview answer: immediate SIGABRT termination, bypassing atexit handlers and stdio flushing.

My Private Notes

Notes are auto-saved locally to this device.