A strong answer
A strong candidate frames How should a function take and return a struct? around the exact memory layout, compiler semantics, and hardware execution model covered in Passing, Returning and Pointing to Structs. Choose value, reference and pointer forms for structs and explain free return by value.
Lesson 7 of module 2 gave three ways to pass a value, and lessons 6 and 8 of module 3 added pointers. A struct makes the choice matter. A struct can be much larger than an int, so copying it is no longer free, and the same struct may need to be handed to a function, modified there, or returned from it.
Returning one raises its own worry. If a function builds a struct and returns it by value, does the whole thing get copied back? And when all you have is a pointer to a struct, how do you reach its members? This lesson answers both.
In a production interview, always connect the specification rule to its concrete runtime consequence: state what lives on the stack versus the heap or static storage segment, how the compiler or operating system validates alignments, lifetimes, and error codes, and what happens under adversarial or boundary inputs (such as zero-length buffers, integer overflow, concurrent mutation, or partial I/O). Backing your explanation with a small trace of register/memory state transitions immediately separates senior systems engineers from candidates who only memorized syntax.
What the interviewer is listening for
- Mastery of core invariant: Predict whether a function changes the caller's struct for value, reference and pointer parameters.
- Mastery of core invariant: Use the dot and arrow operators correctly and say what the arrow means.
- Mastery of core invariant: Explain why returning a struct by value is usually cheap.
- Hardware and ABI awareness: Explaining how sizes, alignments, calling conventions, or kernel boundaries behave on real 64-bit targets rather than relying on implementation-defined assumptions.
- Defensive engineering: Naming the exact compiler flags (
-Wall -Wextra -Werror), static analyzers, or runtime sanitizers (-fsanitize=address,undefined,thread) that verify correctness in CI.
Common mistakes
- Explaining only surface syntax without tracing the underlying memory addresses, ownership lifetime, or kernel/user-space state transitions.
- Assuming undefined behavior (such as signed overflow, uninitialized reads, strict-aliasing violations, or dangling pointers) will "just work" because it passed an unoptimized
-O0debug build. - Ignoring return codes, partial reads/writes, alignment padding, or cleanup paths when an allocation or syscall fails midway through a function.