CSC115 C Programming

C ProgrammingUnit 612 min read

Arrays in C – Definitions, Types, Operations, Pointer Relations & Common Algorithms

Unit 6 of C Programming provides a comprehensive note on arrays, covering one‑ and multi‑dimensional arrays, memory layout, pointer relationship, common manipulations (input, output, sum, average, sorting, matrix operations) and typical exam‑style programs.

Key points

  • An array is a contiguous block of memory holding elements of the same data type, accessed by index.
  • One‑dimensional and multi‑dimensional arrays differ only in how the index calculation is performed.
  • The name of an array is a constant pointer to its first element, enabling pointer arithmetic.
  • Common array algorithms (sum, average, second largest, sorting, matrix addition, transpose) are frequently asked in TU exams.
  • Proper bounds checking, initialization and use of `sizeof` prevent common runtime errors.

1. What is an Array?

An array in C is a collection of objects of the same type stored in contiguous memory locations. The compiler allocates a fixed amount of memory at compile time (for static arrays) or at run time (for dynamic arrays using malloc).

int scores[5];          // 5 integers, indices 0 … 4
char name[20];          // 20 characters, indices 0 … 19
float matrix[3][3];     // 3×3 float matrix, total 9 elements

Benefits of using arrays

Benefit Explanation
Random access Any element can be accessed directly using its index, O(1) time.
Compact storage No extra pointers are needed; memory is contiguous, improving cache performance.
Ease of iteration Loops can process all elements uniformly.
Facilitates algorithms Sorting, searching, matrix arithmetic become straightforward.
Interoperability Many library functions (e.g., printf, scanf, qsort) expect arrays.

2. Memory Layout and Index Calculation

For a one‑dimensional array T a[N], the address of a[i] is:

For a two‑dimensional array T a[R][C] (row‑major order in C):

Thus, a 2‑D array is stored as a single linear block; the compiler performs the index arithmetic automatically.

3. Declaring, Initializing, and Accessing Arrays

3.1 Static Initialization

int primes[5] = {2, 3, 5, 7, 11};
char vowel[]   = {'a','e','i','o','u'};   // size inferred as 5
float zeros[4] = {0.0};                  // first element 0.0, rest also 0.0

If fewer initializers are supplied than the declared size, the remaining elements are zero‑initialized.

3.2 Dynamic Allocation

int *dyn = malloc(10 * sizeof(int));   // space for 10 integers
if (!dyn) { perror("malloc"); exit(EXIT_FAILURE); }

Remember to free(dyn) when done.

3.3 Input & Output

for (int i = 0; i < 5; ++i) {
    printf("Enter element %d: ", i);
    scanf("%d", &arr[i]);
}

printf("%d ", arr[i]); prints each element.

4. One‑Dimensional vs Two‑Dimensional Arrays

Feature One‑Dimensional Array Two‑Dimensional Array
Syntax type name[size]; type name[rows][cols];
Indexing Single index a[i] Double index a[i][j]
Memory Linear block of size elements Linear block of rows*cols elements, accessed row‑wise
Typical Use Lists, vectors, queues Matrices, tables, image pixels
Example int ages[500]; int matrix[3][3];

5. Relationship Between Arrays and Pointers

  • The array name (e.g., arr) decays to a pointer to its first element (&arr[0]) in most expressions.
  • Pointer arithmetic on this decayed pointer yields the same addresses as array indexing.
int a[5] = {10,20,30,40,50};
int *p = a;               // same as int *p = &a[0];
printf("%d %d\n", *(p+2), a[2]);   // both print 30

5.1 Pointer and One‑Dimensional Array Example

void modify(int *ptr, int n) {
    for (int i = 0; i < n; ++i)
        *(ptr + i) = *(ptr + i) * 2;   // double each element
}
int main(void) {
    int data[4] = {1,2,3,4};
    modify(data, 4);   // data decays to pointer
    // data now holds {2,4,6,8}
}

6. Call‑by‑Value vs Call‑by‑Reference

Aspect Call‑by‑Value Call‑by‑Reference
Argument passing Copies the actual value into the parameter. Passes the address (pointer) of the argument.
Effect on original variable No change to caller’s variable. Caller’s variable can be modified.
Typical use with arrays Arrays automatically decay to pointers → effectively call‑by‑reference. Explicit pointer parameters for single variables.
Example void f(int x){ x = 5; } – original unchanged. void g(int *p){ *p = 5; } – original becomes 5.

Program demonstrating both concepts

#include <stdio.h>

void byValue(int x) {          // copy of x
    x = x + 10;
    printf("Inside byValue: %d\n", x);
}

void byReference(int *p) {     // address of original
    *p = *p + 10;
    printf("Inside byReference: %d\n", *p);
}

