Values That Belong Together

A student has a roll number and marks; a point has an x and a y; a date has three parts. Arrays collect many values of one kind; the structure collects different kinds that belong to one thing. It is C's mechanism for defining your own types, the foundation of every record-keeping program, and, later, of every dynamic data structure.

Declaring the Shape, Then the Variables

The struct Point { ... }; declaration defines a type, no memory yet: two int members, x and y, in every value of the type. Point is the tag, and here sits the rule that separates C from its descendants: the type's name is struct Point, both words. Bare Point is not a type in C (C++ made the tag alone work, and that habit leaking back is the classic cross-language error). Variables then declare like any other: struct Point origin, with brace initializers filling members in declaration order, constants in this dialect, and the chapter 7 rule carrying over: too-short lists zero the rest.

The dot operator selects a member: origin.x is an int, usable anywhere an int is, read, assigned, passed, scanned into as &origin.x. It binds tighter than every arithmetic and relational operator, so origin.x + 1 and a.x > b.x need no parentheses whatsoever: the member is selected first, always.

The Shorthand Exam Code Uses

Variables can be declared in the definition itself, in the space between the closing brace and the semicolon:

struct Point {
    int x;
    int y;
} origin = {0, 0}, corner = {40, 25};

One statement that defines the type and two variables of it, initializers included. Old papers lean on this form constantly, so read it fluently: whatever follows the closing brace is a variable list, never a second tag name. Dropping the tag is legal as well, struct { int x; int y; } screen;, and it is the one variant to avoid, because the type then has no name at all: nothing later can declare another variable of it, take it as a parameter, or return it.

One rule no shorthand bends: members cannot be initialized inside the template. struct Point { int x = 0; }; is not C in any dialect, and gcc rejects the line with expected ':', ',', ';', '}' or '__attribute__' before '=' token, the compiler saying it would accept a bit-field width or the end of the member declaration but never a value. The template describes layout; values arrive when a variable is declared.

Structs Assign (Arrays Never Did)

second = first copies every member at once, a genuine whole-value assignment that arrays never had, and the copy is independent: changing second.x leaves first alone. The same courtesy does not extend to ==: comparing structs needs member-by-member tests, because the language defines no struct equality (and memcmp is wrong, padding bytes, coming below, hold indeterminate values).

typedef: Naming the Type Once

Writing struct Point everywhere is honest but heavy. typedef creates a synonym:

typedef struct Point {
    int x;
    int y;
} Point;

After this, Point alone is a complete type name: Point origin = {0, 0};. The pattern reads as "define this struct type and also call it Point", tag and typedef name conventionally matching. Exams ask both spellings; real codebases split on taste; this course uses the plain struct form first and typedef where repetition earns it. What matters is knowing the two-word rule underneath: typedef is sugar over it, not a replacement.

Size and Padding

sizeof(struct Point) is 8 on this platform, two 4-byte ints, but the general rule surprises: a struct's size may exceed the sum of its members. The compiler inserts padding between members so each sits at an address its type prefers (alignment), and a char followed by an int typically costs 8 bytes, not 5. Two consequences worth owning now: never assume a struct's layout byte-for-byte, and always take sizes from sizeof, a rule that becomes load-bearing when structs meet malloc and binary files in later chapters.

Nesting

Members can themselves be structs, and the dots chain:

struct Line {
    struct Point start;
    struct Point end;
};

line.start.x = 0;

Read left to right: from line, take start, take x. Records containing records, a date inside an employee inside a department, model real data directly, and the chained dot is the whole access story.

Key Takeaways

  • struct Tag { members }; defines a type; the type's name is struct Tag, both words, and bare tags are a C++ habit that does not compile here.
  • The dot operator selects members and outranks every arithmetic and relational operator, so a.x > b.x needs no parentheses; brace initializers fill in order and zero the remainder.
  • Variables may be declared with the definition (struct Point { ... } origin, corner;); omitting the tag works but leaves the type unnameable, and members can never be initialized inside the template.
  • Structs assign whole (b = a copies all members, independently), but do not compare: == is undefined for structs, and equality is member-by-member.
  • typedef struct Tag { ... } Name; makes Name a standalone synonym.
  • Padding means a struct's size can exceed its members' sum: take sizes from sizeof, never by adding fields.
  • Nested structs chain dots: line.start.x.