C Programming Basics: Part 6 – Arrays and Strings

Welcome to the sixth part of our C programming tutorial series! In previous articles, we’ve covered the fundamentals of C, variables, operators, control flow, and functions. Now, let’s explore arrays and strings, which allow you to work with collections of related data.

Understanding Arrays in C

An array is a collection of elements of the same data type, stored in contiguous memory locations. Think of an array as a row of storage boxes, where each box can hold one value, and all boxes are numbered sequentially.

flowchart TD
    A[Declare Array] --> B[Initialize Array]
    B --> C[Access Elements]
    
    C --> D[Direct Access]
    C --> E[Loop Through Array]
    C --> F[Add/Modify Elements]
    C --> G[Find/Search Elements]
    
    E --> E1[For Loop]
    E --> E2[While Loop]
    
    G --> G1[Linear Search]
    G --> G2[Binary Search for Sorted Arrays]
    
    F --> H[Modify Single Element]
    F --> I[Modify Multiple Elements]
    
    J[Array Manipulation] --> J1[Sort Array]
    J --> J2[Reverse Array]
    J --> J3[Merge Arrays]
    J --> J4[Filter Array]
    
    K[Advanced Operations] --> K1[Pass Arrays to Functions]
    K --> K2[Return Arrays from Functions]
    K --> K3[Dynamic Memory Allocation]
    
    classDef basic fill:#e1f5fe,stroke:#29b6f6,stroke-width:2px
    classDef operations fill:#e8f5e9,stroke:#66bb6a,stroke-width:2px
    classDef advanced fill:#fff3e0,stroke:#ffa726,stroke-width:2px
    
    class A,B,C basic
    class D,E,F,G,E1,E2,G1,G2,H,I operations
    class J,J1,J2,J3,J4,K,K1,K2,K3 advanced

Array Declaration and Initialization

Here’s how to declare an array:

C
data_type array_name[array_size];

For example, to declare an array of 5 integers:

C
int numbers[5];

You can initialize an array at declaration time:

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

If you provide all initial values, the array size is optional:

C
int numbers[] = {10, 20, 30, 40, 50};  // Size 5 is determined automatically

You can also initialize specific elements:

C
int numbers[5] = {0};  // Initializes all elements to 0
int marks[10] = {1, 2};  // First two elements are 1 and 2, rest are 0

Accessing Array Elements

Array elements are accessed using their index, which starts from 0:

C
int numbers[5] = {10, 20, 30, 40, 50};
printf("%d\n", numbers[0]);  // Outputs 10 (first element)
printf("%d\n", numbers[4]);  // Outputs 50 (last element)

Here’s a visual representation of an array in memory:

graph LR
    subgraph "Array numbers[5]"
    A[10<br>index 0] --- B[20<br>index 1] --- C[30<br>index 2] --- D[40<br>index 3] --- E[50<br>index 4]
    end

Modifying Array Elements

You can change array elements by assigning new values:

C
int numbers[5] = {10, 20, 30, 40, 50};
numbers[2] = 35;  // Change the third element to 35

Arrays and Loops

Arrays are often processed using loops:

C
#include <stdio.h>

int main() {
    int numbers[5] = {10, 20, 30, 40, 50};
    
    // Print all elements using a loop
    for (int i = 0; i < 5; i++) {
        printf("numbers[%d] = %d\n", i, numbers[i]);
    }
    
    // Calculate the sum of array elements
    int sum = 0;
    for (int i = 0; i < 5; i++) {
        sum += numbers[i];
    }
    printf("Sum: %d\n", sum);
    
    return 0;
}

Array Size and Memory

The total memory allocated for an array equals the size of one element multiplied by the number of elements. You can use the sizeof operator to determine this:

C
#include <stdio.h>