int main(void) {
    int a = 5;
    printf("Original a: %d\n", a);
    byValue(a);                // a remains 5
    printf("After byValue: %d\n", a);
    byReference(&a);           // a becomes 15
    printf("After byReference: %d\n", a);
    return 0;
}

7. Common Array Algorithms

7.1 Sum and Average of N Numbers

#define N 10
int main(void) {
    int arr[N];
    int sum = 0;
    for (int i = 0; i < N; ++i) {
        scanf("%d", &arr[i]);
        sum += arr[i];
    }
    double avg = (double)sum / N;
    printf("Sum = %d, Average = %.2f\n", sum, avg);
    return 0;
}

7.2 Second Largest Element

int secondLargest(int *a, int n) {
    int largest = INT_MIN, second = INT_MIN;
    for (int i = 0; i < n; ++i) {
        if (a[i] > largest) {
            second = largest;
            largest = a[i];
        } else if (a[i] > second && a[i] != largest) {
            second = a[i];
        }
    }
    return second;
}

7.3 Sorting an Array (Ascending) – Simple Bubble Sort

void bubbleSort(int *a, int n) {
    for (int i = 0; i < n-1; ++i)
        for (int j = 0; j < n-i-1; ++j)
            if (a[j] > a[j+1]) {
                int tmp = a[j];
                a[j] = a[j+1];
                a[j+1] = tmp;
            }
}

7.4 Matrix Addition

#define ROW 3
#define COL 3
void addMatrices(int A[ROW][COL], int B[ROW][COL], int C[ROW][COL]) {
    for (int i = 0; i < ROW; ++i)
        for (int j = 0; j < COL; ++j)
            C[i][j] = A[i][j] + B[i][j];
}

Full program (adds two 3×3 matrices and prints result)

#include <stdio.h>
#define ROW 3
#define COL 3

int main(void) {
    int A[ROW][COL], B[ROW][COL], C[ROW][COL];

    printf("Enter elements of first matrix (3x3):\n");
    for (int i = 0; i < ROW; ++i)
        for (int j = 0; j < COL; ++j)
            scanf("%d", &A[i][j]);

    printf("Enter elements of second matrix (3x3):\n");
    for (int i = 0; i < ROW; ++i)
        for (int j = 0; j < COL; ++j)
            scanf("%d", &B[i][j]);

    // addition
    for (int i = 0; i < ROW; ++i)
        for (int j = 0; j < COL; ++j)
            C[i][j] = A[i][j] + B[i][j];

    printf("Resultant matrix:\n");
    for (int i = 0; i < ROW; ++i) {
        for (int j = 0; j < COL; ++j)
            printf("%4d", C[i][j]);
        printf("\n");
    }
    return 0;
}

7.5 Transpose of a Matrix

void transpose(int src[ROW][COL], int dest[COL][ROW]) {
    for (int i = 0; i < ROW; ++i)
        for (int j = 0; j < COL; ++j)
            dest[j][i] = src[i][j];
}

Worked trace (3×2 matrix)

src =  [ 1 2 ]
       [ 3 4 ]
       [ 5 6 ]

After transpose (2×3):
dest = [ 1 3 5 ]
       [ 2 4 6 ]

8. Practical Example: Age Statistics of 500 Persons

#include <stdio.h>
#define PERSONS 500

int main(void) {
    int age[PERSONS];
    long sum = 0;
    int count_25_30 = 0;

    for (int i = 0; i < PERSONS; ++i) {
        scanf("%d", &age[i]);
        sum += age[i];
        if (age[i] >= 25 && age[i] <= 30)
            ++count_25_30;
    }

    double avg = (double)sum / PERSONS;
    printf("Average age = %.2f\n", avg);
    printf("Number of persons aged 25‑30 = %d\n", count_25_30);
    return 0;
}

9. Advantages, Disadvantages, and Typical Applications

Aspect Advantages Disadvantages Typical Applications
Static arrays Compile‑time size, no runtime overhead, fast access. Fixed size; cannot grow/shrink. Fixed‑size buffers, lookup tables, embedded systems.
Dynamic arrays (malloc) Size decided at run time, can be large. Need explicit free; fragmentation risk. Large data sets, user‑defined size structures, runtime matrices.
Multi‑dimensional arrays Natural representation of matrices, images, grids. Index calculation can be error‑prone; large stack usage if declared locally. Scientific computing, graphics, game boards.
Array‑pointer equivalence Enables generic functions (void *, qsort). Misunderstanding can lead to off‑by‑one bugs. Library APIs, generic algorithms.

