I SEMESTER · C PROGRAMMING · UNIT 03
Organize data.
Understand memory.
Lists, matrices and complete records.
Clear definitions. Every concept explained.
Each topic includes its definition, syntax, rules, examples and memory representation. Read in order, use the interactive diagrams, then revise with the written answers.
UNIT 03 · 01
One name. Several related values.
Array. An array is a collection of elements of the same type, stored consecutively in memory and accessed using a common name and an index.
Each element is an individual object. For example, five students’ integer marks can be stored as int marks[5]; instead of using five unrelated variables. In the fixed-size C90 examples in this unit, the declared element count stays the same for that array object.
Element. An element is one value-holding object within an array, such as marks[2].
Index or subscript. An index is an integer used to identify an array element by its position. C array indexing starts at zero.
Characteristics and uses
- One element type: an int array contains int elements. An array of structures contains elements of one structure type, even though each structure can contain different member types.
- Consecutive storage: adjacent elements are separated by exactly the size of one element.
- Indexed access: any valid position can be read or updated without first visiting earlier positions.
- A known extent:
int a[5]has five elements. Standard C90 does not allow a zero-length array or a variable-length array. - Useful for repeated data: marks, temperatures, tables, vectors, matrices and collections of records.
Advantages and limitations
An array reduces repetitive declarations and works naturally with loops. It makes searching, totaling and processing related values convenient. However, a fixed array cannot resize itself, it does not directly combine unrelated element types, and C does not automatically check its bounds.
Count is not the last index
For int marks[5], the count is 5 and the valid indexes are 0, 1, 2, 3 and 4. marks[5] is outside the array. Accessing it has undefined behaviour even if a particular run appears to work.
UNIT 03 · 02
Declare a list. Use one index.
One-dimensional array. A one-dimensional array is an array whose elements are accessed with a single index. It represents a linear sequence of values.
Array declaration. An array declaration states the element type, array name and number of elements, allowing the compiler to determine the array object’s type and storage requirements.
element_type array_name[number_of_elements];
int marks[5];
float temperature[7];
char letters[4];In int marks[5];, int is the element type, marks is the array name, and 5 is the number of elements. This declaration also defines a local array object when it appears in a function without extern. Declaration alone does not assign explicit initial values to an automatic local array.
Reading and changing elements
marks[0] = 80; /* store in the first element */
marks[4] = 95; /* store in the last element */
printf("%d\n", marks[0]); /* read the first element */
scanf("%d", &marks[2]); /* address of the third int */The array element expression marks[2] behaves as an int object. Use its address with scanf’s %d conversion and check the scanf return value. In a loop, use i < 5, not i <= 5, to visit five valid positions.
#include <stdio.h>
int main(void)
{
int marks[5] = {10, 20, 30, 40, 50};
int i;
printf("Before:");
for (i = 0; i < 5; i++) printf(" %d", marks[i]);
marks[2] = 99;
printf("\nAfter:");
for (i = 0; i < 5; i++) printf(" %d", marks[i]);
putchar('\n');
return 0;
}
Before: 10 20 30 40 50
After: 10 20 99 40 50
An array is not an assignable whole object
After an array has been declared, marks = {10, 20, 30, 40, 50}; is invalid. Nor can one ordinary array be copied to another with b = a;. Assign elements in a loop when copying arrays. An array name also is not a modifiable pointer variable: marks++ is invalid.
For a fixed-size C90 array, the bound must be a positive integer constant expression. A macro such as #define COUNT 5 can supply the bound. A local const int count = 5; is not an integer constant expression in C90 and cannot be used for this fixed-size declaration. Variable-length arrays belong to later C standards.
UNIT 03 · 03
Give every element a defined starting value.
Array initialization. Array initialization supplies initial values when the array object is defined, usually using a brace-enclosed list of values in element order.
int full[5] = {10, 20, 30, 40, 50};
int partial[5] = {7, 9}; /* 7, 9, 0, 0, 0 */
int zero[5] = {0}; /* all five become zero */
int inferred[] = {2, 4, 6, 8}; /* size inferred as four */| Form | Meaning | Important rule |
|---|---|---|
| Complete initializer | One listed value for every element | Do not provide more initializers than the declared count. |
| Partial initializer | Fewer listed values than elements | Unspecified remaining int elements are initialized to zero. |
| Inferred count | Omit the bracket size when an initializer determines it | int a[] = {1, 2, 3}; has three elements. |
| No initializer, automatic local array | No explicit starting values | Elements have indeterminate values; reading them can cause undefined behaviour. |
| No initializer, static-storage array | Storage exists for the program’s lifetime | Elements are zero-initialized, unlike an uninitialized automatic local array. |
Initialization versus later assignment
int a[3] = {1, 2, 3}; initializes an array as it is defined. a[0] = 9; later assigns a new value to one existing element. In introductory terminology, inputting values into a declared array at runtime is sometimes called “runtime initialization”; in C language syntax, those input operations are later stores, not a brace initializer.
#include <stdio.h>
int main(void)
{
int partial[5] = {7, 9};
int zero[3] = {0};
int inferred[] = {2, 4, 6, 8};
int i;
printf("Partial:");
for (i = 0; i < 5; i++) printf(" %d", partial[i]);
printf("\nZero:");
for (i = 0; i < 3; i++) printf(" %d", zero[i]);
printf("\nInferred element count = %lu\n",
(unsigned long)(sizeof inferred / sizeof inferred[0]));
return 0;
}
Partial: 7 9 0 0 0
Zero: 0 0 0
Inferred element count = 4
A character array can contain a string
char word[] = "BCA"; /* B, C, A, followed by the null character */
char word2[4] = {'B', 'C', 'A', '\0'};Both examples above have four elements. A C string needs its terminating null character. char letters[3] = {'B', 'C', 'A'}; is a character array but is not a null-terminated string, so do not print it with %s. Strings are shown here only to clarify character-array initialization.
UNIT 03 · 04
An index becomes a byte offset.
Memory representation. Memory representation describes how an object’s elements or members occupy storage locations, including their order, sizes and offsets from a starting address.
Base address. For the address formulas here, the base address B is the address at which the first array element begins.
Byte offset. A byte offset is the distance, measured in C bytes, from an object’s starting address to a particular element or member.
If an array has N elements and each element occupies w bytes, its total storage is N × w bytes. For zero-based C indexing, the address of element i is:
Address of a[i] = B + i × w
Here B is the first-element address, i is a valid index, and w is sizeof a[0]. This is an address calculation in bytes, not a suggestion to add a byte count to an int pointer: a + i already advances by i element sizes.
| Element | Value | Offset if w = 4 | Illustrative address if B = 1000 |
|---|---|---|---|
| a[0] | 10 | 0 | 1000 |
| a[1] | 20 | 4 | 1004 |
| a[2] | 30 | 8 | 1008 |
| a[3] | 40 | 12 | 1012 |
| a[4] | 50 | 16 | 1016 |
With these assumptions, a[3] begins at 1000 + 3 × 4 = 1012, and the whole array occupies 20 bytes, from 1000 through 1019. The table assumes a four-byte element for teaching; C does not guarantee that int always occupies four bytes.
EXPLORE THE CONCEPT
Select an element and inspect its address
Base address 1000 is illustrative. This is a storage model, not an allocation of memory in your browser.
Measuring the real object
sizeof a gives the byte size of an actual array object. sizeof a / sizeof a[0] gives its element count. This count formula works where a still has array type; it does not recover the length from a pointer or an array parameter adjusted to pointer type.
In most expressions, an array name converts to a pointer to its first element. It does not do this for sizeof or when used as the operand of unary &. Arrays and pointers are related, but an array object and a pointer object are different things.
#include <stdio.h>
int main(void)
{
int values[5] = {10, 20, 30, 40, 50};
unsigned char *first = (unsigned char *)&values;
printf("Element count = %lu\n",
(unsigned long)(sizeof values / sizeof values[0]));
printf("Array size equals 5 element sizes: %d\n",
sizeof values == 5 * sizeof values[0]);
/* Character pointers traverse the array object's byte representation. */
printf("Adjacent spacing equals one element size: %d\n",
first + sizeof values[0] == (unsigned char *)&values[1]);
return 0;
}
Element count = 5
Array size equals 5 element sizes: 1
Adjacent spacing equals one element size: 1
UNIT 03 · 05
Use the count to control the loop.
Input, total, average and maximum
The following program reserves ten elements but accepts a logical count from 1 to 10. It reads only that many values. Reserving capacity and recording the number actually used are separate tasks. Each value is restricted to −100 through 100 so the example’s total is safely representable even on a C implementation with a small int range.
- Read and validate n.
- For each index 0 through n − 1, read a value, validate it and add it to sum.
- Initialize maximum from the first valid element, then compare the remaining elements.
- Convert sum to double before dividing by n to obtain a fractional average.
#include <stdio.h>
#define CAPACITY 10
int main(void)
{
int values[CAPACITY], n, i, sum = 0, maximum;
if (scanf("%d", &n) != 1 || n < 1 || n > CAPACITY) {
puts("Enter a count from 1 to 10.");
return 1;
}
for (i = 0; i < n; i++) {
if (scanf("%d", &values[i]) != 1 || values[i] < -100 || values[i] > 100) {
puts("Enter each value from -100 to 100.");
return 1;
}
sum += values[i];
}
maximum = values[0];
for (i = 1; i < n; i++) {
if (values[i] > maximum) maximum = values[i];
}
printf("Sum = %d\nAverage = %.2f\nMaximum = %d\n",
sum, (double)sum / n, maximum);
return 0;
}
Sum = 150
Average = 30.00
Maximum = 50
Dry run: for values 10, 20, 30, 40 and 50, sum changes to 10, 30, 60, 100 and 150. The average is 150 / 5 = 30.00, and the maximum is 50. Do not initialize maximum to zero when all input values might be negative.
Array traversal. Array traversal visits the array’s elements in a chosen order, usually using a loop whose index stays within the valid range.
Linear search. Linear search examines elements one at a time until a matching value is found or all relevant elements have been examined.
#include <stdio.h>
int main(void)
{
int values[5] = {10, 20, 30, 40, 50};
int target, i, found = -1;
if (scanf("%d", &target) != 1) {
puts("Enter an integer target.");
return 1;
}
for (i = 0; i < 5; i++) {
if (values[i] == target) { found = i; break; }
}
if (found == -1) puts("Not found");
else printf("Found %d at index %d\n", target, found);
return 0;
}
Found 30 at index 2
In the search example, −1 records “no index found”; it is a sentinel value, not a valid index. Only use values[found] after confirming that found is a valid position.
UNIT 03 · 06
Rows of arrays. Two indexes.
Two-dimensional array. A two-dimensional array is an array whose elements are themselves one-dimensional arrays. It represents rows and columns, and each scalar element is accessed using two indexes.
element_type array_name[rows][columns];
int matrix[2][3];
int matrix2[2][3] = {{1, 2, 3}, {4, 5, 6}};int matrix[2][3] creates two row arrays, each containing three ints: six ints altogether. The first index chooses a row; the second chooses a column. Row indexes are 0 through 1 and column indexes are 0 through 2. matrix[1][2] is the last element, 6 in the initialized example.
| Expression | Meaning |
|---|---|
matrix[0][0] | First row, first column |
matrix[0][2] | First row, third column |
matrix[1][0] | Second row, first column |
matrix[1][2] | Second row, third column |
Ways to initialize a 2D array
int complete[2][3] = {{1, 2, 3}, {4, 5, 6}};
int flat[2][3] = {1, 2, 3, 4, 5, 6};
int partial[2][3] = {{1, 2}, {4}};
int zero[2][3] = {0};
int inferred_rows[][3] = {{1, 2, 3}, {4, 5, 6}};Complete and flat have the same values: initializers fill rows in order. Nested braces make row boundaries clearer. Partial becomes {{1, 2, 0}, {4, 0, 0}}; zero has six zero elements. With a determining initializer, the first dimension can be omitted, but the number of columns must be specified in this C90 form. int a[][] = ...; does not define the row size.
The same storage-duration rule applies to both dimensions: a local automatic array with no initializer has indeterminate elements; a static-storage array is zero-initialized when no initializer is supplied.
#include <stdio.h>
int main(void)
{
int matrix[2][3] = {{1, 2, 3}, {4, 5, 6}};
int row, column;
for (row = 0; row < 2; row++) {
for (column = 0; column < 3; column++) {
printf("%d%s", matrix[row][column], column == 2 ? "\n" : " ");
}
}
return 0;
}
1 2 3
4 5 6
#include <stdio.h>
int main(void)
{
int matrix[2][3] = {{1, 2}, {4}};
int row, column;
for (row = 0; row < 2; row++) {
for (column = 0; column < 3; column++) {
printf("%d%s", matrix[row][column], column == 2 ? "\n" : " ");
}
}
return 0;
}
1 2 0
4 0 0
Access using nested loops
Use the outer loop for rows and the inner loop for columns. Reset the column counter for each row. Use &matrix[row][column] with scanf for an int element, and pass the value matrix[row][column] to printf. Both indexes must independently be in range: matrix[0][3] is invalid even if the next row happens to be adjacent in storage.
UNIT 03 · 07
C stores complete rows consecutively.
Row-major order. Row-major order stores the elements of the first row consecutively, followed by the second row, and so on. The last index changes fastest in storage order.
For int a[2][3] = {{1,2,3},{4,5,6}};, storage order is a[0][0], a[0][1], a[0][2], a[1][0], a[1][1], a[1][2]. This follows from C’s representation of the outer array as consecutive row-array objects.
Address of a[i][j] = B + (i × C + j) × w
B is the first scalar element’s starting address, C is the number of columns, w is the scalar element size, and i/j are valid row/column indexes. There are i complete rows before row i, each containing C elements, then j elements before the desired column. Total storage is R × C × w bytes; the row stride is C × w bytes.
Row stride. Row stride is the byte distance between the starting locations of consecutive rows. In a C array with C columns, it equals C multiplied by the scalar element size.
Worked calculation
For a[2][3], assume B = 2000 and w = 4 bytes. The offset of a[1][2] is (1 × 3 + 2) × 4 = 20 bytes, so its illustrative address is 2020. Each row occupies 12 bytes and the full matrix occupies 24 bytes.
EXPLORE THE CONCEPT
Connect the grid to the memory strip
STORAGE ORDER · ROW MAJOR
Illustrative B = 2000, scalar size = 4 bytes. Each cell labels its row and column indexes.
#include <stdio.h>
int main(void)
{
int matrix[2][3] = {{1, 2, 3}, {4, 5, 6}};
unsigned char *base = (unsigned char *)&matrix;
printf("Rows = %lu, columns = %lu\n",
(unsigned long)(sizeof matrix / sizeof matrix[0]),
(unsigned long)(sizeof matrix[0] / sizeof matrix[0][0]));
printf("Row stride equals 3 element sizes: %d\n",
base + 3 * sizeof matrix[0][0] == (unsigned char *)&matrix[1][0]);
printf("matrix[1][2] offset equals 5 element sizes: %d\n",
base + 5 * sizeof matrix[0][0] == (unsigned char *)&matrix[1][2]);
return 0;
}
Rows = 2, columns = 3
Row stride equals 3 element sizes: 1
matrix[1][2] offset equals 5 element sizes: 1
Transpose. The transpose of a matrix is obtained by interchanging its rows and columns. An R × C matrix becomes a C × R matrix.
The following program reads a 2 × 3 matrix and prints its transpose without storing a second matrix. It changes traversal order: columns become the outer loop and rows the inner loop. A separate transpose array would be declared int result[3][2]; and assigned with result[column][row] = matrix[row][column];.
#include <stdio.h>
int main(void)
{
int matrix[2][3], row, column;
for (row = 0; row < 2; row++) {
for (column = 0; column < 3; column++) {
if (scanf("%d", &matrix[row][column]) != 1) {
puts("Enter six integers.");
return 1;
}
}
}
puts("Transpose (3 x 2):");
for (column = 0; column < 3; column++) {
for (row = 0; row < 2; row++) {
printf("%d%s", matrix[row][column], row == 1 ? "\n" : " ");
}
}
return 0;
}
Transpose (3 x 2):
1 4
2 5
3 6
UNIT 03 · 08
Combine the fields of one record.
Structure. A structure is a user-defined aggregate type that groups named members, which may have different types, into one record. Each member has its own storage and the members’ values can coexist.
Structure member. A structure member is a named component declared inside a structure definition, such as roll, name or marks in a Student record.
Structure definition. A structure definition specifies a structure type’s tag and its member declarations, enclosed in braces and followed by a semicolon.
struct Student {
int roll;
char name[30];
double marks;
};The tag is Student; the type name used in declarations is struct Student. The member declarations describe the contents of each object of that type. This definition alone does not create a Student variable or reserve storage for a particular student. The semicolon after the closing brace is required.
Why structures are useful
- Group related information: roll number, name and marks form one student record.
- Mix member types: a record can contain an int, a character array and a double.
- Keep records together: an array of Student structures can hold several complete students instead of coordinating separate arrays.
- Improve clarity:
student.marksexplains its meaning better than unrelated variables. - Copy or pass complete records: compatible structure objects can be assigned, and structures can be passed to or returned from functions.
- Model larger data: structures can contain arrays and other complete structure types.
Members may also have the same type: a Point with two double members is still a structure. The distinction from an array is named record fields and independent member types, not a requirement that all member types differ.
UNIT 03 · 09
Create the record. Work with its members.
Structure variable declaration. A structure variable declaration creates an object of a previously defined structure type, giving the object a name and reserving storage for its members.
struct Student first;
struct Student first, second; /* alternative declaration: two objects */The snippets above are alternatives, not declarations to paste together into the same scope. Objects can also be declared with the type definition:
struct Point {
int x;
int y;
} start, finish;This defines the type struct Point and two objects. Keeping the definition and object declarations separate is often easier to read. In C, the bare tag Student is not a type alias by itself; use struct Student unless a typedef has introduced an alias.
Structure initialization. Structure initialization supplies initial member values when a structure object is defined. In a positional brace initializer, values correspond to members in declaration order.
struct Student student = {7, "Asha", 85.5};
struct Student partial = {7}; /* name becomes an empty string; marks is 0.0 */
struct Student zero = {0}; /* roll 0, empty name, marks 0.0 */Unspecified remaining members receive zero initialization when a brace initializer is present. An automatic local structure with no initializer does not acquire usable starting values by being declared. Write each member before reading it. Static-storage structures without an initializer are zero-initialized.
Member access. Member access selects a named field of a structure or union object. The dot operator selects a member from an object; the arrow operator selects a member through a pointer to an object.
student.roll = 8;
student.marks = 90.0;
printf("%s\n", student.name);
scanf("%lf", &student.marks);
/* Pointer form, shown for recognition */
struct Student *p = &student;
p->roll = 9; /* same member as (*p).roll */The last three lines illustrate pointer syntax; in a C90 function, place declarations such as p before statements. The runnable examples follow that rule. Use parentheses in (*p).roll because the dot operator binds more tightly than unary *. Pointer topics are developed later.
#include <stdio.h>
struct Student {
int roll;
char name[30];
double marks;
};
int main(void)
{
struct Student student = {7, "Asha", 85.5};
printf("Roll = %d\nName = %s\nMarks = %.2f\n",
student.roll, student.name, student.marks);
student.marks = 90.0;
printf("Updated marks = %.2f\n", student.marks);
return 0;
}
Roll = 7
Name = Asha
Marks = 85.50
Updated marks = 90.00
An array member cannot receive a string by ordinary assignment
student.name = "Tara"; is invalid because name is a character array. A string literal can initialize it in the original structure definition of the object, but later copying needs a suitable operation, such as strcpy with enough destination capacity. For input that includes spaces, use a bounded line read.
#include <stdio.h>
#include <string.h>
struct Student { int roll; char name[30]; double marks; };
int main(void)
{
struct Student student;
if (fgets(student.name, sizeof student.name, stdin) == NULL) {
puts("Enter a name.");
return 1;
}
/* A full line is required: a name can contain up to 28 characters here. */
if (strchr(student.name, '\n') == NULL) {
puts("Use a name of at most 28 characters, followed by newline.");
return 1;
}
student.name[strcspn(student.name, "\n")] = '\0';
if (student.name[0] == '\0' ||
scanf("%d %lf", &student.roll, &student.marks) != 2 ||
student.roll < 1 || student.roll > 999 ||
!(student.marks >= 0.0 && student.marks <= 100.0)) {
puts("Enter a nonempty name, roll 1 to 999, and marks 0 to 100.");
return 1;
}
printf("Roll = %d\nName = %s\nMarks = %.2f\n",
student.roll, student.name, student.marks);
return 0;
}
Roll = 7
Name = Asha Rao
Marks = 85.50
The input program expects a name line followed by roll and marks. It checks that the complete name line fits, removes the newline and checks all numeric fields before using them.
Whole-record assignment
second = first; copies compatible structure values, including an array member’s contents as part of the structure. Changing second afterward does not change first in the example below. A copied pointer member would copy the pointer value, not create a separate copy of the pointed-to object. Compare member values explicitly; first == second is not supported for whole structures in C.
#include <stdio.h>
#include <string.h>
struct Student { int roll; char name[30]; double marks; };
int main(void)
{
struct Student first = {7, "Asha", 85.5};
struct Student second;
second = first;
second.roll = 8;
strcpy(second.name, "Tara"); /* Fits in the 30-character array. */
printf("Original: %d %s %.2f\n", first.roll, first.name, first.marks);
printf("Copy: %d %s %.2f\n", second.roll, second.name, second.marks);
return 0;
}
Original: 7 Asha 85.50
Copy: 8 Tara 85.50
struct Student class_list[2] = {
{1, "Asha", 85.5},
{2, "Ravi", 78.0}
};
/* class_list[1].marks is the second student's marks. */UNIT 03 · 10
Independent storage may include gaps.
Padding. Padding is extra storage that an implementation may insert between structure members or after the final member to meet layout and alignment requirements. It is not another declared member.
Alignment. Alignment is a requirement or convention that an object begin at an address suitable for its type. A compiler may add padding to satisfy the alignment of structure members and array elements.
Non-bit-field structure members occur in declaration order at increasing addresses. There is no padding before the first member, but there may be padding between members and at the end. Therefore, the size of a structure may exceed the sum of its member sizes. Measure the complete size with sizeof and a member’s offset with offsetof from <stddef.h>.
struct Record { char c; int i; double d; };
sizeof(struct Record)
offsetof(struct Record, i)EXPLORE THE CONCEPT
Compare two illustrative layout models
These are explicitly assumed models, not guarantees of a particular C compiler. The runnable measurement program below reports actual values.
In the first model, c occupies offset 0, three padding bytes follow, i begins at offset 4 and occupies four bytes, and d begins at offset 8 and occupies eight bytes. The structure’s size is 16 bytes, although the member sizes sum to 13. Different type sizes and alignment requirements can produce a different result.
Structure arrays use sizeof(struct Record) as their element stride, including any tail padding. Do not assume that rearranging members, compiling for a new platform or using a nonstandard packing option preserves offsets. For this syllabus, understand the normal layout and use sizeof rather than memorizing a universal byte count.
UNIT 03 · 11
Several member names. Shared storage.
Union. A union is a user-defined type whose named members overlap in the same storage. It can represent alternative member values using one shared memory region rather than separate storage for each member.
Union definition. A union definition specifies a union type’s tag and alternative member declarations within braces, followed by a semicolon.
union Value {
int integer;
double real;
char letter;
};Union variable declaration. A union variable declaration creates an object of a union type, such as union Value value;, allocating storage sufficient for its largest member and any implementation-required padding.
union Value value;
union Value first, second; /* two separate union objects */The definition by itself describes the type. The declarations create objects. As with structures, a union definition can also be followed by object names before its semicolon. Its members need not all have different types, but each provides a named view of the same storage.
Memory representation
Every union member begins at the union’s starting address. The union has enough space and suitable alignment for any one member. Its size is at least the size of the largest member; padding or alignment can make it larger. It is not the sum of the member sizes.
In an illustrative Value with int size 4 and double size 8, all three members start at offset zero. A conventional eight-byte-aligned implementation may use eight bytes for the union. Always measure the actual object with sizeof.
Union initialization. Union initialization supplies an initial value for one member when a union object is defined. In C90, an ordinary brace initializer initializes the first declared member.
union Value value = {42}; /* initializes value.integer */
/* Later stores, not declaration-time initialization */
value.real = 3.5;
value.letter = 'A';union Value value = {42, 3.5, 'A'}; is not a valid way to initialize all alternatives. C99 introduced designated initializers such as union Value v = {.real = 3.5};, but our runnable examples use C90-compatible first-member initialization. For an automatic union with no initializer, assign an appropriate member before reading it.
Access only the intended current value
Use value.integer, value.real or value.letter. Storing in one member changes the shared representation, so earlier values in other members are not independent saved values. A union does not automatically record which member the program intends to use.
Reading a different member from the one most recently stored interprets the shared representation as another type. It does not reliably retrieve the earlier value and can involve implementation-dependent representation or a trap representation. The beginner examples always read the member they just stored.
EXPLORE THE CONCEPT
Store one member, then choose another
integer, real and letter all begin at offset 0.
Read value.integer: 42. Other members do not hold independent saved values.
This demonstration tracks the intended member; it does not reinterpret or fabricate the raw bytes of a different C type.
#include <stdio.h>
union Value { int integer; double real; char letter; };
int main(void)
{
union Value value = {42}; /* C90 initializes the first member. */
printf("Integer member = %d\n", value.integer);
value.real = 3.5;
printf("Real member = %.2f\n", value.real);
value.letter = 'A';
printf("Character member = %c\n", value.letter);
/* Read only the member most recently stored in this example. */
return 0;
}
Integer member = 42
Real member = 3.50
Character member = A
Why use a union?
Use a union when a record needs one of several alternative representations, such as a value that is either an integer or a real number. It can reduce storage compared with keeping all alternatives in a structure. In a larger program, a separate enum or integer tag can record which alternative is valid; that combination is commonly called a tagged union. Use a structure when the fields must all coexist.
A union with static storage duration and no explicit initializer has its first named member zero-initialized. This does not turn its alternatives into separately stored values. In every case, keep track of the member the program intends to read.
UNIT 03 · 12
Choose based on which values must coexist.
| Point of comparison | Structure | Union |
|---|---|---|
| Definition | Groups named members with separate storage | Groups named alternatives with overlapping storage |
| Storage | Each ordinary member has its own location | All alternatives begin at the same location |
| Values | All initialized member values can coexist | Use one intended member value at a time |
| Size | Includes member storage and possible padding | At least the largest member’s size; possible padding |
| Writing a member | Does not overwrite a different ordinary member | Changes the shared representation used by other members |
| Initialization in C90 | May initialize several members in declaration order | Ordinary brace initializer initializes the first member |
| Member access | Dot for an object; arrow for a pointer | Dot for an object; arrow for a pointer |
| Suitable example | Student roll, name and marks needed together | A number stored as int or double |
| Whole-object assignment | Supported between compatible structure objects | Supported between compatible union objects |
Compare the same member list
struct Record { char c; int i; double d; };
union Shared { char c; int i; double d; };A Record can retain c, i and d simultaneously. A Shared uses one storage region for the alternatives. In the illustrative 1/4/8-byte model with appropriate 1/4/8-byte alignment, Record may occupy 16 bytes and Shared may occupy 8 bytes. These sizes are examples, not definitions of the types.
#include <stdio.h>
#include <stddef.h>
struct Record { char c; int i; double d; };
union Shared { char c; int i; double d; };
int main(void)
{
printf("Member sizes: char=%lu int=%lu double=%lu\n",
(unsigned long)sizeof(char), (unsigned long)sizeof(int),
(unsigned long)sizeof(double));
printf("Structure offsets: c=%lu i=%lu d=%lu\n",
(unsigned long)offsetof(struct Record, c),
(unsigned long)offsetof(struct Record, i),
(unsigned long)offsetof(struct Record, d));
printf("Structure size = %lu\n", (unsigned long)sizeof(struct Record));
printf("Union offsets: c=%lu i=%lu d=%lu\n",
(unsigned long)offsetof(union Shared, c),
(unsigned long)offsetof(union Shared, i),
(unsigned long)offsetof(union Shared, d));
printf("Union size = %lu\n", (unsigned long)sizeof(union Shared));
return 0;
}
Illustrative output when char = 1 byte, int = 4 bytes, double = 8 bytes and double alignment = 8:
Member sizes: char=1 int=4 double=8
Structure offsets: c=0 i=4 d=8
Structure size = 16
Union offsets: c=0 i=0 d=0
Union size = 8
Actual sizes and offsets depend on the compiler and target.The program prints actual sizes and offsets. The displayed sample is one common layout; a different result can be correct on another compiler. C90 has no %zu conversion, so the program casts these small sizeof/offsetof results to unsigned long for %lu.
Arrays versus structures
| Feature | Array | Structure |
|---|---|---|
| Components | Elements of one element type | Named members, possibly of different types |
| Selection | Integer index: a[i] | Member name: s.marks |
| Storage order | Consecutive elements; each stride is sizeof element | Members in declaration order, possibly with padding |
| Ordinary whole-object assignment | Not supported for arrays | Supported for compatible structures |
| Typical purpose | Repeated items of the same kind | Related fields of one record |
An array of structures combines both ideas: each array element has the same structure type, and each structure element contains its named record fields.
UNIT 03 · 13
Compile the idea. Inspect the result.
Choose an example and load it into the embedded C compiler. Select I/O inside the compiler, check the sample STDIN, and press Run. Change the input or edit the source to explore a different case.
Load the compiler when you’re ready to practise.
Your C workspace
Edit real C code and see real compiler output.
Compiler provided by OneCompiler. Opening the editor connects to that service; code and input you run are processed there. If the embed is unavailable, open the C compiler in a new tab and paste the source below.
View / copy the selected source
#include <stdio.h>
int main(void)
{
int marks[5] = {10, 20, 30, 40, 50};
int i;
printf("Before:");
for (i = 0; i < 5; i++) printf(" %d", marks[i]);
marks[2] = 99;
printf("\nAfter:");
for (i = 0; i < 5; i++) printf(" %d", marks[i]);
putchar('\n');
return 0;
}
Practice tasks
- Change one initializer in the 1D array program, predict the changed output, then run it.
- Use the array-summary program with three negative values. Explain why maximum starts with values[0].
- Change a 2D initializer to
{{9}, {8, 7}}and identify every zero-filled element. - Calculate the byte offset of matrix[1][1] symbolically before running the storage verification.
- Add a member to Student, update the initializer, and print it with an appropriate conversion.
- Change only the copied student’s name and confirm that the original remains unchanged.
- Run the layout program and compare its measured offsets with the illustrative diagram.
- In the union example, explain why each value is printed before the next member is stored.
UNIT 03 · 14
Definitions you can explain in an exam.
Written questions with model answers
These answers are complete study notes, not just a list of questions. Add a labelled diagram and a short program where the question requests them. Use the print button to save the notes as PDF; written answers expand for printing.
1. Define an array and explain a one-dimensional array.
An array is a collection of elements of the same type stored consecutively and selected by a common name and index. A one-dimensional array uses one index to represent a linear sequence. Its declaration is type name[count];. For example, int marks[5] = {10,20,30,40,50}; creates five int elements. Valid indexes are 0 through 4. marks[2] is the third element, initially 30. A loop using i = 0 while i < 5 traverses every element. Arrays reduce repeated declarations but do not check bounds or automatically resize.
2. Explain declaration and initialization of a 1D array.
Declaration specifies the element type, name and count, for example int a[4];. Initialization gives values when the object is defined: int a[4] = {1,2,3,4};. A partial initializer such as {1,2} leaves the remaining two elements zero. {0} initializes all int elements to zero. With int a[] = {1,2,3};, the count is inferred as three. An uninitialized automatic local array has indeterminate elements; a static-storage array without an initializer is zero-initialized. Later assignments such as a[0] = 9 change individual existing elements.
3. Derive the 1D array address formula.
Let B be the first-element address and w the size of one element in bytes. There are i elements before a[i]. Since elements are consecutive, those elements occupy i × w bytes. Therefore Address(a[i]) = B + i × w for valid zero-based i. If B = 1000, w = 4 and i = 3, the address is 1012. For N elements, total storage is N × w. An int size of four bytes is an assumption for this example; use sizeof to find the actual size.
4. Define a 2D array and explain initialization and row-major storage.
A two-dimensional array is an array of one-dimensional row arrays. Its declaration is type name[rows][columns];. int m[2][3] = {{1,2,3},{4,5,6}}; creates two rows of three ints each. The element m[1][2] has value 6. Initialization fills rows in order; unspecified elements are zero when a brace initializer is supplied. C uses row-major order: m[0][0], m[0][1], m[0][2], m[1][0], m[1][1], m[1][2]. The first dimension may be inferred from an initializer, but the column count must remain specified in this C90 form.
There are i complete rows of C elements before row i and j elements before column j. Thus Address(m[i][j]) = B + (i × C + j) × w. Total storage is R × C × w. For B = 2000, C = 3, w = 4, i = 1 and j = 2, the address is 2020.
5. Define a structure and state its advantages.
A structure is a user-defined aggregate type that groups named members, possibly of different types, into a record with separate storage for each member. struct Student { int roll; char name[30]; double marks; }; defines a record type. It groups related information, accommodates several member types, improves naming and readability, allows arrays of complete records, supports whole-record assignment and can be passed to or returned from functions. Its members can hold their values simultaneously. A structure definition alone describes the type and does not create a particular record object.
6. Explain declaration, initialization and member access for a structure.
After defining Student, struct Student s; declares an object. struct Student s = {7,"Asha",85.5}; initializes members in declaration order. Omitted members in a partial brace initializer receive zero initialization. Use s.roll, s.name and s.marks to access members; for a pointer p, use p->marks. A member can be updated with s.marks = 90.0;. Use &s.marks with scanf’s %lf conversion. The name array cannot be assigned a new string with =; copy a string within its capacity or use bounded input. Compatible structures can be copied with second = first;, including their array members.
7. Define a union and explain declaration, initialization and use.
A union is a user-defined type whose named alternatives overlap in one storage region. union Value { int integer; double real; char letter; }; defines the type, and union Value v; creates an object. In C90, union Value v = {42}; initializes its first declared member, integer. Access a member with the dot operator, for example v.integer. To store a real number, assign v.real = 3.5 and then read v.real. A subsequent write to another member changes the shared representation, so other members do not independently retain previous values. All member offsets are zero, and sizeof the union is at least the largest member size, possibly increased by padding. Use a separate tag in programs that must track the intended member.
8. Distinguish structures and unions, including memory size.
A structure gives members separate locations; a union overlaps members at one starting address. Structure member values can coexist; a union is used for alternative values. Writing one ordinary structure member does not replace a different member, while writing a union member changes shared storage. A structure’s size includes all members plus possible internal/tail padding. A union’s size is at least the largest member size and may include padding. Structures can initialize several members in order; a normal C90 union initializer selects the first member. Both use dot/arrow member access and support compatible whole-object assignment. A student record needs a structure; a value that is either int or double can use a union.
Additional practice
- Draw the address map of a[6] assuming B = 500 and w = 2. Find a[4] and the total storage.
- For m[3][4], assuming B = 1000 and w = 4, find m[2][1]. State valid row and column ranges.
- Write a program to read five values into an array and print them in reverse order.
- Define an Employee structure with ID, name and salary; initialize and print one record.
- Explain why a structure can be copied with = while an ordinary array cannot.
- Explain why a structure size and a union size should be measured instead of assumed.
Address checks: a[4] = 508, array storage = 12 bytes; m[2][1] = 1036, valid rows 0–2 and columns 0–3.
UNIT 03 · 15
A complete syllabus companion.
E. Balagurusamy, Programming in ANSI C, Eighth Edition, McGraw Hill Education (India) Private Limited. ISBN 9789353165130.
This page provides original definitions, explanations, worked calculations, programs and model answers for the supplied Unit 3 syllabus. Use the book’s Arrays and Structures and Unions topics for additional exercises. The publisher’s catalogue confirms the edition; the full textbook chapter text was not available for consultation here, so these notes do not quote it or claim page-by-page correspondence.