Struct field offset calculator
A char, an int and a char take 12 bytes, not 6: this calculator shows each member's offset, the padding before it and the trailing padding, with the same rules your compiler follows. Free in your browser.
- Free
- Runs in your browser
- No sign-up
- Your data stays on your device
Run — free
C and C++ compilers place each struct member at a byte offset that respects the member’s natural alignment, then pad the overall type so arrays of the struct stay aligned. That layout is exactly what the offsetof macro and sizeof operator report, yet hand-counting padding across ABIs and #pragma pack still produces silent bugs in binary protocols, FFI bindings, and memory dumps. This struct field offset calculator accepts an ordered list of fields with known C scalar types or explicit size and alignment, applies the same align-up rules a compiler uses, and returns every field’s offset, padding before the field, the struct’s total size, overall alignment, and trailing padding. Optional pack values model packed structs; optional abi selects LP64, LLP64, or ILP32 for long and pointer widths.
How the compiler lays out each member
For a sequence of members in declaration order the compiler keeps a running byte cursor that starts at zero. Before placing a field it rounds the cursor up to a multiple of that field’s effective alignment, records that address as the field offset, then advances the cursor by the field’s size. Any bytes skipped in the round-up become padding before the member and are never part of the field’s own storage.
After the last member the compiler may insert trailing padding so the total size is a multiple of the structure’s alignment, which is defined as the maximum effective alignment among all members. That final size is what sizeof returns, and each recorded address is what offsetof returns for the corresponding member name.
Natural alignment for scalars follows the platform ABI: on LP64 systems char is one byte aligned to one, short two, int four, long and pointer eight, float four, double eight; LLP64 keeps long at four while pointers stay eight, and ILP32 uses four for both long and pointers. This calculator encodes those tables so you can switch ABI without rewriting field lists.
Understanding that jump is the whole point of an offsetof tool: memory dumps, wire formats, and generated bindings must agree with the compiler, not with a casual mental sum of sizes alone.
What you can do with it
Binary protocol and dump parsing
Confirm each field’s byte offset and total sizeof before writing a wire decoder or interpreting a memory dump against a C struct.
FFI and multi-ABI bindings
Generate or verify LP64, LLP64, and ILP32 layouts for the same member list so native bindings match each target data model.
FAQ
How do I compute C struct field offsets?
Send fields as an ordered array of {name, type} (or {name, size, alignment}).
Does this match the offsetof macro?
Yes for scalar members under the chosen ABI and optional pack value. Offsets and sizeof follow the same placement rules compilers use for natural or packed structs of scalars.
What does pack do?
pack models #pragma pack(n). Omit pack for natural (unpacked) layout.
Which ABIs are supported?
lp64 (default: long and pointer 8 bytes), llp64 (Windows 64-bit: long 4, pointer 8), and ilp32 (long and pointer 4).
Why is sizeof my struct bigger than the sum of its members?
Because the compiler adds padding. With a char, an int and a char, three bytes go before the int so it starts at offset 4, and three more go at the end so the total of 12 is a multiple of the struct's alignment of 4.
Related tools
For developers — API access
Everything on this page is available programmatically. This section is for teams who want to wire it into their own systems; everyone else can just use the tool above.
API endpoint
Prefer to automate it? One authenticated POST creates the task; the result comes back by webhook or a signed link. The same capability also runs here on the web, by email and from Telegram — and soon from our app too.
Call it from your stack
curl -X POST https://api.kit.forhosting.com/dev/struct-field-offset \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"fields":[{"name":"a","type":"char"},{"name":"b","type":"int"},{"name":"c","type":"char"}]}'const res = await fetch("https://api.kit.forhosting.com/dev/struct-field-offset", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"fields": [
{
"name": "a",
"type": "char"
},
{
"name": "b",
"type": "int"
},
{
"name": "c",
"type": "char"
}
]
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/dev/struct-field-offset",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"fields": [
{
"name": "a",
"type": "char"
},
{
"name": "b",
"type": "int"
},
{
"name": "c",
"type": "char"
}
]
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/dev/struct-field-offset", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"fields":[{"name":"a","type":"char"},{"name":"b","type":"int"},{"name":"c","type":"char"}]}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"fields":[{"name":"a","type":"char"},{"name":"b","type":"int"},{"name":"c","type":"char"}]}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/dev/struct-field-offset", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Example request
{
"fields": [
{
"name": "a",
"type": "char"
},
{
"name": "b",
"type": "int"
},
{
"name": "c",
"type": "char"
}
]
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "dev.struct_field_offset",
"status": "queued",
"_links": {
"result": "/tasks/tsk_…/result"
}
}The API is asynchronous: the call returns a task_id immediately and the result arrives by webhook. Polling is capped at 1 req/s per task.
Pricing
Published price — no tokens, no invented credits. A failed task is never charged.
Errors
| HTTP | Code | Meaning |
|---|---|---|
401 | unauthorized | Missing or invalid API key. |
402 | insufficient_balance | Your balance doesn't cover the task price. |
404 | unknown_type | That task type doesn't exist. |
429 | rate_limited | Too many requests. Use the webhook instead of polling. |
Technical notes
The free browser path and the prepaid API share one pure analytic module, so homework keys and production oracles never disagree. Failed validation raises invalid_input and is not billed.
How C places fields: alignment, padding, and sizeof
When two adjacent fields need no padding the padding_before value is zero; when a char is followed by an int under natural layout three bytes of padding appear before the int and the offset jumps from one to four.
Input contract, packing, ABI, response shape, and errors
Optional pack may be one, two, four, or eight to model #pragma pack(n): each field’s effective alignment becomes the minimum of its natural alignment and pack. Omit pack for natural unpacked layout. Optional abi defaults to lp64 and may be llp64 or ilp32. Custom opaque members supply positive integer size and a power-of-two alignment without relying on the type table. POST a JSON body with fields as a non-empty array of objects, each with a non-empty name and either a type string or both size and alignment in bytes. Known types include char, short, int, long, long long, float, double, bool, the fixed-width intN_t and uintN_t family, and pointer or size_t for address-sized integers. Aliases members and layout are accepted for the array. Empty arrays, unknown type names, non-power-of-two alignments, missing names, and pack values outside the enum fail with invalid_input. On success the body includes abi; optional pack when packing is active; field_count; fields with index, name, type, size, natural_alignment, effective_alignment, offset, and padding_before; struct_size; struct_alignment; trailing_padding; total_padding; used_bytes (sum of member sizes only); method c_struct_offsetof; and a short formula string. Example: char a, int b, char c under natural LP64 yields offsets zero, four, and eight with struct_size twelve and three bytes of internal padding before b plus three trailing bytes so size is a multiple of four. Pair the offsets with your own dump or code generator; this endpoint does not emit C source, only the layout numbers offsetof and sizeof would imply.
Where a struct offset API earns its keep, pricing, and limits
Systems programmers and reverse engineers use a struct field offset calculator to verify that a hand-written layout matches the compiler before shipping a parser for a binary protocol or a kernel structure dump. Failed validation does not consume balance. Keep field lists within the published maximum, prefer named types for portability, and pin offsets in tests. FFI and binding generators need deterministic offsetof and sizeof numbers for LP64 versus Windows LLP64 so the same schema description can target Linux, macOS, and Win64 without recompiling a probe program for every host. Teaching labs on alignment and packing assign students a field list and pack setting; a pure API oracle marks wrong the common mistake of summing sizes without padding. Continuous-integration suites pin struct_size and each offset so a schema change that inserts a field or changes ABI flags fails tests immediately.
Integration use cases
Teaching alignment and packing
Let students check natural versus #pragma pack layouts and see padding_before and trailing_padding without compiling probe programs.
Technical questions
What does the struct field offset calculator cost?
The browser tool is free. API requests use prepaid KIT balance at $0.002 per successful call; invalid input is not billed.