Interview questions

Decorator Pattern in C Interview: A Crash Course

August 14, 2025Updated May 20, 202616 min read
Can The Decorator Pattern In C Be Your Secret Weapon For Acing Your Next Interview

Learn the Decorator Pattern in C interview settings with a minimal working example, memory ownership rules, decorator layering tradeoffs, and a ready-to-say.

Most candidates who struggle with the decorator pattern in a C interview aren't missing knowledge — they're missing a clean mental model that survives contact with a real C codebase. The decorator pattern C interview question trips people up not because the concept is hard, but because every clean explanation they've seen uses Java or Python classes, and when the interviewer asks them to sketch it in C, the class-based scaffolding vanishes and they're left hand-waving about "something like inheritance but with structs."

This guide gives you one minimal, correct C example you can build from memory, explain without bluffing, and defend when the follow-up questions come. Wrapping, delegation, ownership, and tradeoffs — all of it, in one tight pass.

Say the Pattern Clearly Before You Touch the C Code

What the Decorator Pattern is actually doing

The Decorator Pattern is runtime extension by wrapping: you take an existing object, wrap it in a new object that holds a reference to it, and add behavior before or after delegating to the original. The wrapped object doesn't know it's been wrapped. The wrapper looks identical to the wrapped thing from the outside. That interface consistency is the whole point.

The class-based explanation — "a decorator implements the same interface as the component and holds a reference to one" — is correct as far as it goes. But it stops short of the question interviewers actually care about: why would you structure code this way instead of just modifying the original, and what does "same interface" mean when you're writing C? Steelmanning the OOP version is fine in an interview. Just don't stop there.

The Gang of Four definition frames it as attaching additional responsibilities to an object dynamically, as a flexible alternative to subclassing for extending functionality. That framing holds in C — the mechanism just looks different.

What this looks like in practice

Here is the sentence you want ready before the interview starts:

"A Decorator wraps an existing component, exposes the same interface, and adds behavior by delegating to the wrapped object — so callers don't know or care that the behavior changed."

The add-logging example is the clearest way to make this concrete. You have an operation — say, `write_sensor_data()` — and you want to add timing around it without touching the function itself. A decorator wraps the call: it records a timestamp, calls the original, records another timestamp, and logs the delta. The caller still calls one function. The original function is unchanged. The behavior is extended at runtime by layering, not by editing.

I've used this approach specifically to add tracing around stable, low-level code paths that I couldn't safely modify — functions that had been in production for years and had no test coverage worth speaking of. Wrapping let me add the behavior I needed without touching the code I couldn't afford to break.

Why C Forces You to Build the Pattern by Hand

The missing inheritance ladder is the whole point

In a language with classes, the "same interface" part of the pattern is enforced by the type system. The decorator subclasses or implements the same interface as the component, and the compiler catches mismatches. In C, none of that exists. There is no inheritance, no interface keyword, no virtual dispatch. Composition in C is not a stylistic preference — it's the only tool available.

This is actually a good thing to say in an interview, because it shows you understand why the pattern looks the way it does in C, not just that it looks different. The struct-and-function-pointer approach you'll use isn't a workaround. It's the direct expression of what the pattern is doing conceptually: define a shared shape, hold a reference to the wrapped object, delegate explicitly.

The principle of composition over inheritance isn't a C-specific idea, but C makes it mandatory in a way that clarifies why the principle exists at all.

What this looks like in practice

The mental model is: one struct that defines the interface (a function pointer), a base implementation that fills that function pointer with real behavior, and a wrapper struct that holds a pointer to the base and fills the same function pointer with its own behavior — which calls the base's version inside.

The "why not just add a flag?" question is worth anticipating, because interviewers sometimes ask it. Adding a `bool enable_logging` flag to the original struct couples logging logic into the base component, makes the base harder to test in isolation, and means every future behavior variant needs another flag. Wrapping keeps each layer's concern separate and composable. In embedded and systems work, keeping interfaces small and coupling low isn't aesthetic — it's how you avoid a maintenance problem six months later when the codebase has grown and the original author has left.

