sizeof(*p) on a pointer-to-array answers the element size
Measured
gcc 13 as the oracle, pxx at 3a53468cb267 and at the pinned binary —
identical, so this predates the Track A indexing fix that turned it up.
int a[4]; int (*p)[4] = &a;
int m[3][4]; int (*r)[4] = m;
printf("%zu %zu\n", sizeof(*p), sizeof(*r));
| gcc | pxx | |
|---|---|---|
sizeof(*p) |
16 | 4 |
sizeof(*r) |
16 | 4 |
Every other row of the same program agrees with gcc: (*p)[1], p[0][1],
(*pc)[1] on a char (*)[4], (*psp)[1] on a char *(*)[3], (*prr)[1].a
on a struct R (*)[3], and the write (*p)[2] = 99. Only sizeof is wrong,
which is the tell that the shape is recognised and one reader is not.
Why it is probably small
The metadata is present and the Pascal side already reads it correctly:
SizeOf(p^) over ^array[0..3] of Integer answers 16, and over
^array[0..1, 0..2] of Integer answers 24 — both matching FPC. That path goes
through DerefPtrArrayInfo's flatCount (symtab.inc), which is exactly the
product this needs and is already split out for reuse; DerefPtrArraySym beside
it hands back the symbol. So this looks like the C sizeof arm asking
PtrElemTk's size without first asking whether SymPtrElemArrLen > 0.
Do not assume that guess. It is a reading of two functions, not a measurement — the arm has not been found, let alone run.
Why it is filed rather than fixed
Track C's gate is C tests + self-host + cross, and the session that found it holds A/P. It is a one-line-ish read but it is in someone else's lane and their gate is the one that would catch a mistake in it.
2026-08-30, frankA — fixed in df4cf5ba6, before this ticket was pulled
Filed by frank-rust and fixed by frankA within the hour, from the relayed lead rather than from the file — so the two halves were written independently and agree, which is worth stating rather than assuming.
The fix is one multiply in cparser.inc's sizeof(*ident) arm:
if SymPtrElemArrLen[si] > 0 then sz := sz * SymPtrElemArrLen[si]. That column
holds the FLATTENED product for a multi-dim pointee (its own note in defs.inc
says so), so int (*mm)[3][4] went 4 → 48 with no second case.
Both of this ticket's rows verified against gcc, including the
pointer-to-a-ROW spelling: int (*p)[4] = &a and int (*r)[4] = m over
int m[3][4] both answer 16, matching gcc.
Three controls, chosen so an extent multiply applied one level too widely
moves them — a pointer to a SCALAR stays 4, the POINTER itself stays 8, a
plain array stays 16. That is why the guard is SymPtrElemArrLen > 0 and not a
type test.
The existing test sat directly on the gap (frank-rust's catch, and it would
have cost me the whole detour): test/csizeof_deref_ptr_b79.c covers
sizeof(*p) for struct, union and scalar pointees, all green, and has no
array pointee. A file named for exactly this construct, answering a different
question about it. New file test/c_sizeof_deref_ptr_to_array.c, gcc oracle,
wired into test-c; non-vacuous — rows A/B/C/M read 4/8/1/4 on pinned.
NOT fixed, and it is a different defect rather than a narrower one:
sizeof(*s.fp) where fp is an int (*)[4] struct member answers 8, the
pointer size — that arm never fires, so an extent multiply there would change
nothing. (*s.fp)[1] reads and writes correctly, so the field carries enough to
ADDRESS and not enough to SIZE.
[[bug-c-sizeof-of-a-pointer-to-array-struct-field-answers-the-pointer-size]]
Log
- 2026-08-30 — resolved, commit 80560ba52.