int main() {
    int numbers[5] = {10, 20, 30, 40, 50};
    
    printf("Size of int: %lu bytes\n", sizeof(int));
    printf("Size of array: %lu bytes\n", sizeof(numbers));
    printf("Number of elements: %lu\n", sizeof(numbers) / sizeof(int));
    
    return 0;
}

Passing Arrays to Functions

When passing an array to a function, you’re actually passing a pointer to the first element. This means the function can modify the original array:

C
#include <stdio.h>

// Function prototype - both are equivalent
void printArray(int arr[], int size);
// void printArray(int *arr, int size);

// Function to double each element of an array
void doubleElements(int arr[], int size);

int main() {
    int numbers[5] = {1, 2, 3, 4, 5};
    
    printf("Original array:\n");
    printArray(numbers, 5);
    
    doubleElements(numbers, 5);
    
    printf("Modified array:\n");
    printArray(numbers, 5);
    
    return 0;
}

void printArray(int arr[], int size) {
    for (int i = 0; i < size; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
}

void doubleElements(int arr[], int size) {
    for (int i = 0; i < size; i++) {
        arr[i] *= 2;  // Modify the original array
    }
}

It’s important to pass the array size as a parameter because C doesn’t track array sizes internally.

Returning Arrays from Functions

C doesn’t allow you to directly return an array from a function. Instead, you can:

  1. Pass an existing array to be modified
  2. Return a pointer to a static array
  3. Dynamically allocate an array and return its pointer

Here’s an approach using a static array:

C
#include <stdio.h>

// Function returning a pointer to a static array
int* getEvenNumbers(int n) {
    static int evens[10];  // Static array persists after function returns
    
    for (int i = 0; i < n && i < 10; i++) {
        evens[i] = (i + 1) * 2;
    }
    
    return evens;
}

int main() {
    int* evenNumbers = getEvenNumbers(5);
    
    printf("First 5 even numbers:\n");
    for (int i = 0; i < 5; i++) {
        printf("%d ", evenNumbers[i]);
    }
    printf("\n");
    
    return 0;
}

Note: Static arrays have limitations in size and scope, and more complex programs typically use dynamic memory allocation instead.

Multidimensional Arrays

C supports multidimensional arrays – arrays of arrays. The most common are 2D arrays, which you can think of as tables or matrices.

Declaring and Initializing 2D Arrays

C
// Declaration
data_type array_name[rows][columns];

// Example: 3x4 array of integers
int matrix[3][4];

// Initialization
int matrix[3][4] = {
    {1, 2, 3, 4},    // Row 0
    {5, 6, 7, 8},    // Row 1
    {9, 10, 11, 12}  // Row 2
};

Here’s a visual representation of a 2D array:

2D Array: int matrix[3][4] Row 0 Row 1 Row 2 Col 0 Col 1 Col 2 Col 3 1 [0][0] 2 [0][1] 3 [0][2] 4 [0][3] 5 [1][0] 6 [1][1] 7 [1][2] 8 [1][3] 9 [2][0] 10 [2][1] 11 [2][2] 12 [2][3]

Accessing 2D Array Elements

Elements are accessed using row and column indices:

C
int value = matrix[1][2];  // Gets 7 (row 1, column 2)
matrix[0][3] = 44;         // Sets the element in row 0, column 3 to 44

Processing 2D Arrays with Nested Loops

To process all elements in a 2D array, we typically use nested loops:

C
#include <stdio.h>

int main() {
    int matrix[3][4] = {
        {1, 2, 3, 4},
        {5, 6, 7, 8},
        {9, 10, 11, 12}
    };
    
    // Print all elements
    for (int i = 0; i < 3; i++) {        // Outer loop for rows
        for (int j = 0; j < 4; j++) {    // Inner loop for columns
            printf("%3d ", matrix[i][j]);
        }
        printf("\n");  // New line after each row
    }
    
    return 0;
}

Passing 2D Arrays to Functions

When passing a 2D array to a function, you must specify the column size:

C
void processMatrix(int mat[][4], int rows);  // Column size (4) is required

// Usage:
processMatrix(matrix, 3);

Here’s a complete example:

C
#include <stdio.h>

// Function prototype
void printMatrix(int mat[][4], int rows);
void doubleMatrix(int mat[][4], int rows);

int main() {
    int matrix[3][4] = {
        {1, 2, 3, 4},
        {5, 6, 7, 8},
        {9, 10, 11, 12}
    };
    
    printf("Original matrix:\n");
    printMatrix(matrix, 3);
    
    doubleMatrix(matrix, 3);
    
    printf("\nModified matrix:\n");
    printMatrix(matrix, 3);
    
    return 0;
}

void printMatrix(int mat[][4], int rows) {
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < 4; j++) {
            printf("%3d ", mat[i][j]);
        }
        printf("\n");
    }
}

