CSE240 Module 3: Complete Study Guide

Pointers, Arrays, Strings & Structures in C

LO1: Develop C Programs with Pointers

LO1.1: The Basic Concept of a Pointer

A pointer is a variable that stores the memory address of another variable. Think of it like a house address that tells you where to find something.

int x = 42;      // regular variable storing value 42
int *ptr = &x;  // pointer storing address of x

// Two key operators:
// & (address-of): gets the address of a variable
// * (dereference): accesses the value at an address
x
Value: 42
Addr: 0x1000
ptr
Value: 0x1000
Addr: 0x2000
Pointer declaration: type *name; where 'type' is what the pointer points to. The * in declaration is different from * in expressions (dereference).

LO1.2: Pointer Operators and Tracing

int a = 10, b = 20;
int *p1 = &a;
int *p2 = &b;

// Trace these operations:
*p1 = 30;        // a becomes 30
p1 = p2;         // p1 now points to b
*p1 = 40;        // b becomes 40
a = *p2;         // a becomes 40
Operation a b p1 points to p2 points to
Initial 10 20 a b
*p1 = 30 30 20 a b
p1 = p2 30 20 b b
*p1 = 40 30 40 b b
a = *p2 40 40 b b

LO1.3: Pointers with Strings

char str[] = "Hello";     // array of characters
char *ptr = "World";      // pointer to string literal

// Key differences:
// str[] is modifiable, stored in stack
// *ptr points to read-only memory

str[0] = 'J';              // OK: now "Jello"
// ptr[0] = 'B';          // ERROR: can't modify literal

// String traversal with pointers:
char *p = str;
while (*p != '\0') {
    printf("%c ", *p);
    p++;                   // move to next character
}
String literals are stored in read-only memory. Always use char arrays if you need to modify the string!

LO1.4: Pointers with Multi-Dimensional Arrays

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

int (*ptr)[4] = matrix;   // pointer to array of 4 ints

// Accessing elements:
// matrix[i][j] == *(*(matrix + i) + j)
// ptr[i][j]    == *(*(ptr + i) + j)

printf("%d\n", ptr[1][2]);      // prints 7
printf("%d\n", *(*(ptr+1)+2)); // also prints 7
Array name is a constant pointer to the first element. For 2D arrays, it's a pointer to the first row (which is itself an array).

LO1.5: Pointers vs Unsigned Integers

// Pointers can be cast to/from integers, but it's dangerous!
int x = 100;
int *ptr = &x;
unsigned int addr = (unsigned int)ptr;  // store address as number

// Key differences:
// 1. Pointers have type information
// 2. Pointer arithmetic uses sizeof(type)
// 3. Pointers can be dereferenced
// 4. Size varies (32-bit vs 64-bit systems)

int arr[] = {10, 20, 30};
int *p = arr;
p++;        // moves by sizeof(int) bytes, not 1
            // now p points to arr[1]

LO2: Arrays of Strings

LO2.1: String Literal Limitations

// String literals are READ-ONLY!
char *str1 = "Hello";     // points to read-only memory
char str2[] = "Hello";    // creates modifiable copy

// str1[0] = 'J';         // CRASH! Segmentation fault
str2[0] = 'J';            // OK! str2 is now "Jello"

// Limitations:
// 1. Can't modify string literals
// 2. Multiple identical literals may share memory
// 3. Size is fixed at compile time

LO2.2: Array of Strings Implementation

// Method 1: 2D char array (fixed size for each string)
char names1[5][20] = {
    "Alice",
    "Bob",
    "Charlie",
    "David",
    "Eve"
};

// Method 2: Array of char pointers (variable sizes)
char *names2[5] = {
    "Alice",
    "Bob",
    "Charlie",
    "David",
    "Eve"
};

// Accessing strings:
printf("%s\n", names1[2]);    // prints "Charlie"
printf("%c\n", names2[1][0]); // prints 'B'

// Modifying:
strcpy(names1[0], "Alan");   // OK for 2D array
names2[0] = "Alan";          // OK: change pointer
// names2[0][0] = 'X';       // ERROR: literal is read-only
Memory Layout Comparison:
2D Array: Contiguous memory, each string gets 20 bytes
Pointer Array: Pointers in array, strings scattered in memory

2D Array wastes space but allows modification
Pointer Array saves space but strings are read-only

LO3: Defining New Types in C

LO3.1: Constants with #define and const