Build the Smallest C Decorator Chain That Still Counts

Minimal types for the base component and wrapper

The base component needs two things: a data shape and a function pointer that represents the operation. The decorator needs three things: the same function pointer (so it looks identical to callers), a pointer to the wrapped component, and its own data.

The `base` field being first is load-bearing. It means a pointer to `ConcreteWriter` is also a valid pointer to `Component`, which is how C fakes interface polymorphism. This is the structural piece that makes delegation possible without a vtable.

What this looks like in practice

Here is a decorator that adds logging around the base write operation:

Usage:

The ownership comment in the code is not optional. Write it in a real interview sketch too — it signals that you've thought through the memory story, not just the structural shape.

The interview answer you want ready on your tongue

"The decorator holds a pointer to the wrapped component and exposes the same function pointer interface. When write is called on the decorator, it runs its own behavior — logging, in this case — then forwards the call to the wrapped object using the same function signature. The wrapped object doesn't know it's wrapped. The caller doesn't know there's a decorator. That's the whole contract."

That's the answer. It describes wrapping plus forwarding in plain language, and it doesn't oversell the cleverness of the pointer casting. Interviewers who ask about C design patterns from a systems programming background are listening for whether you understand the delegation chain, not whether you can name the pattern.

Own the Wrapped Object or the Chain Gets Ugly Fast

The part candidates usually hand-wave

Ownership is where most decorator explanations fall apart. People can describe the wrapping structure, but when the interviewer asks "so who calls free?" the answer gets vague. In a C decorator chain, the ownership rule has to be explicit and consistent, because the compiler won't enforce it.

The convention that works: each decorator owns the object it wraps. The outermost layer is what the caller holds. Freeing the outermost layer triggers a chain of frees inward. This mirrors how the construction went — the base was created first, passed into the decorator, and the decorator took ownership at that point.

C memory management references like the Linux kernel coding style documentation reinforce this: ownership transfers should be explicit, and the allocation and deallocation sites should be easy to audit.

What this looks like in practice

If you have a chain — base → logging decorator → timing decorator — the timing decorator owns the logging decorator, and the logging decorator owns the base. Freeing the timing decorator frees the logging decorator, which frees the base. The annotated free function makes this explicit:

Double-free risk appears when a caller holds a pointer to the base and also calls free on the outermost decorator. The fix is simple: once you pass ownership into a decorator, treat the original pointer as invalid. Don't keep it. If you need access to the base for inspection, keep it through the chain, not through a separate pointer.

Explain Why Decorator Beats Inheritance Without Sounding Rehearsed

Inheritance feels neat until the variants pile up

Subclassing looks clean for one extension. You have `Writer`, you create `LoggingWriter extends Writer`, done. The problem arrives when you want combinations: logging plus timing, logging plus validation, timing plus validation, all three. With inheritance, that's six subclasses for three behaviors, and it scales exponentially. Decorator vs inheritance isn't a close call once you're past the first variant.

In C, you don't have real subclassing anyway, so the comparison is slightly academic — but interviewers still ask it, because it tests whether you understand why the pattern is structured the way it is, not just how to implement it.

What this looks like in practice

Say you have three behaviors: logging, timing, and input validation. With wrapping, you compose them at runtime:

Swap the order of logging and timing and you get different behavior — timing now wraps logging, so you measure including the log overhead. That runtime flexibility is exactly what the open-closed principle is pointing at: the base component is closed for modification, but the system is open for extension by adding new wrappers. In stable low-level code — firmware, protocol handlers, sensor drivers — that distinction matters. You cannot always modify the original. You can almost always wrap it.

Know the Failure Modes Before You Stack Too Many Layers

The hidden cost of a nice-looking chain

