Skip to practice questions

Part 2 · Containers and aggregates

Dynamic storage and aggregate types

Choose queues, dynamic arrays, associative arrays, structures, and unions from their ownership, access, and representation contracts.

Reading path
2 of 3
Concept chapters
2
Practice links
7

Question-first preparation

Practice the mechanisms on this page.

Each question maps directly to one of the chapters below, so you can test the contract before reading the explanation.

03 · Dynamic storage

Choose a container by access pattern, lifetime, and ownership.

Dynamic arrays, queues, and associative arrays all grow at run time, but they solve different problems and have different copying, ordering, and missing-entry semantics.

Rule to say firstUse dynamic arrays for resize-infrequent contiguous collections, queues for ordered ends, and associative arrays for sparse key lookup; make aliasing explicit when elements are class handles.

Access-pattern selector

Three dynamic containers, three contracts

  1. 01Dynamic arraynew[N]

    Indexed collection; explicit allocation and resize.

  2. 02Queue[$]

    Ordered sequence with push/pop at the ends.

  3. 03Associative array[key_t]

    Sparse map with key-based lookup and ordered traversal rules.

Dynamic arraynew[N]

Indexed collection; explicit allocation and resize.

Resize
new[N](old)
Copies the retained prefix; allocation without old discards prior contents.
Queue bound
[$:max]
A bounded queue exposes an architectural capacity.
Traversal
first(ref key)
Returns success and writes the key through its argument.

Container selection is part of the model's algorithm and resource contract, not a cosmetic syntax preference.

01

Observe

Measure how data is inserted, removed, indexed, traversed, resized, and passed across subroutine boundaries.

02

Decide

Select the narrowest container whose methods match the dominant operation, then define bounds and failure handling.

03

Failure

An unbounded queue hides backpressure, a resized dynamic array silently loses elements, and an associative traversal that ignores the success return can use a stale key.

04

Prove

Assert capacity, check every method return, test empty and missing-key cases, and verify whether copied elements are values or shared object handles.

Allocation and resizing are observable operations

A dynamic array must be allocated before indexed use. Resizing with new_size and an optional source preserves only the overlapping prefix. Queue operations shift logical positions, while associative arrays allocate entries on demand.

Check every boundarysystemverilog
int dyn[];
int q[$:7];
int score[string];
string key;

dyn = new[4];
foreach (dyn[i]) dyn[i] = i * 10;
dyn = new[6](dyn);             // keep first four values

if (q.size() < 8) q.push_back(42);
if (!score.exists("pkt7")) score["pkt7"] = 0;

if (score.first(key)) begin    // return is success; key is an output
  do $display("%s=%0d", key, score[key]);
  while (score.next(key));
end

Copying a container does not deep-copy its object graph

Pass-by-value copies the container value. For integral elements that means independent data; for class-handle elements it copies handles, so both containers still refer to the same objects. Pass by ref when the caller's container shape must change, and clone elements when independent objects are required.

Container decision guide
NeedBest starting pointBoundary to review
Known compile-time sizeFixed unpacked arrayRange direction
Runtime size, random accessDynamic arrayResize and allocation
FIFO/deque behaviorQueueBound and empty access
Sparse numeric or string keysAssociative arrayexists and traversal
Shared mutable objectsAny handle containerClone versus alias

Methods return information—do not throw it away

delete, size, insert, push, pop, exists, first, last, next, and prev encode the container's state transitions. A pop on an empty queue or use of a missing associative entry should be handled as a protocol decision, not left to an accidental default.

  • foreach follows the declared index order; do not assume numeric ascending order for every declaration.
  • Associative iteration order is defined by the index type's ordering, not insertion order.
  • Wildcard-index associative arrays reduce type safety and portability; prefer a specific key type.
  • A queue is not a synchronization primitive—concurrent producers still need a mailbox, semaphore, or explicit protocol.

Explain it out loud

Interview reasoning checkpoints

Strong answer

Use a dynamic array for runtime-sized indexed storage, a queue for ordered insertion and removal, and an associative array for sparse key lookup. Then constrain capacity and define empty or missing access.

Reason it through
  1. Start from the dominant access operation, not just whether size changes.
  2. Account for allocation, ordering, and traversal guarantees.
  3. Verify the worst-case memory and backpressure behavior.
Interviewer lens

Good answers connect the language feature to algorithmic complexity and resource intent.

Common trap

Using an unbounded queue for every dynamic collection.

Strong answer

