Structures in C: Syntax, Member Access and Worked Examples

Learn how C structures group related values, then trace object and pointer access through complete, implementation-safe examples.

KnowledgeGate Team

Exam prep & CS education

Updated 10 Oct 20266 min read

Separate variables work for one student or one point, but they become difficult to manage when every record has several related values. A C structure solves this by grouping those values under one user-defined type while each member keeps its own type. C Structures: Declaration, Initialization and Union Comparison with Worked Examples places declarations, initialisation and union comparison side by side. The Student, Result and Rectangle traces build on that foundation by combining member access with function calls, arrays of records, nesting and padding, while Coding & DSA Courses for Placements supplies the wider programming path.

What a C structure is and what its declaration creates

Consider three loose variables:

c
int roll = 17;
char name[20] = "Asha";
float average = 84.0f;

They are related only because the programmer treats them as one record. A structure makes that relationship part of the type:

c
struct Student {
    int roll;
    char name[20];
    float marks[3];
};

Student is the tag, while roll, name and marks are members. The closing semicolon is required. The declaration describes a layout but creates no object. struct Student first; and struct Student second; create two objects, where struct Student is the type name and first and second are variable names.

Complete C structure program: create, print and update a record

This program uses ., ->, an array member and a pointer-to-const parameter together:

c
#include <stdio.h>

struct Student {
    int roll;
    char name[20];
    float marks[3];
};

float average(const struct Student *s);

int main(void) {
    struct Student student = {17, "Asha", {78.0f, 84.0f, 90.0f}};

    printf("Roll: %d\n", student.roll);
    printf("Name: %s\n", student.name);
    printf("Average: %.2f\n", average(&student));

    student.marks[1] = 88.0f;
    printf("Updated average: %.2f\n", average(&student));
    return 0;
}

float average(const struct Student *s) {
    return (s->marks[0] + s->marks[1] + s->marks[2]) / 3.0f;
}

The exact output is:

Code
Roll: 17
Name: Asha
Average: 84.00
Updated average: 85.33

Initially, the marks sum to 78 + 84 + 90 = 252, and 252 / 3 = 84.00. Updating index 1 changes only the second mark, so the new sum is 78 + 88 + 90 = 256. The value 256 / 3 is 85.333..., which %.2f prints as 85.33.

&student passes the object's address. Inside average, s->marks[i] reads a member through that pointer. The const qualifier prevents the function from changing the record through s.

Diagram of struct Student with roll 17, name Asha and marks 78, 84, 90, tracing marks[1] to 88 and the average 256 / 3 = 85.33.

Initialise structure members and use the dot operator safely

Given struct Point { int x; int y; };, struct Point p = {3, 4}; gives p.x = 3 and p.y = 4. The clearer designated form struct Point q = {.y = 8, .x = 6}; still gives q.x = 6 and q.y = 8.

Members omitted from an initializer are zero-initialized. An automatic object declared as struct Point r; has no useful defaults and must not be read first. After p.x = p.x + 5; p.y = -2;, p holds (8, -2). The dot operator selects from an object and can chain, as in box.top_left.x.

The array member name accepts "Asha" during initialization, but later student.name = "Ravi"; is invalid because arrays are not assignable. A bounded update can use snprintf(student.name, sizeof student.name, "%s", "Ravi");.

Pass C structures by value or through a pointer

student.roll uses . because student is an object. ptr->roll uses -> because ptr is a pointer, and it is equivalent to (*ptr).roll. The parentheses are necessary because . binds more tightly than unary *.

With struct Point a = {2, 3}; struct Point b = a; b.x = 9;, same-type assignment copies the record. Afterwards, a remains (2, 3) and b is (9, 3).

A function can deliberately mutate the caller's object:

c
void move(struct Point *p, int dx, int dy) {
    p->x += dx;
    p->y += dy;
}

Calling move(&a, 4, -1) changes a from (2, 3) to (6, 2). Use a value for an independent copy, a const pointer for read-only access without copying, and a non-const pointer to update the caller's record.

Arrays of structures: find the highest score by hand

An array stores values of the same structure type while keeping each record's members together:

c
struct Result { int roll; int score; };
struct Result results[3] = {{21, 72}, {22, 91}, {23, 86}};
int best = 0;

for (int i = 1; i < 3; i++) {
    if (results[i].score > results[best].score)
        best = i;
}

Start at index 0, score 72. At index 1, 91 > 72 is true, so best = 1. At index 2, 86 > 91 is false, so best stays 1. The final members print Topper roll: 22, score: 91.

Diagram of three results records with rolls 21, 22, 23 and the scan moving best from index 0 to index 1, ending at roll 22, score 91.

Nested structures and typedef aliases

Nested structures model records inside records:

c
typedef struct { int x; int y; } Point;
typedef struct { Point top_left; int width; int height; } Rectangle;

Rectangle screen = {{10, 20}, 640, 480};

typedef creates aliases, not objects, and does not change member behaviour. Here, screen.top_left.x is 10, screen.top_left.y is 20, screen.width is 640 and screen.height is 480. The opposite corner is (10 + 640, 20 + 480) = (650, 500).

Self-reference must use a pointer, as in struct Node { int data; struct Node *next; };. A structure cannot contain a full instance of its own type because that would require infinite size, but it can point to another instance.

Structure padding, copying and common traps

For struct Sample { char tag; int count; };, alignment may insert padding. Only sizeof(struct Sample) >= sizeof(char) + sizeof(int) is portable. Measure the target, never assume 5 or 8.

Trap

Correct fix

Forgetting the semicolon after a definition

Add ; after the closing brace.

Using . on a pointer or -> on an object

Match the operator to the expression.

Reading an uninitialized automatic structure

Initialize before reading.

Assigning a string to a character-array member

Use a bounded string operation such as snprintf.

Returning a pointer to a local structure

Return by value or use caller-owned storage.

Assuming fixed padding

Use sizeof on the target.

Containing the same structure type by value

Store a pointer to that type instead.

Same-type assignment such as b = a is valid, but a == b is not. Compare meaningful members individually. Avoid memcmp for semantic equality because padding bytes and member representations can mislead.

How exam questions test C structures

Check yourself against these traced results before running any code:

  1. After struct Point a={2,3}; struct Point b=a; b.y=7;, a.y is 3 and b.y is 7.

  2. If struct Point *p=&a; p->x=5;, then a.x is 5.

  3. For Rectangle screen={{10,20},640,480}, the opposite corner is (650,500).

  4. The three-record score scan ends at roll 22 with score 91.

Questions ask you to distinguish a type from an object, choose . or ->, trace initialization, follow value or pointer updates, index arrays, and reject fixed padding. C Programming for Teaching CS Exams: Key Concepts gives broader context on the same core CS syllabus.

A structure defines one record type. . accesses an object, -> accesses through a pointer, same-type assignment copies, arrays hold records, and sizeof includes implementation-defined padding. Over 30 practice and previous-year questions on this topic are available for revision.

Rewrite the student example with roll 31, name "Kabir" and marks {65.0f, 75.0f, 85.0f}. Predict Average: 75.00, then change the first mark to 80.0f and predict Updated average: 80.00. Continue with the C Language Course: Concepts, MCQs and Coding, or use CS Fundamentals for Placements for placement-focused core CS revision.