Find and fix wasted space in your C/C++ structs — without leaving the editor.

struct-align is a real-time layout analyzer for C and C++ structs. As you
type, it highlights padding bytes the compiler silently inserted between your
fields and offers a one-click reorder that recovers them — typically 10–40%
of the struct's size, with zero functional change.
Why struct padding matters
C and C++ lay out struct fields in declaration order. Each field must sit at an offset that is a multiple of its alignment. When the next field needs more alignment than the running offset provides, the compiler inserts padding bytes — silent, invisible, and free in CPU terms but costly in memory and cache.
Consider this struct:
struct Order {
char order_type; // 1 byte
double price; // 8 bytes
uint32_t quantity; // 4 bytes
uint64_t order_id; // 8 bytes
};
It looks like 21 bytes of data. The compiler actually allocates 32 bytes — eleven bytes are padding the developer never wrote. Reorder by alignment (largest first) and the same data fits in 24 bytes:
struct Order {
double price;
uint64_t order_id;
uint32_t quantity;
char order_type;
};
That's 25% less memory, with identical semantics. For systems processing millions of records — fintech order books, ECS components, network packet buffers, blockchain account state — this compounds into measurable gains in cache pressure, bandwidth, and dollar cost per request.
struct-align finds these wasted bytes automatically, every time you save.
Features
- Real-time padding diagnostics on every keystroke, powered by an in-process WebAssembly analyzer. No subprocess, no filesystem access — the analyzer is sandboxed inside the extension host.
- Quick Fix code action — apply the suggested reorder with a single click. Preserves comments, attributes, bitfield widths, and inline initializers.
- Hover popup showing the full byte map per struct (offset, size, field name, type) plus a per-field summary (offset, alignment, padding before, parent struct).
- Deep analysis (libclang) — opt-in for projects where the lightweight
analyzer can't resolve a custom type. Resolves templates, inheritance, and
stdlib types exactly, using your existing
compile_commands.json. - Multi-platform layout: target
x86_64-linux,x86_64-windows, orx86_32. Stdlib-aware heuristics (libstdcxx,libcxx,msvc-stl) forstd::string,std::vector<T>, and the other usual suspects. - Honest about limits — flags bitfields, packed structs, virtual methods, and unknown types instead of silently producing wrong numbers.
- Configurable via 5-level cascade: CLI flags > env vars > project config > user-global config > defaults. Register custom types (opaque handles, third-party objects) with explicit size and alignment.
Quick start
- Install the extension from the Marketplace
(or
code --install-extension vesangelov.struct-align). - Open any
.c,.cpp,.hpp,.cc, or.hfile containing astructdeclaration. - Padding above the
structAlign.paddingThreshold(default 5%) gets a warning squiggle on the struct name, with the savings shown inline. - Click the lightbulb 💡 → "Reorder fields to save N bytes" to apply.
- Hover any struct or field for the full byte-map / per-field view.
For optional deep (libclang) analysis on save:
- Install
libclang-dev(Linux),Xcode Command Line Tools(macOS), or the LLVM Windows installer. - Build and install
struct-align-clifrom the project repo with-DBUILD_LIBCLANG_PARSER=ON. - Set
structAlign.deepAnalysistoon-save.
Settings
| Key | Default | Description |
|---|---|---|
structAlign.platform |
x86_64-linux |
Target ABI for layout calculation. |
structAlign.stdlibImpl |
libstdcxx |
Stdlib heuristic column for std::string, std::vector<T>, etc. |
structAlign.variant |
lightweight |
lightweight (WASM, real-time) or libclang (CLI subprocess, deeper). |
structAlign.paddingThreshold |
5 |
Minimum padding percent to surface as a diagnostic. |
structAlign.enableRealTime |
true |
Analyze on every edit; disable to only analyze on save. |
structAlign.deepAnalysis |
off |
When to invoke Variant B: off, on-save, or on-demand. |
structAlign.cliPath |
"" (auto-detect) |
Path to struct-align-cli. Auto-discovered from PATH and ~/.local/bin. |
structAlign.compileCommandsPath |
"" (walk up) |
Explicit compile_commands.json path. Empty = libclang walks up from the source file. |
structAlign.deepAnalysisTimeoutSec |
5 |
Hard timeout for the libclang subprocess. |
structAlign.devSchemaValidation |
false |
Validate the WASM module's JSON output against the embedded ADR-0003 schema on every analysis. Off by default; enable for dev. |
Commands
| Command | Description |
|---|---|
struct-align: Analyze current file (lightweight) |
Manual one-shot of the Variant A analysis. |
struct-align: Run deep analysis (libclang) |
Manual one-shot of Variant B (no setting needed). |
struct-align: Show output channel |
Reveal the extension's "struct-align" output channel. |
How it works
The struct-align core is a C++20 layout calculator + optimizer, compiled to WebAssembly via Emscripten. The extension's TypeScript layer loads the WASM module lazily on first analysis, then calls it in-process — no spawn, no file I/O, no temp files.
Deep analysis (Variant B) routes through a native struct-align-cli
subprocess that uses libclang for full type resolution. Results are merged
with Variant A diagnostics under a distinct source label so you can tell
which layer produced which warning.
Architectural decisions are recorded as ADRs in the project repository.
Modelled constructs (v1.0)
| Construct | Status |
|---|---|
| Primitive types + pointers + arrays | ✅ full layout + reorder |
Stdlib types (std::string, std::vector) |
✅ via stdlib heuristic table |
| Custom types (in-file cascade or registry) | ✅ |
| Virtual methods (vtable pointer) | ✅ modelled, pinned at offset 0 |
| Single non-virtual inheritance | ✅ modelled via __base pinned |
| Unions | ✅ size + alignment (no reorder) |
#pragma pack / __attribute__((packed)) |
✅ honored for size (no reorder) |
| Bitfields | ✅ total size (no reorder) |
[[no_unique_address]] |
✅ detect + warn |
Known limitations
- Multiple / virtual inheritance — detected and flagged with
INHERITANCE_UNSUPPORTED; layout not modelled (ADR-0012 defers this to v2.0 because it needs non-linear field placement). - Polymorphism + inheritance combo — same reason; vtable+base ABI interaction is its own sub-algorithm.
- Uninstantiated templates — no concrete size, skipped with an
UNSUPPORTED_FEATUREinfo diagnostic. Instantiate via ausingdeclaration to make them visible. - Bitfield / packed / union optimization — layout is reported correctly but no reorder is suggested. Reordering these changes wire format / ABI and is deliberately never proposed.
The forward roadmap lives in ADR-0012.
Feedback & support
| Channel | Best for |
|---|---|
| GitHub Issues | Bug reports, feature requests, reproducible incorrect layouts |
| GitHub Discussions | Questions, ideas, sharing use cases |
| Email: [email protected] | Private security reports, non-public feedback |
| Marketplace Q&A | Quick end-user questions inside VSCode |
When reporting a bug or an incorrect layout, please include:
- The smallest struct that reproduces the issue — one file, one struct, no external types if possible.
- Output of
struct-align: Show output channel— timestamps + the diagnostic codes emitted make triage minutes instead of hours. - Your
structAlign.*settings — paste the relevant block fromsettings.json(particularlyplatform,stdlibImpl,variant,deepAnalysis). - Expected vs actual layout — what
sizeof/alignofyour compiler reports and what the extension reports.
Feature ideas are welcome even when incomplete. If you have a real-world struct that hits a limitation above, an issue with the offending code + "here's what I wish the tool said" is exactly the input v1.x releases are prioritised from.
License
MIT © vesangelov.