10. Common Pitfalls and How to Avoid Them

  1. Off‑by‑one errors – Remember that valid indices are 0 to size‑1.
  2. Uninitialized elements – Static arrays without explicit initializer contain indeterminate values; always initialize.
  3. Array‑pointer decay confusion – sizeof(arr) gives total size, while sizeof(ptr) gives pointer size. Use sizeof(arr)/sizeof(arr[0]) for element count.
  4. Boundary checks – Never read/write beyond allocated bounds; use loops that respect the declared size.
  5. Mixing row‑major vs column‑major – C uses row‑major; transposition code must swap indices accordingly.

11. Summary of Key Functions Used

Function Purpose Prototype
scanf Input from console int scanf(const char *fmt, …);
printf Output to console int printf(const char *fmt, …);
malloc Dynamic memory allocation void *malloc(size_t size);
free Release dynamic memory void free(void *ptr);
qsort Generic sorting (optional) void qsort(void *base, size_t nitems, size_t size, int (*compar)(const void *, const void *));

12. Worked Example: Full Program Combining Several Concepts

The following program reads 10 integers, prints the sum, average, second largest, and then sorts the array in ascending order.

#include <stdio.h>
#include <limits.h>

#define N 10

int secondLargest(int *a, int n) {
    int largest = INT_MIN, second = INT_MIN;
    for (int i = 0; i < n; ++i) {
        if (a[i] > largest) {
            second = largest;
            largest = a[i];
        } else if (a[i] > second && a[i] != largest) {
            second = a[i];
        }
    }
    return second;
}

void bubbleSort(int *a, int n) {
    for (int i = 0; i < n-1; ++i)
        for (int j = 0; j < n-i-1; ++j)
            if (a[j] > a[j+1]) {
                int tmp = a[j];
                a[j] = a[j+1];
                a[j+1] = tmp;
            }
}

int main(void) {
    int arr[N];
    int sum = 0;

    printf("Enter %d integers:\n", N);
    for (int i = 0; i < N; ++i) {
        scanf("%d", &arr[i]);
        sum += arr[i];
    }

    double avg = (double)sum / N;
    int sec = secondLargest(arr, N);
    bubbleSort(arr, N);

    printf("\nSum = %d\n", sum);
    printf("Average = %.2f\n", avg);
    printf("Second largest = %d\n", sec);
    printf("Sorted array: ");
    for (int i = 0; i < N; ++i)
        printf("%d ", arr[i]);
    printf("\n");
    return 0;
}

Trace of execution (sample input)

Input: 5 12 7 3 9 20 15 8 11 4
Sum = 94
Average = 9.40
Second largest = 15
Sorted array: 3 4 5 7 8 9 11 12 15 20

13. Frequently Asked Questions (FAQ)

  • Q: Can I pass a whole 2‑D array to a function?
    A: Yes, but you must specify the second dimension (or use a pointer to an array). Example: void func(int a[][COL], int rows);

  • Q: What is the difference between int a[10]; and int *a = malloc(10*sizeof(int));?
    A: The first is a static array allocated on the stack with compile‑time size; the second is a dynamic array on the heap, size decided at run time, and must be freed.

  • Q: Why does sizeof(arr) give a different result inside a function?
    A: Inside a function, arr decays to a pointer, so sizeof(arr) yields the size of the pointer (typically 4 or 8 bytes), not the total array size.

14. Best Practices

  1. Always use constants for sizes (#define MAX 500 or const int MAX = 500;) to avoid magic numbers.
  2. Prefer size_t for loop counters when dealing with sizeof.
  3. Initialize arrays at declaration when possible.
  4. Encapsulate repeated logic (e.g., input, printing) into functions to keep main clean.
  5. Use const qualifier for arrays that should not be modified, especially when passing to functions.

Exam tip

  • Read the question carefully: Most TU exam items ask for a specific operation (e.g., “find transpose”, “second largest”). Write only the required code; extra functions may cost marks.
  • Show array declaration and size explicitly; examiners often look for correct syntax (int a[5][5];).
  • Include a brief comment indicating what each loop does; it demonstrates understanding and can earn partial credit even if a minor syntax error occurs.
  • Remember pointer decay: When a function expects a pointer, you can pass the array name directly. If the question asks to “discuss relationship”, write the formula array[i] == *(array + i).
  • Edge cases: For algorithms like second largest, handle duplicate maximum values; a simple INT_MIN initialization avoids undefined behavior.
  • Time management: Allocate ~2 minutes per short program (input‑output, sum/average) and ~5 minutes for multi‑step tasks (matrix addition, transpose). Write clean, indented code to avoid syntax mistakes that cost marks.

Based on the TU BSc CSIT syllabus for C Programming (CSC115), unit 6.

Discussion

Loading…