C 3

Phase 3 — Arrays, Strings & User-Defined Types

This phase is where C starts becoming much more interesting.

You will learn how C handles collections of data, text, and your own custom data types.

We’ll cover this phase in 10 chapters:

  1. 1D Arrays

  2. Array Initialization & Traversal

  3. Multidimensional Arrays

  4. Arrays Passed to Functions

  5. Character Arrays & C Strings

  6. String Input & Safety

  7. <string.h> String Functions

  8. struct

  9. Nested Structures & Arrays of Structures

  10. union, enum, and typedef


Chapter 1 — One-Dimensional Arrays

Question

Given below is a program that:

  • creates an array of five integers

  • stores values in it

  • accesses individual elements

  • modifies one element

  • calculates a total

What should be the output?

#include <stdio.h>

int main(void)
{
    // Create an array containing five integers.
    int marks[5] = {70, 85, 90, 65, 80};

    // Change the third element.
    marks[2] = 95;

    // Calculate the total.
    int total = marks[0] + marks[1] + marks[2] + marks[3] + marks[4];

    // Print the third mark and total.
    printf("Third mark: %d\n", marks[2]);
    printf("Total: %d\n", total);

    return 0;
}

Predict the output before reading further.

Answer

Third mark: 95
Total: 395

Step-by-step explanation

An array is a collection of values of the same type stored under one name.

int marks[5];

means:

Create an array called marks capable of holding 5 integers.

The positions are:

marks[0]  marks[1]  marks[2]  marks[3]  marks[4]
   70        85        90        65        80

Critical rule

C arrays use zero-based indexing.

So:

first element  → [0]
second         → [1]
third          → [2]
fourth         → [3]
fifth          → [4]

This:

marks[2] = 95;

changes:

90 → 95

Therefore:

70 + 85 + 95 + 65 + 80 = 395

How to read the important code

Read:

int marks[5]

as:

marks is an array of 5 integers.

Read:

marks[2]

as:

the element at index 2 of marks.

Beginner trap

marks[5] does not mean the fifth element.

It means the array has 5 elements, whose valid indexes are 0 through 4.

Key takeaway

An array stores multiple values of the same type, and C arrays start at index 0.


Chapter 2 — Array Initialization and Traversal

Question

What will this program print?

#include <stdio.h>

int main(void)
{
    // Create and initialize an array.
    int numbers[] = {10, 20, 30, 40, 50};

    // Calculate how many elements are in the array.
    int count = sizeof(numbers) / sizeof(numbers[0]);

    // Visit every element.
    for (int i = 0; i < count; i++)
    {
        printf("%d ", numbers[i]);
    }

    return 0;
}

Answer

10 20 30 40 50

Step-by-step explanation

Notice:

int numbers[] = {10, 20, 30, 40, 50};

We didn't specify the size.

C counts the initializer:

10
20
30
40
50

So the array contains 5 integers.

This expression:

sizeof(numbers)

gives the total number of bytes occupied by the array.

This:

sizeof(numbers[0])

gives the number of bytes occupied by one integer.

Therefore:

sizeof(numbers) / sizeof(numbers[0])

gives the number of elements.

Then:

for (int i = 0; i < count; i++)

produces:

i = 0 → numbers[0]
i = 1 → numbers[1]
i = 2 → numbers[2]
i = 3 → numbers[3]
i = 4 → numbers[4]

How to read the important code

numbers[i]

means:

Give me the array element whose index is currently stored in i.

This pattern is extremely common in C.

Beginner trap

Don't hard-code the array length when you can calculate it safely:

sizeof(numbers) / sizeof(numbers[0])

But remember: this works for an actual array in the scope where it exists. It does not generally work after the array has been passed to a function.

We'll see why later.

Key takeaway

A for loop plus an index variable is the fundamental way to traverse a C array.


Chapter 3 — Multidimensional Arrays

Question

What will be printed?

#include <stdio.h>

int main(void)
{
    // Create a 2D array representing two rows and three columns.
    int matrix[2][3] = {
        {1, 2, 3},
        {4, 5, 6}
    };

    // Change the middle element of the second row.
    matrix[1][1] = 50;

    // Print the element at row 2, column 2.
    printf("%d\n", matrix[1][1]);

    // Print every element.
    for (int row = 0; row < 2; row++)
    {
        for (int col = 0; col < 3; col++)
        {
            printf("%d ", matrix[row][col]);
        }

        printf("\n");
    }

    return 0;
}

Answer

50
1 2 3
4 50 6

Step-by-step explanation

A 2D array:

int matrix[2][3];

can be visualized as:

             columns
          0    1    2
       ┌────┬────┬────┐
row 0  │  1 │  2 │  3 │
       ├────┼────┼────┤
row 1  │  4 │  5 │  6 │
       └────┴────┴────┘