void doubleMatrix(int mat[][4], int rows) {
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < 4; j++) {
            mat[i][j] *= 2;
        }
    }
}

Higher-Dimensional Arrays

C also supports arrays with more than two dimensions:

C
// 3D array (2 layers, 3 rows, 4 columns)
int cube[2][3][4] = {
    {  // First layer
        {1, 2, 3, 4},
        {5, 6, 7, 8},
        {9, 10, 11, 12}
    },
    {  // Second layer
        {13, 14, 15, 16},
        {17, 18, 19, 20},
        {21, 22, 23, 24}
    }
};

// Access element (layer 1, row 2, column 3)
int value = cube[1][2][3];  // Gets 24

Strings in C

In C, strings are arrays of characters terminated by a null character (\0). This null terminator marks the end of the string and is automatically added when you use string literals.

Declaring and Initializing Strings

There are several ways to declare strings:

C
// String literal
char greeting[] = "Hello";  // Compiler adds the null terminator

// Equivalent to:
char greeting[] = {'H', 'e', 'l', 'l', 'o', '\0'};

// With explicit size (must be large enough for characters + null terminator)
char message[10] = "Hello";  // Remaining positions are filled with '\0'

The string "Hello" is stored in memory as:

String in Memory: char greeting[] = “Hello”; H greeting[0] e greeting[1] l greeting[2] l greeting[3] o greeting[4] \0 greeting[5] 0 1 2 3 4 5

String Input and Output

You can use various functions to read and write strings:

C
#include <stdio.h>

int main() {
    char name[50];
    
    printf("Enter your name: ");
    
    // Method 1: Using scanf (stops at whitespace)
    // scanf("%s", name);  // No & operator needed for strings
    
    // Method 2: Using fgets (better for full lines with spaces)
    fgets(name, 50, stdin);
    
    printf("Hello, %s!\n", name);
    return 0;
}

Note: When using fgets, the newline character from pressing Enter is included in the string. You may want to remove it:

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

int main() {
    char name[50];
    
    printf("Enter your name: ");
    fgets(name, 50, stdin);
    
    // Remove the newline character
    name[strcspn(name, "\n")] = '\0';
    
    printf("Hello, %s!\n", name);
    return 0;
}

String Library Functions

The <string.h> header provides many useful functions for string manipulation:

1. String Length – strlen()

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

int main() {
    char text[] = "Hello, World!";
    int length = strlen(text);  // Returns 13
    
    printf("Length of '%s': %d\n", text, length);
    return 0;
}

2. String Copy – strcpy() and strncpy()

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

int main() {
    char source[] = "Hello";
    char destination[10];
    
    // Copy the string
    strcpy(destination, source);
    
    printf("Source: %s\n", source);
    printf("Destination: %s\n", destination);
    
    // Safer version with size limit
    char limited[4];
    strncpy(limited, source, sizeof(limited) - 1);
    limited[sizeof(limited) - 1] = '\0';  // Ensure null-termination
    
    printf("Limited: %s\n", limited);
    
    return 0;
}

3. String Concatenation – strcat() and strncat()

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

