Structures
A struct groups related variables of possibly different types under one name — C's primary tool for modeling a real-world 'thing' (a point, a student, a date) as a single unit instead of a scatter of loose variables.
A structure lets you bundle several related variables — even ones of completely different types — under a single name, so a program can pass around, copy, and reason about one complete 'thing' instead of a handful of loosely connected variables that only exist together by convention. This page walks through the whole feature in one pass: why structures exist, how they're declared and initialized, how members are reached through both plain objects and pointers, how structures behave under assignment, comparison, and function calls, how they nest inside each other and combine with arrays and pointers, what sizeof and padding really mean for a struct's memory layout, and the typedef and self-referential patterns that later become the foundation for linked lists and other dynamic structures.
Why Structures Are Needed
Suppose a program needs to track one student's roll number, name, and marks. Nothing stops you from declaring three separate variables for that, but the moment you need more than one student, the separate-variables approach falls apart — you end up with a pile of numbered variables that C treats as completely unrelated to each other.
Nothing in the language links roll1 to name1 to marks1 — they just happen to share a naming convention that a human reader has to track manually. Passing 'one student' to a function means passing three separate arguments, and there is no way to build an array of 'students' out of these loose variables.
A structure fixes this by letting related variables of different types travel together as one logical object.
struct Student is now a single, reusable type. One struct Student value represents one complete student record, and any number of them can be created, copied, passed to functions, or collected into an array as a unit.
int roll_no;
char name[50];
float marks;
// For several students, this becomes unmanageable:
int roll1, roll2, roll3;
char name1[50], name2[50], name3[50];
float marks1, marks2, marks3;struct Student
{
int roll_no;
char name[50];
float marks;
};Structure Syntax
A structure is defined with the struct keyword, a tag name, and a brace-enclosed list of members, each with its own type.
struct is the keyword that introduces the definition. Student is the structure's tag — its name. roll_no, name, and marks are its members. The semicolon after the closing brace is part of the syntax and is easy to forget, but it is required — this is a declaration statement, not a block.
struct StructureName
{
data_type member1;
data_type member2;
data_type member3;
};struct Student
{
int roll_no;
char name[50];
float marks;
};Structure Declaration vs Structure Variable
Writing the struct Student { ... }; block only defines the shape of the type — it does not, by itself, create any object in memory. A structure variable (also called a structure object) is created separately, the same way you'd declare a variable of any other type.
Think of struct Student as a blueprint and s1, s2 as two separate buildings constructed from it. Changing s1.marks has no effect on s2.marks — each object owns its own storage for every member.
Common mistake: forgetting the struct keyword when declaring a variable. In plain C (without a typedef), the type name is struct Student, not Student — so struct Student s1; is required. Writing Student s1; is only legal after a typedef has created Student as an alias for the type (covered later in 'typedef With Structures').
struct Student
{
int roll_no;
char name[50];
float marks;
};
// This defines the TYPE. No student object exists yet.
struct Student s1;
struct Student s2;
// Now s1 and s2 are actual structure objects, each with its own
// independent roll_no, name, and marks.Accessing Members With the Dot Operator
Once you have a structure object, the dot operator (.) reaches into it to read or write one specific member. The general pattern is structure_object.member.
s1.roll_no and s1.marks are ordinary scalar members, so they can be assigned to directly with =. name is a char array member, so it is filled with strcpy rather than assigned to directly, exactly as with any other character array.
s1 isn't three separate variables — it's one object whose members sit at fixed offsets from s1's own starting address. s1.roll_no, s1.name, and s1.marks are just names for three regions inside that single block of memory.
s1.roll_no = 101;
s1.marks = 85.5;
strcpy(s1.name, "Rina");
printf("%d\n", s1.roll_no);
printf("%.2f\n", s1.marks);
printf("%s\n", s1.name);Structure Initialization
A structure object can be given initial values at the point of declaration, using a brace-enclosed list. Without designators, the values are matched to members strictly in declaration order.
The first value initializes the first member, the second value initializes the second member, and so on — the correspondence is purely positional, so reordering the members in the struct definition would silently change what each initializer value means.
struct Student s1 = {101, "Rina", 85.5};
// roll_no -> 101
// name -> "Rina"
// marks -> 85.5Designated Initializers
Instead of relying on position, C lets you name exactly which member each value initializes, using a .member = value designator inside the initializer list.
This initializes the exact same object as the positional form, but is far more resistant to mistakes: the reader (and the compiler) can see precisely which member each value belongs to, and reordering the initializers — or reordering the struct's own members later — doesn't change the meaning.
struct Student s1 = {
.roll_no = 101,
.name = "Rina",
.marks = 85.5
};Partial Initialization
A brace initializer does not have to supply a value for every member. Any member left out is initialized to its zero value — 0 for integers, 0.0 for floating-point types, and an empty string (all zero bytes) for a char array.
Only roll_no receives an explicit value here. Because the initializer is a brace list — even a short one — the C standard guarantees every member it doesn't mention is zero-initialized, regardless of whether s1 is a local, global, or static object.
struct Student s1 = {101};
// roll_no -> 101
// name -> "" (all zero bytes)
// marks -> 0.0Modifying Structure Members
Members of an already-existing structure object can be changed individually at any time, exactly like any other variable — the type of the structure never changes, only the values stored in its members.
s1 is still the same struct Student object it always was; only the values sitting inside two of its members have been overwritten.
s1.roll_no = 200;
s1.marks = 91.5;Structure Assignment
Unlike arrays, whole structure objects can be assigned directly with =. The assignment copies every member's value, member by member, from the right-hand object into the left-hand one.
After s2 = s1, s2 is a completely independent object holding the same values s1 had at that moment — later changes to s1 do not retroactively affect s2, because the assignment copied values, not a link between the two objects.
This is one of the clearest differences between arrays and structures in C: an array variable is not assignable as a whole (you'd need a loop or memcpy), but a structure variable is, because the language defines structure assignment as copying the entire object's value in one step.
struct Student s1 = {101, "Rina", 85.5};
struct Student s2;
s2 = s1;
// s2.roll_no -> 101
// s2.name -> "Rina"
// s2.marks -> 85.5int a[3];
int b[3];
b = a; // invalid -- arrays cannot be assigned
struct Student s1, s2;
s2 = s1; // valid -- structures can be assignedComparing Structures
C does not provide a built-in == operator that compares two entire structure objects for equality. Structures are aggregates, and the language simply does not define what '==' would mean for one — it's not automatically member-by-member, and it doesn't account for members like char arrays that need strcmp rather than a numeric comparison.
Common mistake: writing s1 == s2 to compare two structure variables. This does not compile, because C has no general equality operator for struct types. Compare the specific members you care about instead, and for floating-point members produced by calculations, consider whether an exact == is even the right check (small rounding differences can make two 'equal' values compare unequal).
Each member is compared with whatever comparison is correct for its own type: == for the numeric members, strcmp for the character array, since two char arrays holding the 'same' text are not the same array and cannot be compared with ==.
if (s1 == s2) // invalid -- will not compile
{
}if (s1.roll_no == s2.roll_no &&
strcmp(s1.name, s2.name) == 0 &&
s1.marks == s2.marks)
{
printf("Same\n");
}Passing Structures to Functions
A structure can be passed as a function argument just like any other value. When passed by value, the function receives a brand-new, independent copy of the entire structure.
Calling display(s1) copies every member of s1 into the parameter s. Because s is its own object, any changes made to s inside display would only affect that local copy — s1 back in the caller is completely untouched.
void display(struct Student s)
{
printf("Roll: %d\n", s.roll_no);
printf("Name: %s\n", s.name);
printf("Marks: %.2f\n", s.marks);
}
// Call:
display(s1);Returning a Structure From a Function
A function can also return a structure by value. The returned structure's members are copied out into whatever object receives the result.
Even though the local variable s inside createStudent stops existing once the function returns, its value has already been copied out into student in main by the time control comes back — so this is entirely safe, unlike returning a pointer to a local variable.
struct Student createStudent(void)
{
struct Student s = {101, "Rina", 90.0};
return s;
}
int main(void)
{
struct Student student;
student = createStudent();
return 0;
}Pointer to a Structure
A pointer can hold the address of a structure object, just as it can hold the address of any other variable. Once you have such a pointer, its members can still be reached, though the syntax needs a bit of care.
ptr holds the address of s1. To reach a member through ptr, you first have to dereference the pointer to get the object itself, and then apply the dot operator — hence (*ptr).roll_no. The parentheses are mandatory: because . binds tighter than unary *, writing *ptr.roll_no without them would be parsed as *(ptr.roll_no), which doesn't even compile (ptr is a pointer, not a structure, so it has no .roll_no member).
Common mistake: applying the dot operator directly to a structure pointer, e.g. p.roll_no when p is declared as struct Student *p;. The dot operator only works on an actual structure object, not on a pointer to one — you must either dereference first, (*p).roll_no, or use the arrow operator, p->roll_no.
struct Student s1;
struct Student *ptr = &s1;
(*ptr).roll_no = 101;The Arrow Operator
Because (*ptr).member is common but a little awkward to write, C provides the arrow operator (->) as a direct shorthand for exactly that combination.
-> combines the dereference and the member access into a single operator, and needs no extra parentheses. The pattern to remember is: object.member for a plain structure object, pointer->member for a pointer to one.
{101, "Rina", 85.5} ptr is its own variable, at its own address, whose stored value is s1's address. ptr->roll_no follows that stored address back to s1 and then reads roll_no from it — it never touches or copies s1 itself.
Common mistake: mixing up . and ->. The rule is fixed and never changes with context: object.member is for a structure object you hold directly; pointer->member is for a pointer to a structure. Using the wrong one is a compile error, not a subtle bug, which is at least easy to catch.
ptr->roll_no = 101;
ptr->marks = 90.5;
// Exactly equivalent to:
(*ptr).roll_no = 101;
(*ptr).marks = 90.5;Why the Arrow Operator Matters
Passing a pointer to a structure, instead of the structure itself, lets a function reach back and modify the caller's original object — the same principle that makes pointer parameters useful for ordinary variables.
updateMarks receives the address of s1, not a copy of it. So s->marks = 95.0; reaches through that address and changes s1.marks directly in the caller. This is also far cheaper for large structures, since only an address is copied into the function rather than the whole object.
void updateMarks(struct Student *s)
{
s->marks = 95.0;
}
// Call:
updateMarks(&s1);Nested Structures
A structure member does not have to be a simple scalar type — it can be another structure. This lets you build up more complex records out of smaller, reusable pieces.
Every struct Student object now contains a complete struct Date object as one of its members — dob isn't a pointer to a separate Date somewhere else, it's an entire Date embedded directly inside the Student.
Reaching a nested member just chains the access operators: s1.dob gets you the embedded Date object, and .day then reaches inside that. When starting from a pointer, only the first step changes to ->; dob.day still uses . because dob itself is a plain object, not a pointer.
dob is not stored somewhere else with a link back to s1 — its three int members sit inline, right inside s1, starting at the offset where dob begins. Nesting a structure means embedding it, not pointing to it.
struct Date
{
int day;
int month;
int year;
};
struct Student
{
int roll_no;
char name[50];
struct Date dob;
};s1.dob.day = 15;
s1.dob.month = 8;
s1.dob.year = 2000;
// Through a pointer:
ptr->dob.day = 15;Array of Structures
One of the most useful things you can do with a structure type is build an array of them — a natural way to represent a whole collection of records of the same kind.
students is now an array of three complete struct Student objects, each with its own independent roll_no, name, and marks — not three pointers to students, three actual student records sitting back to back in memory.
Indexing the array first selects one student object, and the dot operator then reaches into that specific object — exactly the same member-access syntax used for a single structure variable.
Three complete Student objects are laid out contiguously in memory. students[1] begins exactly sizeof(struct Student) bytes after students[0] begins, which is what makes students[i] and pointer arithmetic on the array work correctly.
struct Student
{
int roll_no;
char name[50];
float marks;
};
struct Student students[3];students[0].roll_no = 101;
students[1].roll_no = 102;
students[2].roll_no = 103;
for (int i = 0; i < 3; i++)
{
printf("%d\n", students[i].roll_no);
}Pointer to an Array of Structures
Because an array of structures decays to a pointer to its first element, a plain struct Student * can be used to walk through it, the same way an int * can walk through an array of ints.
ptr + 1 does not advance by one byte — pointer arithmetic on a struct Student * advances by sizeof(struct Student) bytes, landing exactly on the next Student object in the array. That's what lets ptr[i] and (ptr + i)->member behave identically to students[i].member.
struct Student *ptr = students;
ptr[0].roll_no = 101;
ptr[1].roll_no = 102;
// Equivalently, using pointer arithmetic:
(ptr + 1)->roll_no = 102;Structures Containing Arrays
A structure member can itself be an array — the name member you've already seen (char name[50]) is one example, and any array type can be used the same way.
marks is a genuine 5-element int array embedded inside s1 — there are 5 int-sized slots reserved right inside the structure for it. s1.marks[0] first selects the marks member and then indexes into it, exactly like indexing any other array.
struct Student
{
int roll_no;
char name[50];
int marks[5];
};
struct Student s1;
s1.marks[0] = 80;
s1.marks[1] = 90;Structures Containing Pointers
A structure member can also be a pointer. This looks superficially similar to an array member, but the two are fundamentally different: an array member stores the data itself inside the structure, while a pointer member stores only an address pointing somewhere else.
e.name does not hold the characters 'R', 'i', 'n', 'a' — it holds the address of the separate name array, wherever that array happens to live. e itself only stores an id and one address.
e1 and e2 are two separate struct Employee objects, but both of their name members happen to hold the identical address — both point at the same characters. Copying a structure with a pointer member copies the address, never the data it addresses.
This distinction — a structure storing a pointer value rather than the pointed-to data — becomes critical the moment you copy such a structure, since only the address gets duplicated, not what it points to (covered in full in 'Copying Structures With memcpy' below). It also raises questions of object lifetime and ownership: if the buffer that name points to ever goes away, every structure still holding that address is left with a dangling pointer.
struct Employee
{
int id;
char *name;
};
char name[] = "Rina";
struct Employee e = {
.id = 101,
.name = name
};Structure Size and sizeof
The size of a structure type or object can be found with sizeof, exactly as with any other type.
sizeof(struct Student) asks for the size of the type itself; sizeof(s1) asks for the size of a particular object, but for an ordinary (non-array) structure variable the two report the same number of bytes.
Common mistake: assuming sizeof(struct X) always equals the sum of sizeof for each individual member. It usually does not — the compiler is free to insert padding bytes between members (and after the last one) to satisfy alignment requirements, so the true size can only be found with sizeof itself, never by adding up the member sizes by hand.
printf("%zu\n", sizeof(struct Student));
printf("%zu\n", sizeof(s1));struct Example
{
char a;
int b;
char c;
};
// You might expect:
// sizeof(char) + sizeof(int) + sizeof(char)
// but sizeof(struct Example) is typically larger than that.Padding and Alignment
Structure members are stored as part of the structure object, but each member sits at an implementation-defined offset chosen so that it satisfies the platform's alignment requirements — for example, a 4-byte int typically has to start at an address that's a multiple of 4. To make that possible, the compiler may insert unused padding bytes between members, or after the last member, that don't correspond to any member at all.
The members still appear in declaration order, but the exact byte layout -- including how much padding, if any, sits between or after them -- depends entirely on the C implementation and its alignment rules, not on anything the source code states directly.
This is exactly why sizeof(struct) cannot be assumed to equal the sum of sizeof for every member: sizeof(struct) = sum of sizeof(all members) is not a rule of the language, and relying on it will eventually be wrong on some compiler or platform.
struct Student
+---------------------------+
| roll_no |
+---------------------------+
| name[] |
+---------------------------+
| marks |
+---------------------------+
(possible padding bytes are not shown, and are not part
of any member -- their presence depends on the compiler,
the platform, and the declared order of the members)typedef With Structures
Writing struct Student every time you need the type name is correct but repetitive. typedef lets you introduce a shorter alias for a structure type.
This defines an anonymous structure (no tag) and immediately gives it the alias Student via typedef. From this point on, Student is usable anywhere a type name is expected, with no struct keyword needed.
Here the structure keeps its own tag (Student) in addition to gaining the typedef alias (also Student, by convention, though it doesn't have to be) -- so both struct Student and plain Student refer to the identical type. typedef never creates a new kind of structure or a new object; it only creates an alternate name for an existing type.
typedef struct
{
int roll_no;
float marks;
} Student;
Student s1;
Student s2;typedef struct Student
{
int roll_no;
float marks;
} Student;
// Both of these now name the same type:
struct Student s1;
Student s2;Self-Referential Structures
A structure can contain a pointer to another object of its own type. This single idea is the foundation that linked lists, trees, and graphs are all built on.
next doesn't hold another Node directly -- it holds the address of one, which may or may not exist yet. This is what lets one Node be chained to another, and that one to another, forming a list of arbitrary length decided entirely at run time.
node1.next stores node2's address, which is the entire mechanism by which one Node is linked to the next. Following .next repeatedly is how a program walks the whole chain.
A struct Node containing a direct struct Node member would need to contain itself, which would need to contain itself again, forever -- the compiler cannot compute a finite size for such a type and rejects it. A pointer member has a fixed, known size regardless of what it points to, which is exactly why it works.
struct Node
{
int data;
struct Node *next;
};struct Node
{
int data;
struct Node next; // invalid: infinite size
};
// A pointer to the structure is fine instead:
struct Node
{
int data;
struct Node *next; // valid
};Anonymous Structures
A structure can also be defined without a tag at all, when a variable is declared in the same statement and the type doesn't need to be referred to again elsewhere.
There is no name after the struct keyword, so this type has no tag and cannot be used to declare any other variable later -- point is the only object of this particular anonymous type that will ever exist, which is exactly the situation this feature is meant for.
struct
{
int x;
int y;
} point;
point.x = 10;
point.y = 20;Anonymous Structure Members
Modern C standards also allow an unnamed structure (or union) to appear as a member inside another structure. When it has no member name of its own, its own members are promoted and can be accessed directly through the enclosing object.
The inner struct { int x; int y; }; has no member name following its closing brace, which makes it an anonymous member. Its x and y fields behave as if they belonged directly to object, so object.x reaches them without an intermediate member name to go through.
This is a more advanced feature than the rest of this page, and support for it can depend on the compiler and the language standard in use -- treat it as something to recognize when reading other people's code more than something to reach for by default.
struct
{
int id;
struct
{
int x;
int y;
};
} object;
object.id = 1;
object.x = 10;
object.y = 20;const Structures
A structure object can be declared const, in which case none of its members can be modified through that object after initialization.
A pointer can also point to a const structure, which is exactly what a read-only function parameter should use: const struct Student *s promises the caller that display will only read through s, never modify the structure it points to -- the compiler enforces that promise at compile time.
const struct Student s1 = {
.roll_no = 101,
.marks = 90.0
};
s1.marks = 95.0; // invalid -- s1 is constconst struct Student *ptr = &s1;
void display(const struct Student *s)
{
printf("%d\n", s->roll_no);
}Copying Structures With memcpy
Structures can sometimes be copied with memcpy instead of ordinary assignment, though for a simple structure-to-structure copy, assignment is usually clearer and just as correct.
Both lines copy every byte of s1's value into s2. memcpy is more commonly reached for when copying a raw block of memory generically (for example, inside library code that doesn't know the exact type), while plain assignment is the natural choice when you already have two named structure variables of the same type.
Neither approach performs anything more than a byte-for-byte copy of the structure's own storage. If a structure contains a pointer member, copying the structure -- by assignment or by memcpy -- copies the pointer's value (an address), not the data that address points to.
After e2 = e1, e1 and e2 are two independent Employee objects, but their name members are two separate pointer variables that happen to hold the identical address -- both still point at the one and only copy of the characters. This is called a shallow copy.
Common mistake: assuming that structure assignment (or memcpy) performs a deep copy. s2 = s1; copies s1's values into s2, and if those values include pointers, only the pointer values are duplicated -- the objects they point to are not. If e1 and e2 need genuinely independent data, the program must allocate new storage and copy the pointed-to data itself; C never does that automatically.
memcpy(&s2, &s1, sizeof s2);
// Usually clearer for the common case:
s2 = s1;struct Employee
{
int id;
char *name;
};
struct Employee e1, e2;
// ... e1 set up so e1.name points at "Rina" ...
e2 = e1;
// Now both e1.name and e2.name hold the SAME address:
// e1.name ----+
// +----> "Rina"
// e2.name ----+A Complete Worked Example
Putting the core pieces together -- a tag, member declarations, designated initialization, and read-only access through a pointer -- produces a small but complete and realistic program.
s1 is built once, with designated initializers making each field's meaning explicit. displayStudent takes a const pointer, so it can read s1's fields through s->member without paying for a full copy and without any risk of accidentally modifying the caller's object.
struct Student s1 is created and initialized in one step, using designated initializers for clarity.
displayStudent(&s1) passes s1's address, not a copy of the whole structure.
Inside displayStudent, s is a const struct Student *, so s->roll_no, s->name, and s->marks read s1's members through the pointer, and the const qualifier stops the function from modifying them.
Three printf calls print the three fields, after which main returns 0.
#include <stdio.h>
#include <string.h>
struct Student
{
int roll_no;
char name[50];
float marks;
};
void displayStudent(const struct Student *s)
{
printf("Roll No : %d\n", s->roll_no);
printf("Name : %s\n", s->name);
printf("Marks : %.2f\n", s->marks);
}
int main(void)
{
struct Student s1 = {
.roll_no = 101,
.name = "Rina",
.marks = 91.5f
};
displayStudent(&s1);
return 0;
}Structure vs Array
Structures and arrays are both ways of grouping multiple values under one name, but they solve different problems: an array is a sequence of elements that are all the same type, while a structure is a fixed set of members that can each have a different type.
struct Student groups three genuinely different types (an int, a char array, and a float) into one record. int marks[5] instead groups five values of the exact same type into one sequence -- the two constructs are shaped for different jobs, and reaching for the wrong one is what makes a data model awkward.
Feature | Structure | Array
-----------------+--------------------------------+------------------------------
Stores | Related members | Elements of the same type
Member types | Can be different | Must be the same element type
Access | By member name | By numeric index
Example | student.marks | marks[2]
Assignment | Structure assignment allowed | Array assignment not allowed
Main purpose | Model a record / object | Store a sequence / collectionstruct Student
{
int roll;
char name[50];
float marks;
};
// vs.
int marks[5];Structure vs Union
A structure and a union look almost identical in their declaration syntax, but they allocate memory in opposite ways: a structure gives every member its own separate storage, while a union makes all of its members share the exact same storage.
sizeof(struct Data) is at least large enough to hold i, f, and c all at once, because all three exist simultaneously, each in its own region of the structure.
sizeof(union Data) only needs to be as large as its biggest member, because i, f, and c all occupy the same bytes -- writing through one member and then reading through another reinterprets the same stored bits rather than reading independent values. Structures and unions are related in syntax but serve different purposes: a structure represents several values that all exist at once, while a union represents one value that can be interpreted as several different types at different times.
What is the real difference between defining struct Student { ... }; and declaring struct Student s1;?
The struct Student { ... }; block only defines the shape of a new type -- it creates no object in memory by itself. struct Student s1; is a separate step that actually creates a structure object (s1) of that type, with its own independent storage for every member.
Why doesn't C allow s1 == s2 for two structure variables, and what should be done instead?
C has no built-in equality operator defined for whole structure types, so s1 == s2 fails to compile. Instead, compare the specific members that matter, using == for numeric members and strcmp for character-array members -- being aware that direct == on floating-point members produced by calculations may not behave as expected.
Why does struct Student s2 = s1; work in C while int b[3]; b = a; does not, and what exactly happens during that structure assignment?
C defines whole-structure assignment as a built-in operation that copies every member's value from the source object into the destination object, but it defines no equivalent whole-array assignment -- an array variable simply is not assignable as a unit. So s2 = s1; copies s1's member values into s2, leaving two independent objects, while b = a; is rejected outright by the compiler.
If a struct Student is passed to a function by value and the function modifies one of its members, why doesn't that change show up in the caller?
Passing by value copies the entire structure's members into the function's own local parameter, which is a separate object from the caller's original. Any modification inside the function changes only that local copy; the caller's structure is never touched. Passing a pointer to the structure instead lets the function reach the original object directly.
Why can sizeof(struct Example) be larger than the sum of sizeof for each of its individual members?
The compiler is allowed to insert padding bytes between members (or after the last one) so that each member starts at an address satisfying the platform's alignment requirements. Those padding bytes take up space but don't belong to any member, so the true structure size -- found only with sizeof -- can exceed the simple sum of the member sizes.
Write a C program that defines a struct Student with roll_no, name, and marks, creates one variable using designated initializers, and prints all three fields.
The struct Student type groups the three related fields together, and the designated initializer sets each named member explicitly at creation time. The dot operator then reads each field back out for printf, exactly as it would for any structure object.
Write a C program that declares an array of 3 struct Student records, fills each one in a loop, and then prints all three records in a second loop.
students is an array of three complete Student objects stored contiguously. The first loop indexes into the array and uses the dot operator to fill each object's members; the second loop indexes again to read them back, showing that each element is an independent record.
Write a C program that passes a struct Student to one function by value (to display it) and to another function by pointer (to modify its marks), then shows that only the pointer version affects the original.
displayByValue receives a full copy of s1, so setting s.marks inside it never reaches s1 in main -- the printed value after the call is unchanged. updateByPointer instead receives s1's address, so s->marks = 95.0f writes directly into s1's own storage, and the change is visible back in main.
Write a C program that defines a function createStudent returning a struct Student by value, and uses it in main to build and print a student.
createStudent builds a local struct Student and returns it by value, which copies its members out before the local variable's lifetime ends. main assigns that returned value into student and then reads its members normally with the dot operator.
Write a C function that compares two struct Student values for equality by comparing their individual members, since the == operator cannot be used on whole structures.
Since C provides no == for whole structures, studentsEqual checks each member with the comparison appropriate to its type -- == for the two numeric members and strcmp for the character array -- and combines the results with && so all three must match for the students to be considered equal.
Write a C program with a nested structure -- a struct Date embedded inside a struct Student -- that sets and prints the student's date of birth.
dob is a complete struct Date embedded directly inside s1, not a pointer to a separate Date object. Reaching its fields chains two dot operators -- s1.dob gets the embedded Date, and .day, .month, .year then reach inside that.
Write a C program that defines a self-referential struct Node (an int data field and a struct Node *next field), manually links three nodes into a list, and prints their data in order.
Each Node's next member holds the address of the following Node, chaining n1 to n2 to n3, with n3.next set to NULL to mark the end. Starting current at &n1 and repeatedly following current->next walks the whole chain until NULL is reached.
Write a C program that demonstrates a shallow copy: a struct Employee with a char *name member, copied with plain assignment, showing that both structures' name pointers hold the same address.
e2 = e1 copies e1's members into e2, including the name pointer's value -- the address of nameBuffer -- rather than copying the characters it points to. Both e1.name and e2.name end up equal, proving they refer to the exact same underlying buffer, which is what a shallow copy means.
Write a C program that copies one struct Student into another using memcpy instead of assignment, then verifies the two structures hold equal values.
memcpy(&s2, &s1, sizeof s2) copies sizeof(struct Student) bytes straight from s1's storage into s2's storage, which produces the same result as s2 = s1 for a structure with no pointer members. The comparison afterward confirms every member now matches between the two objects.
Write a C program that uses typedef to define a Student struct type without repeating the struct keyword, then creates and prints two students with it.
The typedef gives the anonymous structure the alias Student, so Student can be used directly as a type name everywhere afterward -- in variable declarations and in the displayStudent parameter -- with no struct keyword required.
struct Data
{
int i;
float f;
char c;
};
// Conceptually, each member gets its own space:
+-------+
| int |
+-------+
| float |
+-------+
| char |
+-------+union Data
{
int i;
float f;
char c;
};
// Conceptually, all members overlap the same bytes:
+-------------------+
| shared storage |
+-------------------+
^ ^ ^
i f c