So:

matrix[1][1]

means:

row 1, column 1

which initially contains 5.

Then:

matrix[1][1] = 50;

changes it to 50.

The nested loops work like:

row 0:
    col 0
    col 1
    col 2

row 1:
    col 0
    col 1
    col 2

How to read the important code

Read:

matrix[row][col]

as:

element at this row and this column.

Beginner trap

Again, indexing starts at 0.

For:

int matrix[2][3];

valid indexes are:

row:    0, 1
column: 0, 1, 2

Key takeaway

A multidimensional array is an array whose elements are themselves arrays.


Chapter 4 — Arrays Passed to Functions

Question

What is the output?

#include <stdio.h>

// Receive an array and its number of elements.
void double_values(int numbers[], int count)
{
    // Modify every element.
    for (int i = 0; i < count; i++)
    {
        numbers[i] *= 2;
    }
}

int main(void)
{
    // Create an array.
    int numbers[] = {5, 10, 15};

    // Pass the array to the function.
    double_values(numbers, 3);

    // Print the modified array.
    for (int i = 0; i < 3; i++)
    {
        printf("%d ", numbers[i]);
    }

    return 0;
}

Answer

10 20 30

Step-by-step explanation

The important thing here is:

double_values(numbers, 3);

The array is passed to the function.

Inside:

void double_values(int numbers[], int count)

the parameter looks like an array.

But there is an important C concept hiding underneath:

When an array is passed to a function, the parameter effectively becomes a pointer to its first element.

We'll study the pointer mechanics deeply in Phase 4.

For now, understand the behavior.

The function modifies:

5  → 10
10 → 20
15 → 30

The changes remain visible in main.

Why do we pass count?

Because the function cannot reliably determine the original array's number of elements using:

sizeof(numbers)

inside the function.

So we commonly write:

function(array, number_of_elements);

How to read the important code

void double_values(int numbers[], int count)

means:

This function receives access to an integer array and is told how many elements it contains.

Key takeaway

When passing arrays to functions, normally pass the array together with its element count.


Chapter 5 — Character Arrays and C Strings

Question

What will this print?

#include <stdio.h>

int main(void)
{
    // Store a string inside a character array.
    char name[] = "Alice";

    // Change the first character.
    name[0] = 'M';

    // Print the character array as a string.
    printf("%s\n", name);

    // Print the size of the character array.
    printf("%zu\n", sizeof(name));

    return 0;
}

Answer

Mlice
6

Step-by-step explanation

This:

char name[] = "Alice";

creates a character array.

Internally, C stores it approximately as:

'A' 'l' 'i' 'c' 'e' '\0'

That final:

'\0'

is the null character.

It marks the end of a C string.

Therefore "Alice" requires:

5 characters + 1 null character = 6 bytes

Then:

name[0] = 'M';

changes:

Alice
↓
Mlice

Extremely important distinction

A C string is not a special built-in string type.

It is:

A sequence of characters ending with '\0'.

How to read the important code

char name[]

means:

an array of characters.

printf("%s", name);

means:

print characters starting at name until the null character is encountered.

Beginner trap

These are different:

'A'

and:

"A"

'A' is a character.

"A" is a string containing:

'A' '\0'

Key takeaway

A C string is a character array terminated by '\0'.


Chapter 6 — String Input and Buffer Safety

Question

Why is this version safer than using scanf("%s", name)?

#include <stdio.h>

int main(void)
{
    // Create a character buffer that can hold 49 characters plus '\0'.
    char name[50];

    // Safely read a line of text.
    printf("Enter your name: ");
    if (fgets(name, sizeof(name), stdin) != NULL)
    {
        // Print the entered string.
        printf("Hello, %s", name);
    }

    return 0;
}

Answer

Because:

fgets(name, sizeof(name), stdin)

knows the size of the destination buffer.

It can therefore limit how many characters it reads.

Step-by-step explanation

Suppose:

char name[50];

The array has room for 50 characters.

But a C string needs a terminating:

'\0'

So fgets ensures it doesn't exceed the available buffer when used this way.

For example:

Alice

becomes approximately:

'A' 'l' 'i' 'c' 'e' '\n' '\0'

when the newline fits.

Why scanf("%s", name) can be dangerous

Consider:

char name[10];

scanf("%s", name);

If the user enters a very long word, scanf can write beyond the array.

That can cause:

  • memory corruption

  • crashes

  • undefined behavior

  • security vulnerabilities

Important fgets behavior

fgets may store the newline:

Alice\n\0

So sometimes you remove it manually.

Example:

name[strcspn(name, "\n")] = '\0';

We'll understand strcspn in the next chapter.

How to read the important code

fgets(name, sizeof(name), stdin)

Read it as:

Read a line from standard input into name, but don't exceed the size of the buffer.