It copies the queue container, but class handles inside are still aliases to the same objects. A deep copy requires cloning each referenced object.

Reason it through
  1. Separate container storage from element representation.
  2. Integral elements are values; class variables are handles.
  3. Choose pass-by-reference only when the callee should change the caller's queue itself.
Interviewer lens

The expected distinction is shallow container copy versus deep object copy.

Common trap

Saying either “everything aliases” or “everything is independent.”

04 · Structures and unions

Use aggregates to make layout and validity explicit.

Structures group simultaneously valid fields. Unions overlay alternatives. Packing determines whether the aggregate has a contiguous, portable bit representation.

Rule to say firstUse a packed struct for a protocol word, an unpacked struct for a semantic record, and a tagged union when the active alternative must travel with the value.

One word, named fields

A corrected 32-bit instruction layout

  1. 01op3 bits · 5

    word[31:29]

  2. 02rd5 bits · 5

    word[28:24]

  3. 03rs15 bits · 7

    word[23:19]

  4. 04rs25 bits · 16

    word[18:14]

  5. 05imm14 bits · 14'h1B7E

    word[13:0]

op3 bits · 5

word[31:29]

Sample
32'hA53C1B7E
Asymmetric data makes ordering mistakes visible.
Packed union
Equal width
Every member occupies the same packed storage.
Unpacked aggregate
Semantic shape
It may synthesize, but has no guaranteed contiguous bit layout.

Named packed fields can replace manual slicing while keeping an exact on-wire representation.

01

Observe

Ask whether fields coexist, whether alternatives are mutually exclusive, and whether the object crosses a bit-level interface.

02

Decide

Choose struct versus union for validity and packed versus unpacked for representation; add an explicit discriminator when interpretation can vary.

03

Failure

Reading an inactive unpacked-union member is not a portable bit reinterpretation, and unequal-width packed-union members are illegal.

04

Prove

Assert $bits and member widths, round-trip asymmetric samples, and validate the discriminator before consuming a union member.

Packed structures are integral values

Members are laid out from left to right in declaration order. The first member occupies the most-significant portion of the packed aggregate. Packed structures can be sliced, compared, cast, and transferred as one bit stream.

Decode by layout, then prove the widthsystemverilog
typedef struct packed {
  logic [2:0]  op;
  logic [4:0]  rd;
  logic [4:0]  rs1;
  logic [4:0]  rs2;
  logic [13:0] imm;
} inst_t;

inst_t inst = inst_t'(32'hA53C_1B7E);

initial begin
  assert ($bits(inst_t) == 32);
  assert (inst.op  == 3'd5);
  assert (inst.rd  == 5'd5);
  assert (inst.rs1 == 5'd7);
  assert (inst.rs2 == 5'd16);
  assert (inst.imm == 14'h1B7E);
end

Union members describe alternative views

All members of a packed union must have the same packed width. An unpacked union does not promise a portable raw-bit overlay, and reading a member other than the one most recently written is undefined. A tagged union binds a legal member tag to the stored alternative where supported.

Aggregate semantics
FormMembers validBit layoutBest use
Unpacked structAllImplementation-dependentSemantic records
Packed structAllContiguous, declared orderPackets and registers
Unpacked unionOne activeNot a portable bit viewTyped alternatives
Packed unionOne interpretationEqual-width overlayExact reinterpretation
Tagged unionTag-selected memberTagged alternativeSafe variants

Explain it out loud

Interview reasoning checkpoints

Strong answer

Pack it when the fields must form one exact integral representation for a register, packet, cast, or port. Leave it unpacked when it is primarily a semantic record and physical layout is not the contract.

Reason it through
  1. Determine whether the aggregate crosses a bit-oriented boundary.
  2. If it does, define field order, width, signedness, and reserved bits.
  3. Assert $bits and round-trip a non-symmetric sample.
Interviewer lens

Mention that unpacked structs can still synthesize; packing is about representation, not synthesizability alone.

Common trap

Claiming every synthesizable struct must be packed.

Strong answer

No. Packed-union members must have equal packed width. If alternatives differ, use padding, a surrounding packed struct with a tag, or a tagged-union representation.

Reason it through
  1. A packed union provides multiple interpretations of the same storage.
  2. Different widths would leave ambiguous storage and assignment behavior.
  3. Use $bits checks near typedefs so a member change fails loudly.
Interviewer lens

The answer should distinguish packed and unpacked unions and mention active-member validity.

Common trap

Using an unpacked union as a portable type-punning mechanism.