Evanalysis
0.1Embedded interactionEstimated reading time: 11 min

0.1 Pointers, memory, and structs

Review the C tools that the data-structure course assumes: addresses, dereferencing, heap allocation, typedef, and struct layout.

Course contents

CSCI2520: Data structures

Structured notes for CSCI2520 data-structure foundations with operation-level reasoning and selective interactive demonstrations.

Chapter 0Programming foundations1 sections
Chapter 2Lists and recursion1 sections
Chapter 4Trees and BSTs1 sections

CSCI2520 does not require you to love C as a language, but it does require you to read C examples accurately. The course uses C because pointers, heap allocation, and low-level data layout make data-structure behavior visible in a way that high-level languages often hide.

This note therefore has one narrow purpose: make the core C toolkit readable enough that later stack, queue, and hash-table code does not feel like magic.

Why this review matters in a data-structure course

The local tutorial material says the course does not emphasize any one programming language. That is true at the assessment level. But the lecture and tutorial slides still rely on C examples to explain:

  • what a pointer stores,
  • how a node is allocated,
  • how a structure groups fields,
  • why ADT implementations hide representation details.

If you misread any of those, you do not merely get a syntax error. You lose the ability to see what the data structure is doing in memory.

Pointers store addresses, not values

Definition

Pointer

A pointer variable stores the address of another object.

If p is a pointer to an integer, then p stores where that integer lives in memory, not the integer value itself.

The tutorial review distinguishes two operators:

  • &x means “the address of x”;
  • p*p means “the value stored at the address inside p”.

Those two ideas must not be blurred together. A pointer is not the same thing as the object it points to.

Worked example

Read a pointer trace line by line

Consider the tutorial-style code:

int firstvalue = 5, secondvalue = 15;
int *p1, *p2;

p1 = &firstvalue;
p2 = &secondvalue;
*p1 = 10;
*p2 = *p1;
p1 = p2;
*p1 = 20;

Read it carefully.

  1. p1 first points to firstvalue, and p2 points to secondvalue.
  2. p1=10*p1 = 10 changes firstvalue to 10.
  3. p2=p1*p2 = *p1 copies the value 10 into secondvalue.
  4. p1=p2p1 = p2 does not copy an integer; it changes where p1 points.
  5. p1=20*p1 = 20 now changes secondvalue, because p1 has been redirected there.

So the final values are:

  • firstvalue=10firstvalue = 10
  • secondvalue=20secondvalue = 20

Read and try

Trace one pointer state sequence

The tracer lets you change the starting integers, then replay the pointer tutorial step by step.

Step 1

int firstvalue = ...; int secondvalue = ...; int *p1, *p2;

firstvalue = 5

secondvalue = 15

p1 Points to unassigned

p2 Points to unassigned

Two integers exist, but neither pointer holds a valid address yet.

The key discipline is to separate two questions:

  • what address is each pointer holding?
  • what value lives at that address right now?

Dereferencing changes the pointed-to object

Once a pointer has been assigned a valid address, dereferencing it lets you read or modify the object stored there.

int x = 7;
int *p = &x;
*p = 12;

After the final line, x is 12. The statement p=12*p = 12 does not create a new integer. It writes through the pointer into the existing object.

Common mistake

Assigning a pointer is not the same as assigning through a pointer

p=qp = q changes which address p stores.

p=q*p = *q copies the pointed-to value.

Those two statements may appear similar, but they do completely different jobs.

malloc allocates storage on the heap

The tutorial review also emphasizes malloc, because many data structures create nodes dynamically rather than in fixed-size arrays.

Definition

Heap allocation with malloc

malloc(n) asks the runtime for n bytes of storage and returns a pointer to the beginning of that block.

In C, the usual pattern is:

struct node *x;
x = malloc(sizeof(struct node));

Now x points to newly allocated memory large enough to store one struct node.

Two follow-up rules matter immediately:

  • always check that the returned pointer is used consistently with its type;
  • always know which part of the program is responsible for eventually freeing that memory.

Even when a short classroom example omits free, a real implementation cannot ignore ownership forever.

Worked example

Why dynamic allocation matters for linked structures

Suppose a stack is implemented as a linked list. Each push operation may need to create one new node. The total number of nodes is not known in advance, so a fixed local variable is not enough. malloc is the bridge between the logical operation “create a new node” and the physical action “reserve memory for one more node.”