Key takeaway

In C, always think about the size of the destination buffer when reading strings.


Chapter 7 — <string.h> and String Functions

Question

What is the output?

#include <stdio.h>
#include <string.h>

int main(void)
{
    // Create two strings.
    char first[30] = "Hello";
    char second[] = " World";

    // Add the second string to the first.
    strcat(first, second);

    // Create a copy of the resulting string.
    char copy[30];
    strcpy(copy, first);

    // Compare the two strings.
    int result = strcmp(first, copy);

    // Print string information.
    printf("%s\n", first);
    printf("%zu\n", strlen(first));
    printf("%d\n", result);

    return 0;
}

Answer

Hello World
11
0

Step-by-step explanation

<string.h> provides many standard string functions.

strcat

strcat(first, second);

appends second to first.

So:

Hello
+
 World
=
Hello World

The destination must have enough space.

That's very important.


strcpy

strcpy(copy, first);

copies the C string from first into copy.

Now:

first = "Hello World"
copy  = "Hello World"

Again, the destination must have enough space.


strlen

strlen(first)

returns the number of characters before '\0'.

Hello World

contains:

5 + 1 + 5 = 11

characters.

The terminating '\0' is not counted.


strcmp

strcmp(first, copy)

compares the strings.

If they are equal:

0

If they differ, the result is negative or positive depending on their lexicographical comparison.

Important functions

FunctionPurpose
strlenGet string length
strcpyCopy string
strncpyBounded-style copy
strcatAppend string
strncatAppend with a limit
strcmpCompare strings
strncmpCompare limited characters
strchrFind a character
strstrFind a substring
strcspnFind length before characters from a set

Important safety note

Functions such as:

strcpy()
strcat()

do not automatically know how large your destination buffer is.

Incorrect usage can cause buffer overflows.

How to read the important code

strcmp(a, b)

means:

Compare the contents of the two strings.

Do not use:

a == b

to compare C string contents.

Key takeaway

C's string library works with null-terminated character arrays, so buffer size remains your responsibility.


Chapter 8 — Structures (struct)

Question

What will be printed?

#include <stdio.h>

struct Student
{
    // Store the student's name.
    char name[20];

    // Store the student's age.
    int age;

    // Store the student's marks.
    float marks;
};

int main(void)
{
    // Create a structure variable and initialize it.
    struct Student student = {"Rahul", 21, 87.5f};

    // Modify one member.
    student.age = 22;

    // Print the structure's data.
    printf("%s\n", student.name);
    printf("%d\n", student.age);
    printf("%.1f\n", student.marks);

    return 0;
}

Answer

Rahul
22
87.5

Step-by-step explanation

A struct allows you to group different types of data together.

We define:

struct Student

with:

name
age
marks

Conceptually:

Student
┌────────────────┐
│ name           │
│ age            │
│ marks          │
└────────────────┘

Then:

struct Student student;

creates one variable of that structure type.

To access a member, use:

student.age

The dot:

.

is called the member access operator.

Why structures matter

Without a structure, you might have:

char name[20];
int age;
float marks;

for one student.

For 1,000 students, managing related data becomes messy.

With:

struct Student

you can have:

struct Student students[1000];

which we'll use shortly.

How to read the important code

student.marks

means:

access the marks member belonging to student.

Key takeaway

A struct lets you combine related variables, even when they have different types.


Chapter 9 — Nested Structures and Arrays of Structures

Question

What will be printed?

#include <stdio.h>

struct Address
{
    // Store the city.
    char city[30];

    // Store the PIN code.
    int pin;
};

struct Student
{
    // Store the student's name.
    char name[30];

    // Store the student's address.
    struct Address address;
};

int main(void)
{
    // Create an array containing two students.
    struct Student students[2] = {
        {"Amit", {"Patna", 800001}},
        {"Neha", {"Gaya", 823001}}
    };

    // Modify Neha's PIN code.
    students[1].address.pin = 823002;

    // Print Neha's information.
    printf("%s\n", students[1].name);
    printf("%s\n", students[1].address.city);
    printf("%d\n", students[1].address.pin);

    return 0;
}

Answer

Neha
Gaya
823002

Step-by-step explanation

Here we have a structure inside another structure.

struct Student
{
    char name[30];
    struct Address address;
};

So a Student contains an Address.

The hierarchy is:

Student
├── name
└── address
    ├── city
    └── pin

Therefore:

students[1]

means:

second student.

Then:

students[1].address

means:

that student's address.

And:

students[1].address.pin

means:

that student's address's PIN.

This pattern is extremely important

Real programs frequently have structures containing:

  • other structures

  • arrays

  • pointers

  • strings

  • configuration data

For example:

Employee
 ├── name
 ├── salary
 └── address
      ├── city
      └── country