int main() {
    char first[20] = "Hello, ";
    char last[] = "World!";
    
    // Concatenate strings
    strcat(first, last);
    
    printf("Concatenated: %s\n", first);
    
    return 0;
}

4. String Comparison – strcmp() and strncmp()

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

int main() {
    char str1[] = "apple";
    char str2[] = "banana";
    char str3[] = "apple";
    
    int result1 = strcmp(str1, str2);  // Negative (a comes before b)
    int result2 = strcmp(str1, str3);  // Zero (identical strings)
    int result3 = strcmp(str2, str1);  // Positive (b comes after a)
    
    printf("strcmp(apple, banana): %d\n", result1);
    printf("strcmp(apple, apple): %d\n", result2);
    printf("strcmp(banana, apple): %d\n", result3);
    
    return 0;
}

5. String Searching – strchr() and strstr()

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

int main() {
    char text[] = "The quick brown fox jumps over the lazy dog";
    
    // Find first occurrence of a character
    char *result1 = strchr(text, 'q');
    if (result1) {
        printf("Found 'q' at position: %ld\n", result1 - text);
    }
    
    // Find a substring
    char *result2 = strstr(text, "fox");
    if (result2) {
        printf("Found 'fox' at position: %ld\n", result2 - text);
    }
    
    return 0;
}

Common String Operations

Let’s look at some common string operations:

Converting Between Characters and Integers

Each character in C is represented by an ASCII value:

C
#include <stdio.h>

int main() {
    char ch = 'A';
    int ascii = (int)ch;  // Get ASCII value of 'A' (65)
    
    printf("Character: %c, ASCII: %d\n", ch, ascii);
    
    // Converting between uppercase and lowercase
    char lower = 'a';
    char upper = lower - 32;  // 'A' (ASCII 65) is 32 less than 'a' (ASCII 97)
    
    printf("Lowercase: %c, Uppercase: %c\n", lower, upper);
    
    return 0;
}

Checking Character Types

The <ctype.h> header provides functions for checking character types:

C
#include <stdio.h>
#include <ctype.h>

int main() {
    char ch = 'A';
    
    if (isalpha(ch)) {
        printf("%c is an alphabetic character\n", ch);
    }
    
    if (isupper(ch)) {
        printf("%c is uppercase\n", ch);
    }
    
    if (isdigit('5')) {
        printf("5 is a digit\n");
    }
    
    if (isspace(' ')) {
        printf("Space is a whitespace character\n");
    }
    
    // Converting case
    printf("Lowercase of %c is %c\n", ch, tolower(ch));
    printf("Uppercase of %c is %c\n", 'b', toupper('b'));
    
    return 0;
}

Parsing Integers from Strings

C
#include <stdio.h>
#include <stdlib.h>

int main() {
    char num_str[] = "12345";
    int num = atoi(num_str);  // Convert string to integer
    
    printf("String: %s, Integer: %d\n", num_str, num);
    printf("Integer + 5: %d\n", num + 5);
    
    return 0;
}

Array vs String Comparison in C

Arrays vs Strings in C

Feature Arrays Strings
Definition Collection of elements of any data type Array of characters with null terminator (\0)
Declaration int numbers[5]; char name[20];
Initialization int nums[] = {1, 2, 3, 4, 5}; char name[] = "John";
char name[] = {'J', 'o', 'h', 'n', '\0'};
Termination No special terminator Null character (\0) required
Length Determined at compile time or by memory allocation Determined by position of null terminator, use strlen()
Library Support No dedicated library Extensive via <string.h>
Memory Model Contiguous memory location for elements Contiguous memory location for characters
Direct Assignment Not possible after declaration Not possible after declaration, use strcpy()
Comparison Element by element comparison Use strcmp() or strncmp()
Input/Output No direct I/O, element-by-element Direct with %s format specifier, gets(), fgets()
Size Calculation sizeof(array) / sizeof(array[0]) strlen(str) (excludes null terminator)

