Pascal from the ground up

Records, and arrays of them

Grouping a name and a score into one type, reading the fields with a dot, and keeping a whole list of records in a single array.

Part 8 of 12

Intermediate16 min readBack to Courses

One value with several fields

A record packs related values into a single type. You name it once with type, list its fields, and close it with end; — the same end that closes a block, followed by a semicolon because a type declaration is a statement of its own.

The fields may be of any type, including other records, and each keeps its own name. A record is not a list of loose variables: it is one value that happens to have parts.

The T at the start of TStudent is a convention rather than a rule, but it is the one Pascal programmers use: a glance at the name tells you it is a type.

Reaching a field with a dot

A variable of the record type is declared like any other: var student: TStudent;. Its parts are reached with a dot, and each field behaves exactly like a variable of its own type.

Records copy as a whole. second := first; copies every field, after which the two are separate values with no link between them — changing one leaves the other alone.

An array of records

The two ideas combine naturally: var students: array[1..2] of TStudent; is a row where each slot is a whole record. Reach a field of one slot with both the index and the dot: students[1].score.

A loop over low and high then walks the list, and the body works on one student at a time. That is the shape every small database program in Pascal starts from.

A record type in four lines

program Main;
type
  TPoint = record
    x: integer;
    y: integer;
  end;
var
  p: TPoint;
begin
  p.x := 3;
  p.y := 4;
  writeln(p.x, ',', p.y);
end.

How the fields are laid out

A record is stored as its fields in order, one after another, and the compiler adds padding where a field needs to start at a particular boundary. SizeOf reports the total, padding included, which is why it can be larger than the sum of the field sizes. A nested record is simply one more block inside the outer one.

Two type names with identical fields are still different types. TPoint and TCoord, both holding two integers, cannot be assigned to each other, because Pascal compares the declared type rather than the shape. := works when both sides have exactly the same type name, and it copies every field, including any nested record.

A worked record in memory

Take TPoint with two integer fields. A variable p of that type owns enough storage for both, laid out one after the other. p.x := 3 writes to the first field and p.y := 4 to the second; the dot is resolved at compile time, so neither assignment searches for anything at run time. SizeOf(p) reports the whole block: the two fields plus any padding the machine requires.

Copying with b := a copies that block byte for byte, including a nested record. After the copy the two variables are independent, because the bytes now live in two places. A record passed to a routine without var is copied the same way, which is why a procedure that edits its parameter cannot change the caller.

Why the type name matters

Pascal compares declared types, not shapes. TPoint and TCoord may both hold two integers, but a variable of one cannot be assigned to the other; the compiler sees two names and refuses. That is different from a plain alias, where TScore = integer really is the same type under a second name.

The distinction is worth knowing before it surprises you. If two record types must be interchangeable, declare one as a synonym of the other rather than writing the fields twice. If they are genuinely different ideas that happen to look alike today, keep them apart: the compiler is stopping you from mixing up a point and a coordinate.

Three record mistakes to watch for: writing person.name when person is an array of records and needs an index first; assigning between two record types that look alike but have different names; and forgetting that a record passed to a procedure without var is a copy, so changes inside the procedure are lost.

Try these next

  • Add a score field to TStudent and print it beside the name.
  • Declare a TCoord with the same fields as TPoint and try assigning one to the other.
  • Pass a record to a procedure without var, change a field, and print it in the caller.

Two students and a total

Run it, then add a third student to the array. Change the declaration, fill the new record, and let the loop count it in the total.

Output
Click "Run" to execute your code

A record with one field is not silly: it is how a type gets a name now so that more fields can be added later without changing every caller.

A nested address record

Run it, then change the copy line so `b` starts as a separate empty record and fill each field by hand. Compare the printed lines.

Output
Click "Run" to execute your code

Check what you learned

One question at a time. A wrong answer explains itself — take another go.

Part 1 of 5

How do you read the score of the first student in an array of records?