typedef shortens repeated types

The tutorial review also includes typedef, because data-structure interfaces often hide long pointer types behind short aliases.

typedef struct node *nodePtr;
typedef int stackElementT;

This does not create a new runtime object. It creates a new type name for the compiler and for human readers.

The gain is clarity:

  • function prototypes become shorter,
  • interface files become easier to scan,
  • the ADT boundary becomes more readable.

When the course writes stackADT, the name itself is part of the abstraction. It tells you that the client should think in terms of “a stack object,” not “a pointer to some particular structure layout.”

Definition

Structure

A struct groups multiple fields under one named record type.

For example:

struct node {
   int data;
   struct node *next;
};

This is the standard shape for a linked-list node. One field stores the current payload, and one field stores the address of the next node.

The point is not only syntactic grouping. A struct lets you model the exact pieces of state that an ADT implementation must preserve.

Worked example

Read a queue node structure

If a queue uses linked nodes, a minimal structure might be

struct cellT {
   queueElementT element;
   struct cellT *next;
};

The queue object itself may then store:

  • a pointer to the head node,
  • a pointer to the tail node,
  • optionally a stored length counter.

That means the abstract queue operations enqueue, dequeue, and QueueLength() are implemented by updating a small, explicit collection of pointer fields.

How this feeds directly into ADT design

The earlier C lecture and the ADT lecture fit together tightly.

  • Pointers let one object refer to another.
  • malloc lets nodes be created when the operation needs them.
  • struct lets the implementation store several related fields together.
  • typedef helps the interface hide representation details.

So when the course says an ADT should expose the “what” and hide the “how,” the language tools above are exactly what make that separation possible in C.

Common mistakes

Common mistake

An uninitialized pointer is not a valid object

Declaring intp;int *p; creates a pointer variable, but it does not make it point to safe storage. Dereferencing such a pointer before assignment is undefined behavior.

Common mistake

malloc does not build a node for you

malloc gives raw storage only. You still have to initialize the fields of the new structure explicitly.

Common mistake

A struct field update may affect later pointer traces

If two pointers reach the same structure, updating a field through one pointer changes what the other pointer will see as well.

Quick checks

Quick check

What is the difference between p=qp = q and p=q*p = *q?

Answer in terms of addresses versus pointed-to values.

Solution

Answer

Quick check

Why is malloc(sizeof(struct node)) more reliable than writing a raw byte count by hand?

Think about portability and maintenance.

Solution

Answer

A fuller struct trace

The student example is worth reading one level more slowly, because it combines pointer setup, struct field access, file input, and ownership in one short trace.

Sdata *p;
p = (Sdata *)malloc(sizeof(Sdata));
FILE *fp = fopen("example.txt", "r");
fscanf(fp, "%s %d %s", p->name, &p->age, p->address);

There are four separate questions hidden in those lines:

  1. what object does p point to after malloc?
  2. which buffers receive the strings read into p>namep->name and p>addressp->address?
  3. why does p>agep->age need an address operator while the array fields do not?
  4. after the input is done, which resources must still be released?

If you can answer those four questions cleanly, you are no longer reading the code as isolated syntax. You are reading it as one ownership-and-state trace.

Exercises

Quick check

Trace the final values of x and y in this code: int x=1, y=2; int *p=&x; int *q=&y; *p=*q; q=p; *q=9;

Separate pointer reassignment from writes through the pointer.

Solution

Guided solution

Quick check

Explain why a linked-list node type almost always needs a pointer field to the next node.

Tie the answer to traversal and dynamic growth.

Solution

Guided solution

Section mastery checkpoint

Answer each question correctly to complete this section checkpoint. Correct progress: 0%.

Skills: pointers, addresses, dereference

Which statement correctly describes the difference between `p = q` and `*p = *q`?

Attempts used: 0

Attempts remaining: Unlimited attempts

Preview does not consume an attempt.

Submit records a graded attempt.

Skills: malloc, heap, memory-model

Fill in the blank: `malloc` allocates storage on the ____.

Attempts used: 0

Attempts remaining: Unlimited attempts

Preview does not consume an attempt.

Submit records a graded attempt.

Syntax guidance: Enter one short word only.

Prerequisites

This section can be read on its own.

Key terms in this unit