Practical Example: String Analysis

Let’s create a program that analyzes a string, counting various character types:

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

void analyzeString(const char *str);

int main() {
    char text[100];
    
    printf("Enter a string to analyze: ");
    fgets(text, sizeof(text), stdin);
    
    // Remove newline if present
    text[strcspn(text, "\n")] = '\0';
    
    analyzeString(text);
    
    return 0;
}

void analyzeString(const char *str) {
    int length = strlen(str);
    int letters = 0, digits = 0, spaces = 0, other = 0;
    int uppercase = 0, lowercase = 0;
    
    for (int i = 0; i < length; i++) {
        if (isalpha(str[i])) {
            letters++;
            if (isupper(str[i])) {
                uppercase++;
            } else {
                lowercase++;
            }
        } else if (isdigit(str[i])) {
            digits++;
        } else if (isspace(str[i])) {
            spaces++;
        } else {
            other++;
        }
    }
    
    printf("\nString Analysis Results\n");
    printf("======================\n");
    printf("Total length: %d\n", length);
    printf("Letters: %d (%.1f%%)\n", letters, 100.0 * letters / length);
    printf("  - Uppercase: %d\n", uppercase);
    printf("  - Lowercase: %d\n", lowercase);
    printf("Digits: %d (%.1f%%)\n", digits, 100.0 * digits / length);
    printf("Spaces: %d (%.1f%%)\n", spaces, 100.0 * spaces / length);
    printf("Other characters: %d (%.1f%%)\n", other, 100.0 * other / length);
}

String Analyzer

String Analyzer

Total Length 0
Letters 0
Uppercase 0
Lowercase 0
Digits 0
Spaces 0

Character Distribution

Letters 0%
Digits 0%
Spaces 0%
Special 0%

Array of Strings

To store multiple strings, you can use an array of strings, which is essentially a 2D array of characters:

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

int main() {
    // Array of 5 strings, each with maximum length of 20
    char names[5][20] = {
        "Alice",
        "Bob",
        "Charlie",
        "David",
        "Eve"
    };
    
    // Print all names
    printf("Names:\n");
    for (int i = 0; i < 5; i++) {
        printf("%d. %s\n", i + 1, names[i]);
    }
    
    // Modify a name
    strcpy(names[2], "Chris");
    
    // Print the modified array
    printf("\nAfter modification:\n");
    for (int i = 0; i < 5; i++) {
        printf("%d. %s\n", i + 1, names[i]);
    }
    
    // Find the longest name
    int longest_index = 0;
    for (int i = 1; i < 5; i++) {
        if (strlen(names[i]) > strlen(names[longest_index])) {
            longest_index = i;
        }
    }
    
    printf("\nLongest name: %s (%lu characters)\n", 
           names[longest_index], strlen(names[longest_index]));
    
    return 0;
}

Interactive Name List

Names

#Name

Modify a Name

Find Longest Name

Common Array and String Pitfalls

Working with arrays and strings in C has several potential pitfalls:

1. Array Bounds Violations

C doesn't check if you access elements outside the array's bounds:

C
int numbers[5] = {1, 2, 3, 4, 5};
numbers[10] = 100;  // Undefined behavior! Beyond array bounds

This can lead to unpredictable behavior or crashes. Always ensure index values are within bounds.

Array Bounds Visualization

Array Bounds Visualization

#include <stdio.h>

int main() {
  int numbers[5] = {10, 20, 30, 40, 50};
  
  numbers[0] = 100; // Set value at index
  printf("Value: %d\n", numbers[0]);

  return 0;
}
Warning: In real C programs, accessing array elements outside the bounds (indexes 0-4 for a 5-element array) can lead to undefined behavior, including crashes, data corruption, or security vulnerabilities. This demo illustrates the concept for educational purposes only.
Ready to start...

2. Missing Null Terminator in Strings

Strings must end with a null character ('\0'):

