Pointers

Overview

Embedded software designers are required to access specific addresses within the microprocessor all the time.   We use addresses to access peripheral devices, configure the processor, and a variety of other operations.  In the C language, a pointer is used to access data at a specific address.   Here we will examine the basic syntax of how to allocate and use pointers.

Declaring a pointer

A pointer is declared using the asterix character and indicating the type of data the pointer points to.  This allows the compiler to determine the appropriate  assembly instructions required for accessing the address stored in the pointer.  If you are using a uint16_t pointer to write to a memory location, the compiler knows to use STRH commands. If you use a uint32_t pointer, the compiler knows to use STR commands.

    uint32_t *unsignedWordPtr;
    uint16_t *unsignedHalfWordPtr;
    Node *myNode;

In the example above, how many bytes are required to allocate for each pointer type?  The answer is 4 bytes for all of the pointers.  All pointers in the Cortex-M architecture are 4-bytes in length because all addresses are 32-bits.

Initializing a Pointer

When instantiated, a pointer points to NULL or address 0x00000000. NULL means that the pointer does not point to anything.  In order to initialize a pointer, we need to set the pointer to a valid memory address.  We can do this either by assigning the pointer to the address of a local/global variable or my dynamically allocating memory and setting the pointer to the address returned by malloc.

    //Assigning a pointer to a static variable
    uint32_t  var1;
    uint32_t *myPtr = &var1;

    //Assigning a pointer to a dynamic memory address
    Node *myNode;

    // Allocates memory from the heap 
    // for on Node Struct
    myNode = malloc(sizeof(Node));

If you try to access the memory of an uninitialized pointer, bad things happen.  You are essentially trying to read an invalid memory location.  The result is that the microprocessor will issue a fault, your program terminates, and the processor is stuck in the fault handler until the processor is restarted.

De-referencing a Pointer

When you want to change the value stored at a memory address, we de-reference the pointer.  This does not change the address the pointer contains, rather we change the value found at that address.  De-referencing is indicated with a *

    uint32_t  var1;
    uint32_t *myPtr = &var1;

    // the same as var1=1; 
    *myPtr = 1;

When dealing with pointers to structs, we will access the fields within the struct using “->” to de-reference the field.

    Node *myNode = malloc(sizeof(Node));
    myNode->size = 0;
    myNode->next = NULL;

 

Pointer Arithmetic

Pointer arithmetic is the process of adding an offset to the pointer to access a different memory location.

When we add a constant to a pointer, the resulting memory address is dependent on the size of the data being pointed to.  When we add positive 1 to a 32-bit variable, we actually increment the memory address by 4 bytes.  If we add +1 to a 16-bit variable, we increment the address by 2 bytes.

uint32_t *ptr1;
uint8_t  *ptr2;

// Allocate space for 4 32-bit variables
ptr1 = malloc(4 * sizeof(uint32_t) );  

// if ptr1 = 0x20000000, ptr <= 0x20000004 after
// this instruction
ptr1 = ptr1 + 1; 

// Allocate space for 16 8-bit variables
ptr2 = malloc(16 * sizeof(uint8_t) );  

// if ptr2 = 0x30000000, ptr <= 0x30000001
// after this instruction
ptr2 = ptr2 + 1;

 

If we are adding constants to a structure, adding positive 1 to the pointer will advance the pointer the total number of bytes for all the fields contained in the struct.

typedef struct 
{
     uint32_t v1;
     uint32_t v2;
     uint32_t v3;
     uint8_t *ptr;
} genStruct;

genStruct *ptr1;
genStruct structArray[10];

ptr1 = structArray;

// if ptr1 = 0x20000000,  after this instruction
// ptr1 = 0x20000000 + sizeof(genStruct)
ptr1 = ptr1 + 1;

Examples

// Create a pointer to a character
char *string;

// Allocate 27 characters
string = malloc(27);

// Initialize the string from a-z
for(i=0; i<26; i++)
{
  string[i] = 'a' + i;
}

// Set the last character in the string to 0 to end the string
string[26] = 0;

// Return the memory to the heap.
free(string);

This example is functionally the same as above, but illustrates a different way to access data using pointers.

  int i;

  // Create a pointer to a character
  char *string;
  char *currentChar;

  // Allocate 27 characters
  string = malloc(27);

  currentChar = string;
  // Initialize the string from a-z
  for(i=0; i<26; i++)
  {
    *currentChar = 'a' + i;
    currentChar++;
  }

  // Set the last character in the string to 0 to end the string
  currentChar = 0;

  printf("** %snr",string);

  // Return the memory to the heap.
  free(string);

 

Leave a Reply