C Structures: Declaration, Initialization and Union Comparison with Worked Examples
Learn what a structure declaration creates, how each initializer form behaves, and why a union makes a different storage promise. Worked traces cover pointers, copying, padding and tagged unions.
KnowledgeGate Team
Exam prep & CS education

A structure definition creates a type, while an object declaration allocates an object. Positional, designated and partial initializers set members by different rules. A union reuses one storage region instead of retaining every member simultaneously, so member access, copying, layout and active-member tracking must be reasoned about separately.
C guarantees the order of ordinary structure members and shared storage for union members, but not exact offsets, padding, alignment or sizeof values across all implementations. Every byte count below is illustrative and applies only under the assumptions stated with it.
C structure declaration: define the record before creating objects
struct Student {
int roll;
char grade;
float marks;
};This introduces the tag Student and a structure type with three ordered members, but no object. The separate declaration struct Student first, second; creates and allocates two objects.
In first.marks, Student is the tag used with struct, first is the object, and marks is a member. typedef struct Student Student; creates a type alias, so Student third; declares an object. The alias is not another object or layout.
For wider programming study, use Coding & DSA Courses for Placements.
C structure initialization: positional, designated and partial forms
These three valid forms reach their final values in different ways:
form | declaration | final member values |
|---|---|---|
positional |
|
|
designated |
|
|
partial |
|
|
A positional initializer follows declaration order, so it becomes fragile if members are reordered. A designated initializer names each destination and, in C99 and later, may use a different order.
The partial form is initialization. Omitted members are zero-initialized, giving the null character and floating zero shown above. This does not apply to struct Student local; without an initializer at block scope. Its automatic object's members are indeterminate until assigned.
Structure member access: a complete . and -> trace
Now trace an array and a pointer:
struct Student batch[2] = {
{17, 'A', 82.5f},
{23, 'B', 91.0f}
};
struct Student *p = &batch[1];
p->marks += 1.5f;
float total = batch[0].marks + p->marks;
float average = total / 2.0f;The pointer targets batch[1], where marks starts at 91.0. The update gives p->marks = 91.0 + 1.5 = 92.5. Then total = 82.5 + 92.5 = 175.0 and average = 175.0 / 2.0 = 87.5.
Use . with a structure object and -> with a pointer to a structure. The expression p->marks is equivalent to (*p).marks. The parentheses matter because . binds more tightly than unary *.
![Two batch structure records with pointer p aimed at batch[1], and the average of the two marks computed as 87.5.](https://cdn.knowledgegate.ai/blog-assets/blog_asset_1784247624997_iaec85.jpg)
Structure copying: member values versus pointer addresses
Whole-structure copying copies each member value:
struct Student copy = batch[1];
copy.roll = 99;Afterward, copy contains (99, 'B', 92.5), while batch[1] remains (23, 'B', 92.5). Scalar members were copied by value.
If a structure contains a pointer, copying it copies the address, not the pointed-to allocation. This is not a deep copy. C also forbids == or != between complete structure objects.
Structure versus union: simultaneous members versus shared storage
The earlier Storage Classes, Structures and Enums in C: Worked Examples for GATE Output and sizeof Questions connects storage classes, enum rules and padding. By contrast, struct Sample retains code, count and mean together; union SampleValue retains only the last-written member, and the tagged-union example then adds a discriminator.
Compare these definitions:
struct Sample {
char code;
int count;
float mean;
};
union SampleValue {
char code;
int count;
float mean;
};struct Sample s = {'K', 25, 6.5f}; retains all three values. For union SampleValue u, after u.count = 25; u.mean = 6.5f;, mean is active and count is not separately retained.
Structure members occupy distinct regions in declaration order, possibly with internal or trailing padding. Union members overlap from the same address. A union is large and aligned enough for every member, possibly with trailing padding. Reading a member other than the last stored is non-portable representation-level behaviour, not safe numeric conversion.
Assume sizeof(char)=1, alignof(char)=1, sizeof(int)=alignof(int)=4, and sizeof(float)=alignof(float)=4. For struct Sample, code is at byte 0, bytes 1 to 3 are padding, count is at bytes 4 to 7, and mean is at bytes 8 to 11, totalling 12 bytes. Every union member starts at byte 0, so its illustrative size is 4 bytes. Another implementation may differ.

Tagged union: pair the shared bytes with an explicit kind
A tagged union records which payload the program should read:
enum ValueKind { VALUE_INT, VALUE_FLOAT };
struct Value {
enum ValueKind kind;
union {
int i;
float f;
} data;
};
struct Value v = {.kind = VALUE_FLOAT, .data.f = 6.5f};
if (v.kind == VALUE_FLOAT) {
float current = v.data.f;
}The discriminator is VALUE_FLOAT, so the matching read is v.data.f. After v.data.i = 25; v.kind = VALUE_INT;, the logical state is kind=VALUE_INT with data.i=25; code should no longer read data.f.
The outer structure keeps the discriminator beside the union, which saves storage for mutually exclusive payloads. A union does not convert an integer to a float. Portable code tracks the active member instead of guessing from overlaid representations.
C structures and unions in exam questions: patterns, traps and solving order
Common practice questions ask whether a declaration defines a type or creates an object, find final initialized values, choose . or ->, calculate layout from stated assumptions, or trace the active union member.
Use this four-step order:
Mark the declared member order.
Write the concrete value of each object member.
Annotate storage duration, or mark the last union write.
Only then compute the output or layout.
Under our assumptions, this gives sizeof(struct Sample) = 12 bytes and sizeof(union SampleValue) = 4 bytes. These are not universal constants.
Repair these common traps:
Do not add member sizes without checking alignment and padding.
Do not assume an uninitialized automatic object starts at zero.
Do not keep a positional initializer unchanged after reordering members.
Do not write
p.markswhenpis a pointer.Do not compare complete structures with
==.Do not treat
memcmpas value comparison because padding bytes may differ.Do not read an inactive union member as if it were a numeric cast.
C structures and unions: the short version and next step
A structure definition creates a type template.
An object declaration allocates an object.
Designated initializers reduce mistakes caused by member order.
.accesses an object, while->accesses through a pointer.A structure keeps every member simultaneously.
A union shares storage and requires active-member discipline.
In the pointer trace, p->marks=92.5, total=175.0 and average=87.5. Under the stated layout assumptions, the structure is 12 bytes and the union is 4 bytes.
If you want concept teaching followed by practice, continue with the C Language Course: Concepts, MCQs & Coding. If you want a complete C programming route, use the C Programming Course.
Keep learning

Functions in C: Syntax, Parameters and Worked Examples
Learn how C declarations, calls, and definitions fit together. Compile two complete programs, follow their values, and trace composition and recursion.

Smart Pointers in C++: unique_ptr, shared_ptr and weak_ptr with Worked Examples
Learn who owns a C++ object, when it is destroyed, and how to choose among unique_ptr, shared_ptr, weak_ptr and a borrowed raw pointer.

Pointers in C: Addresses, Arrays and Functions with Worked Examples
Build a practical pointer model with runnable C programs for dereferencing, arrays, swapping, dynamic memory and common tracing questions.

Pointers and Arrays in C: Decay, Indexing and Worked Examples
Separate arrays from pointers with runnable traces covering indexing, one-past boundaries, function parameters, two-dimensional arrays and declaration traps.