C design patterns generally come with the warning that abstraction has a cost, and a decorator chain is no exception. Each layer adds an indirection: one more function pointer call, one more pointer dereference, one more stack frame in a debug trace. For most applications this is negligible. For tight interrupt handlers or real-time loops, it can matter.

The harder problem is debugging. A chain of three decorators means that when something goes wrong, you have three places where the behavior might have diverged from what you expected — and the call stack in a debugger shows you the chain from the outside in, not from the inside out.

What this looks like in practice

Consider: validation decorator → logging decorator → base. If validation passes bad data through when it shouldn't, the log will show the bad data being written. The bug is in the validation layer, but the symptom shows up in the log and at the base. Tracing this in gdb means stepping through multiple forwarding calls before you reach the point of failure.

I've had a wrapper chain become genuinely painful to trace — three layers deep, each with its own state, and a subtle ordering bug where the timing wrapper was measuring before the validation wrapper had finished. The fix was straightforward once found, but finding it required mentally unwinding the chain in the right order. The lesson: keep chains short, name each layer clearly, and document the intended order. A chain that's hard to explain is a chain that's hard to debug.

Abstraction overhead in layered systems is a well-documented tradeoff in systems design literature — the benefit of composability has to be weighed against the cost of indirection, especially in performance-sensitive paths.

Use the Pattern in Embedded Work Without Overcomplicating It

The practical version for tight C codebases

The embedded systems decorator pattern is not always the right tool. In a constrained environment — limited RAM, no dynamic allocator, interrupt-driven execution — a full malloc-based decorator chain may be inappropriate. But the pattern's core idea — wrapping a function call with pre/post behavior while keeping the interface stable — is still useful, even if the implementation looks different.

The practical version for embedded work often skips dynamic allocation entirely. Instead of heap-allocated wrapper structs, you use statically allocated instances and initialize them at startup. The function pointer interface remains. The ownership is trivially obvious because nothing is on the heap.

What this looks like in practice

A UART send function wrapped with a timeout check and a transmission log is a real embedded scenario. The wrapper calls the timeout check, calls the base send, then logs the result. All three are statically allocated structs initialized in `main()` or a hardware init routine. The decorator adds behavior without touching the UART driver, which might be vendor-supplied or safety-certified code you cannot modify.

In interrupt-sensitive code, keep the wrapper logic minimal. If the pre-behavior in a decorator takes too long, you've changed the timing characteristics of the wrapped call — which can break assumptions in the base. The rule I'd apply: if the decorator does anything that could block or take variable time, it does not belong inside an interrupt context, regardless of how clean the abstraction looks. Embedded systems references like Barr Group's embedded C coding standard consistently emphasize minimal overhead and predictable execution paths — the decorator pattern serves those goals only when the wrappers are genuinely lightweight.

FAQ

Q: What is the Decorator Pattern in one interview-ready sentence?

A Decorator wraps an existing component, exposes the same interface, and adds behavior by delegating to the wrapped object — so callers don't know or care that the behavior changed. That sentence covers the three things interviewers are listening for: wrapping, interface consistency, and delegation.

Q: How do you implement the Decorator Pattern in C without classes or inheritance?

Define a struct with a function pointer as the "interface." The base implementation fills that function pointer with real behavior. The decorator struct contains the same function pointer field (first, for safe casting) plus a pointer to the wrapped component. The decorator's function calls its own logic, then delegates to the wrapped object via `wrapped->fn(wrapped, ...)`. No inheritance required — just consistent struct layout and explicit delegation.

Q: Who owns the wrapped object in a C decorator chain, and when should it be freed?

The decorator that receives a component pointer takes ownership of it. The outermost layer is what the caller holds. Freeing the outermost decorator triggers a chain of frees inward — each decorator's free function frees its wrapped object before freeing itself. Once you pass a pointer into a decorator, treat your original pointer as invalid to avoid double-free.

Q: Why use composition instead of subclassing for this pattern?