// Preprocessor macros (compile-time substitution)
#define MAX_SIZE 100
#define PI 3.14159
#define SQUARE(x) ((x) * (x))   // macro with parameter

// const keyword (runtime constant)
const int max_items = 50;
const double pi = 3.14159;

// Key differences:
// #define: no type checking, just text replacement
// const: type-safe, occupies memory, can't be modified

int array[MAX_SIZE];        // OK with #define
// int array2[max_items];   // ERROR in C (OK in C++)
Always use parentheses in macros! SQUARE(x+1) without parentheses would expand to x+1*x+1, not (x+1)*(x+1)

LO3.2: Typedef and Enumerations

// typedef: create alias for existing type
typedef unsigned long ulong;
typedef int bool;
typedef char String[100];    // array type

ulong bigNumber = 1000000;
String name;                 // same as char name[100]

// enum: create named integer constants
enum Days {
    MONDAY,      // 0
    TUESDAY,     // 1
    WEDNESDAY,   // 2
    THURSDAY,    // 3
    FRIDAY = 10, // 10 (custom value)
    SATURDAY,    // 11
    SUNDAY       // 12
};

enum Days today = MONDAY;

// typedef with enum for cleaner syntax
typedef enum {
    FALSE,
    TRUE
} Boolean;

Boolean isReady = FALSE;

LO3.3: Structures (Composite Data)

// Define a structure type
struct Person {
    char name[50];
    int age;
    float height;
    char email[100];
};

// Create structure variables
struct Person person1;
struct Person person2 = {"Alice", 25, 5.6, "alice@email.com"};

// Access members with dot operator
person1.age = 30;
strcpy(person1.name, "Bob");

// typedef for cleaner syntax
typedef struct {
    double x;
    double y;
} Point;

Point p1 = {3.5, 7.2};

// Structures with pointers
struct Person *ptr = &person1;
ptr->age = 31;              // arrow operator for pointer access
(*ptr).age = 31;            // equivalent using dereference

// Nested structures
typedef struct {
    Point center;
    double radius;
} Circle;

Circle c = {{0.0, 0.0}, 5.0};
Arrow operator (->): ptr->member is shorthand for (*ptr).member. Use it when accessing structure members through a pointer.

LO4: Complete Contact Manager Implementation

This demonstrates all concepts: flow control, arrays, pointers, structs, and I/O

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

#define MAX_CONTACTS 100
#define NAME_LENGTH 50
#define PHONE_LENGTH 20

// Structure definition
typedef struct {
    char name[NAME_LENGTH];
    char phone[PHONE_LENGTH];
    int id;
} Contact;

// Global array and counter
Contact contacts[MAX_CONTACTS];
int contactCount = 0;

// Function prototypes
void displayMenu();
void insertContact();
void searchContact();
void deleteContact();
void displayAll();

int main() {
    int choice;
    
    while (1) {
        displayMenu();
        printf("Enter choice: ");
        scanf("%d", &choice);
        getchar(); // consume newline
        
        switch (choice) {
            case 1:
                insertContact();
                break;
            case 2:
                searchContact();
                break;
            case 3:
                deleteContact();
                break;
            case 4:
                displayAll();
                break;
            case 5:
                printf("Exiting...\n");
                return 0;
            default:
                printf("Invalid choice!\n");
        }
    }
}

void displayMenu() {
    printf("\n=== Contact Manager ===\n");
    printf("1. Insert Contact\n");
    printf("2. Search Contact\n");
    printf("3. Delete Contact\n");
    printf("4. Display All\n");
    printf("5. Exit\n");
}

void insertContact() {
    if (contactCount >= MAX_CONTACTS) {
        printf("Contact list full!\n");
        return;
    }
    
    Contact *newContact = &contacts[contactCount];
    
    printf("Enter name: ");
    fgets(newContact->name, NAME_LENGTH, stdin);
    newContact->name[strcspn(newContact->name, "\n")] = '\0';
    
    printf("Enter phone: ");
    fgets(newContact->phone, PHONE_LENGTH, stdin);
    newContact->phone[strcspn(newContact->phone, "\n")] = '\0';
    
    newContact->id = contactCount + 1;
    contactCount++;
    
    printf("Contact added successfully! ID: %d\n", newContact->id);
}