C
char name[4] = {'J', 'o', 'h', 'n'};  // Missing '\0'
printf("%s", name);  // Undefined behavior! May print garbage characters

Correct version:

C
char name[5] = {'J', 'o', 'h', 'n', '\0'};
// or
char name[5] = "John";

3. Buffer Overflow in String Functions

Many standard string functions don't check buffer sizes:

C
char small[5];
strcpy(small, "This is too long");  // Buffer overflow!

Use safer functions or check lengths:

C
char small[5];
strncpy(small, "Long", sizeof(small) - 1);
small[sizeof(small) - 1] = '\0';  // Ensure null-termination

4. Forgetting That Arrays Are Zero-Indexed

The first element is at index 0, not 1:

C
int scores[5] = {90, 85, 95, 80, 88};
int first = scores[0];  // First element (90)
int last = scores[4];   // Last element (88) - not scores[5]!

5. Not Accounting for the Null Terminator When Allocating Strings

Always allocate one more byte than the string's visible length:

C
// For a 5-character string "Hello", allocate at least 6 bytes
char greeting[6] = "Hello";

Advanced Topics

Dynamic Arrays (Using Pointers and Memory Allocation)

For arrays whose size is determined at runtime:

C
#include <stdio.h>
#include <stdlib.h>

int main() {
    int size;
    printf("Enter the number of elements: ");
    scanf("%d", &size);
    
    // Allocate memory for the array
    int *dynamic_array = (int*)malloc(size * sizeof(int));
    
    if (dynamic_array == NULL) {
        printf("Memory allocation failed\n");
        return 1;
    }
    
    // Use the array
    for (int i = 0; i < size; i++) {
        dynamic_array[i] = i * 10;
    }
    
    // Print the array
    for (int i = 0; i < size; i++) {
        printf("%d ", dynamic_array[i]);
    }
    printf("\n");
    
    // Free the allocated memory when done
    free(dynamic_array);
    
    return 0;
}

String Tokenization

Breaking a string into tokens (separated by delimiters):

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

int main() {
    char sentence[] = "This is a sample sentence.";
    const char delimiters[] = " ,.";
    
    // Get the first token
    char *token = strtok(sentence, delimiters);
    
    // Walk through other tokens
    while (token != NULL) {
        printf("Token: %s\n", token);
        token = strtok(NULL, delimiters);
    }
    
    return 0;
}

String Tokenization

String Tokenization Visualization

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

int main() {
  char sentence[] = "This is a sample sentence.";
  const char delimiters[] = " ,.";

  // Get the first token
  char *token = strtok(sentence, delimiters);

  // Walk through other tokens
  while (token != NULL) {
    printf("Token: %s\n", token);
    token = strtok(NULL, delimiters);
  }

  return 0;
}

Tokens:

Ready to tokenize...

Conclusion

Arrays and strings are fundamental data structures in C programming. They allow you to work with collections of data efficiently, making them essential for almost any non-trivial program. Understanding how to manipulate arrays and strings properly will make you a much more effective C programmer.

In this article, we've covered:

  • Array declaration and initialization
  • Accessing and modifying array elements
  • Passing arrays to functions
  • Multidimensional arrays
  • String basics and null termination
  • String input/output and manipulation
  • Common string library functions
  • Array of strings
  • Common pitfalls and best practices
  • Advanced techniques

In the next part of our series, we'll explore structures and unions in C, which allow you to create custom data types for complex information.

Practice Exercises

  1. Write a program that finds the minimum and maximum values in an array of integers.
  2. Create a program that checks if a string is a palindrome (reads the same forwards and backwards).
  3. Implement a function that reverses the elements of an array.
  4. Write a program that counts the occurrences of each word in a sentence.
  5. Create a function that removes all whitespace from a string.
  6. Implement your own version of the strlen() function without using any standard library functions.
  7. Write a program that takes a sentence as input and prints each word on a new line in reverse order.

Happy coding!

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top