Subclassing scales badly when you want mix-and-match behaviors — three behaviors produce six subclasses. Composition lets you combine behaviors at runtime by stacking wrappers in any order. In C specifically, you don't have real subclassing, so composition via struct embedding and function pointers is the only practical path. The open-closed principle makes the architectural case: extend behavior by wrapping, not by modifying stable code.

Q: How is Decorator different from Adapter, Proxy, and Bridge in a C interview context?

Decorator adds behavior while keeping the same interface. Adapter converts one interface to another — it's about compatibility, not extension. Proxy controls access to the wrapped object (think lazy init or access checks) but doesn't add new behavior in the decorator sense. Bridge separates abstraction from implementation so both can vary independently — it's a structural pattern for managing two parallel hierarchies, not a runtime wrapping pattern. In a C interview, the clearest differentiator is intent: Decorator extends, Adapter translates, Proxy guards, Bridge decouples.

Q: What real-world C or embedded example can I use to explain this pattern clearly?

Wrapping a UART send or sensor read with logging, validation, or timeout logic is the clearest embedded example. The base function is vendor-supplied or safety-certified code you cannot modify. The decorator adds the behavior you need — tracing, error checking, timing — without touching the original. For non-embedded contexts, wrapping a file write or network send with retry or logging logic makes the same point.

Q: What are the main tradeoffs or failure modes when stacking multiple decorators?

Extra indirection per layer, harder debugging when the chain is deep, and order-sensitivity — the order of wrappers changes the behavior and can change the timing characteristics of the call. In embedded work, a decorator that does variable-time work inside an interrupt context can break real-time guarantees. Keep chains short, document the intended order, and make sure each wrapper's behavior is genuinely independent of the others.

Q: How do you test a decorated object or function chain to make sure each layer behaves correctly?

Test each layer in isolation first: create the base component and verify it behaves correctly, then wrap it with one decorator and verify the decorator's behavior without the full chain. For the chain as a whole, use a mock or stub base that records what it receives, then verify that each decorator in the chain produces the expected pre/post behavior around the delegation call. This isolation approach also confirms that each decorator's free function correctly handles the wrapped object — run it under valgrind or AddressSanitizer to catch leaks and double-frees.

How Verve AI Can Help You Ace Your Software Engineer Coding Interview

The hardest part of a C design patterns interview isn't writing the code on a whiteboard — it's explaining the ownership chain, the tradeoffs, and the "why this instead of that" follow-ups while someone is watching you think. Most preparation tools can quiz you on pattern definitions, but they can't respond to what you actually said, catch where your explanation went vague, or push back with the follow-up the interviewer would reach for.

Verve AI Interview Copilot is built for exactly that gap. It listens in real-time to your answer as you give it, tracks where the explanation loses precision — say, when you describe delegation without mentioning who frees the wrapped object — and surfaces the follow-up before the interviewer does. For a topic like the decorator pattern in C, where the difference between a confident answer and a hand-wavy one is one or two specific sentences about ownership and interface consistency, that kind of live feedback changes the preparation entirely. Verve AI Interview Copilot stays invisible while you practice, so the session feels like a real interview, not a quiz. Use it to rehearse the ownership explanation until it comes out clean, then use it again to practice the Decorator vs inheritance comparison until you can say it without sounding like you memorized it. That's the preparation that actually transfers to the room.

Conclusion

You don't need a grand theory of design patterns to answer this question well. You need one clean C example you can sketch from memory, one sentence that explains wrapping plus delegation without hand-waving, and a clear answer to who frees what and when.

Do this before your interview: say the interview-ready sentence out loud — the one about wrapping, same interface, delegation — until it comes out without hesitation. Then trace the ownership chain in the code example above until it feels boring. When the interviewer asks the follow-up about memory, you want the answer to feel obvious, not retrieved. That's the difference between knowing the pattern and being able to use it under pressure.

JM

James Miller

Career Coach

Ace your live interviews with AI support!

Get Started For Free

Available on Mac, Windows and iPhone