How to read the important code

Read from left to right:

students[1].address.pin

as:

second student → address → PIN.

Key takeaway

Arrays organize multiple objects; structures organize the data belonging to each object.


Chapter 10 — union, enum, and typedef

This chapter contains three related C features.


Question

What will the following program print?

#include <stdio.h>

// Give names to integer constants.
enum Status
{
    OFF = 0,
    ON = 1
};

// Create a structure type using typedef.
typedef struct
{
    char name[20];
    enum Status status;
} Device;

// Create a union where members share the same memory.
union Data
{
    int number;
    float decimal;
};

int main(void)
{
    // Create a device using the typedef name.
    Device device = {"Sensor", ON};

    // Create a union.
    union Data data;

    // Store an integer in the union.
    data.number = 42;

    printf("%s\n", device.name);
    printf("%d\n", device.status);
    printf("%d\n", data.number);

    return 0;
}

Answer

Sensor
1
42

enum

An enumeration:

enum Status
{
    OFF = 0,
    ON = 1
};

creates named integer constants.

Instead of:

int status = 1;

we can write:

enum Status status = ON;

This makes the code easier to understand.

If values aren't explicitly specified, C normally assigns:

0, 1, 2, 3...

typedef

This:

typedef struct
{
    char name[20];
    enum Status status;
} Device;

creates an alias:

Device

for the anonymous structure type.

Without typedef, you might write:

struct Device device;

With this typedef:

Device device;

Important distinction

typedef does not create a completely new runtime type.

It creates another name for an existing type.


union

This is particularly important for understanding C's memory model.

union Data
{
    int number;
    float decimal;
};

Unlike a struct, the members of a union share the same storage.

Conceptually:

Structure

struct
┌──────────┐
│ int      │
├──────────┤
│ float    │
└──────────┘

Both members have separate storage.

Union

union
┌──────────┐
│ shared   │
│ storage  │
└──────────┘

The storage is reused by the members.

So:

data.number = 42;

stores the integer representation in the union's shared storage.

If you subsequently do:

data.decimal = 3.14f;

you overwrite that shared storage.

Beginner trap

Don't think:

union Data

means:

an object containing both an integer and a float simultaneously in separate storage.

It doesn't.

How to read the important code

Device device

means:

device is a variable using the structure type aliased as Device.

device.status

means:

access its status member.

Key takeaway

struct gives members separate storage; union makes members share storage; enum gives names to integer constants; typedef gives types convenient aliases.


Phase 3 — Master Picture

At the end of this phase, you should be able to mentally organize C data like this:

                    C DATA
                       │
          ┌────────────┴────────────┐
          │                         │
      Collections                Custom types
          │                         │
      ┌───┴────┐              ┌─────┼─────┐
      │        │              │     │     │
    Array     String         struct union enum
      │        │
      │        └── char[] + '\0'
      │
   ┌──┴─────────┐
   │            │
  1D           2D
   │            │
   └─────┬──────┘
         │
      Functions
         │
     array parameter
         │
      pointer
         ↓
      Phase 4

The most important concepts from Phase 3

ConceptWhat you should understand
ArrayCollection of same-type elements
IndexPosition beginning at 0
sizeofSize in bytes
2D arrayArray of arrays
Array parameterFunction receives access to array data
Character arrayArray of char
C stringCharacters ending in '\0'
strlenString length excluding '\0'
strcpyCopy a string
strcatAppend a string
strcmpCompare strings
fgetsSafer bounded line input
structGroups related fields
.Access structure member
Nested structStructure containing another structure
Array of structsMultiple structured objects
unionMembers share storage
enumNamed integer constants
typedefType alias

⚠️ Five Things You Should NOT Forget

1. Array indexes start at zero

int a[5];

valid:

a[0] a[1] a[2] a[3] a[4]

Not:

a[5]

2. C strings are not Java strings

There is no built-in:

String

type like Java.

Instead:

char name[] = "Alice";

is:

'A' 'l' 'i' 'c' 'e' '\0'

3. strlen and sizeof are different

For:

char name[] = "Alice";

typically:

strlen(name)  → 5
sizeof(name)  → 6

because sizeof includes the terminating '\0', while strlen doesn't.


4. Never forget buffer size

This is dangerous:

char name[10];

scanf("%s", name);

because the input may exceed the buffer.

C gives you tremendous control—but you are responsible for memory boundaries.


5. struct is the bridge toward real-world C

Once you combine:

struct
+
array
+
string
+
function

you can start representing real things:

Student
Employee
Book
Product
User
File
Network packet
Configuration
Database record

And when we combine those with pointers and dynamic memory in Phase 4, C becomes dramatically more powerful—and considerably more dangerous.

Phase 4 is where arrays, strings, structures, and memory all connect through pointers.

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.