void searchContact() {
    char searchName[NAME_LENGTH];
    printf("Enter name to search: ");
    fgets(searchName, NAME_LENGTH, stdin);
    searchName[strcspn(searchName, "\n")] = '\0';
    
    int found = 0;
    for (int i = 0; i < contactCount; i++) {
        if (strstr(contacts[i].name, searchName) != NULL) {
            printf("Found: ID=%d, Name=%s, Phone=%s\n", 
                   contacts[i].id, contacts[i].name, contacts[i].phone);
            found = 1;
        }
    }
    
    if (!found) {
        printf("No contacts found.\n");
    }
}

void deleteContact() {
    int id;
    printf("Enter ID to delete: ");
    scanf("%d", &id);
    getchar();
    
    int index = -1;
    for (int i = 0; i < contactCount; i++) {
        if (contacts[i].id == id) {
            index = i;
            break;
        }
    }
    
    if (index == -1) {
        printf("Contact not found.\n");
        return;
    }
    
    // Shift elements left to fill gap
    for (int i = index; i < contactCount - 1; i++) {
        contacts[i] = contacts[i + 1];
    }
    contactCount--;
    
    printf("Contact deleted successfully.\n");
}

void displayAll() {
    if (contactCount == 0) {
        printf("No contacts to display.\n");
        return;
    }
    
    printf("\n=== All Contacts ===\n");
    for (int i = 0; i < contactCount; i++) {
        printf("ID: %d | Name: %s | Phone: %s\n",
               contacts[i].id, contacts[i].name, contacts[i].phone);
    }
}

Practice Problems & Test Prep

Question 1: Pointer Basics

What will be the output of this code?

int x = 5, y = 10;
int *p = &x;
int *q = &y;
*p = *q;
q = p;
*q = 15;
printf("%d %d", x, y);
Answer: 15 10
Explanation:
1. *p = *q → x = 10 (copy value of y to x)
2. q = p → q now points to x
3. *q = 15 → x = 15 (q points to x)
4. Final: x = 15, y = 10

Question 2: String Pointers

Which of these will cause a runtime error?

A) char str[] = "Hello"; str[0] = 'J';
B) char *str = "Hello"; str = "World";
C) char *str = "Hello"; str[0] = 'J';
D) char str[10] = "Hello"; strcpy(str, "World");
Answer: C
Explanation: Option C tries to modify a string literal (read-only memory), causing segmentation fault. A is OK (array is modifiable), B is OK (changing pointer, not string), D is OK (copying to array).

Question 3: Structures

Fill in the blank to correctly access the x coordinate:

typedef struct {
    float x, y;
} Point;

typedef struct {
    Point center;
    float radius;
} Circle;

Circle c = {{3.0, 4.0}, 5.0};
Circle *ptr = &c;

// Access center's x coordinate through ptr:
float xCoord = _________;
Answer: ptr->center.x
Explanation: Use arrow operator for pointer, then dot for nested struct. Alternatives: (*ptr).center.x or c.center.x

Question 4: Array of Strings

What's the key difference between these declarations?

char names1[3][10];
char *names2[3];
Answer:
names1: 2D array, 30 bytes total, all strings modifiable, fixed size
names2: Array of pointers, variable string sizes, literals are read-only
Memory: names1 is contiguous, names2 has pointers pointing to scattered strings

Question 5: Pointer Arithmetic

Given an int array, what does ptr+3 mean?

int arr[] = {10, 20, 30, 40, 50};
int *ptr = arr;
Answer: ptr+3 points to arr[3] (value 40)
Explanation: Pointer arithmetic scales by sizeof(type). Since int is typically 4 bytes, ptr+3 advances by 3*4=12 bytes from the start of the array, reaching the 4th element.

Key Concepts to Remember

  • ✓ & gets address, * dereferences (accesses value)
  • ✓ Arrays decay to pointers when passed to functions
  • ✓ String literals are read-only, use char arrays for modification
  • ✓ Arrow operator (->) for pointer to struct member access
  • ✓ Pointer arithmetic scales by sizeof(pointed type)
  • ✓ typedef creates type aliases, enum creates named constants
  • ✓ #define is preprocessor substitution, const is runtime constant

Common Pitfalls to Avoid

1. Never modify string literals (char *str = "hello"; str[0] = 'H'; // CRASH!)
2. Always check array bounds - C doesn't do it for you
3. Initialize pointers before use (avoid wild pointers)
4. Remember strcpy needs destination with enough space
5. Use parentheses in macros: #define SQUARE(x) ((x)*(x))
6. Don't return pointers to local variables from functions