Back to ESAcademy Home Page


www.philipsmcu.comUsing pointers, arrays, structures and unions in 8051 C compilers

by Olaf Pfeiffer, ESAcademy
based on the "C51Primer", by Mike Beach, Hitex UK

Pointers and Arrays | Structures and Unions | Generic and Spaced Pointers ]

 

Home

News

Training Classes

Products

Consulting

Technical Library

Contact Us

Recommended Books

Using Pointers And Arrays

One of C's greatest strengths can also be its greatest weakness - the pointer. The use and, more appropriately, the abuse of this language feature is largely why C is condemned by some as dangerous!

Pointers In Assembler

For an assembler programmer the C pointer equates closely to indirect addressing. In the 8051 this is achieved by the following instructions



    MOV R0,#40     ; Put on-chip address to be indirectly 
    MOV A,@RO      ; addressed in R0

    MOV R0,#40     ; Put off-chip address to be indirectly
    MOVX A,@RO     ; addressed in R0

    MOVX A,@DPTR   ; Put off-chip address to be indirectly
                   ; addressed in DPTR

    CLR A
    MOV DPTR,#0040 ; Put off-chip address to be indirectly 
    MOVC A,@A+DPTR ; addressed in DPTR
     

In each case the data is held in a memory location indicated by the value in registers to the right of the '@'.

Pointers In C

The C equivalent of the indirect instruction is the pointer. The register holding the address to be indirectly accessed in the assembler examples is a normal C type, except that its purpose is to hold an address rather than a variable or constant data value.

It is declared by:

unsigned char *pointer0 ;

Note the asterisk prefix, indicating that the data held in this variable is an address rather than a piece of data that might be used in a calculation etc..

In all cases in the assembler example two distinct operations are required:

  1. Place address to be indirectly addressed in a register.
  2. Use the appropriate indirect addressing instruction to access data held at chosen address.

Fortunately in C the same procedure is necessary, although the indirect register must be explicitly defined, whereas in assembler the register exists in hardware.



   /* 1 - Define a variable which will hold an address */
   unsigned char *pointer ;

   /* 2 - Load pointer variable with address to be accessed*/
   /*indirectly */
   pointer = &c_variable ;

   /* 3 - Put data '0xff' indirectly into c variable via*/
   /*pointer */
   *pointer = 0xff ;
     

Taking each operation in turn...

  1. Reserve RAM to hold pointer. In practice the compiler attaches a symbolic name to a RAM location, just as with a normal variable.
  2. Load reserved RAM with address to be accessed, equivalent to 'MOV R0,#40'. In English this C statement means: "take the 'address of' c_variable and put it into the reserved RAM, i.e, the pointer" In this case the pointer's RAM corresponds to R0 and the '&' equates loosely to the assembler '#'.
  3. Move the data indirectly into pointed-at C variable, as per the assembler 'MOV A,@R0'.

The ability to access data either directly, x = y, or indirectly, x = *y_ptr, is extremely useful. Here is C example:



/* Demonstration Of Using A Pointer */

void function(void) 
{
unsigned char c_variable ; // 1 - Declare a c variable unsigned char 
*ptr ;                     // 2 - Declare a pointer (not pointing at anything yet!)

  c_variable = 0xff ;      // 3 - Set variable equal to 0xff directly
                           // OR, to do the same with pointers:
  ptr = &c_variable ;      // 4 - Force pointer to point at c_variable at run time
  *ptr = 0xff ;            // 5 - Move 0xff into c_variable indirectly
}
     

Note: Line 4 causes pointer to point at variable. An alternative way of doing this is at compile time thus:



/* Demonstration Of Using A Pointer */

void function (void) 
{
unsigned char c_variable;         // 1-Declare a c variable 
unsigned char *ptr = &c_variable; // 2-Declare a pointer, intialized to pointing at 
                                     c_variable during compilation

   c_variable = 0xff ;   // 3 - Set variable equal to 0xff directly
                         // OR - use the pointer which is already initialized
   *ptr = 0xff           // 5 - Move 0xff into c_variable indirectly
}
     

Pointers with their asterisk prefix can be used exactly as per normal data types. The statement:

x = y + 3 ;

could equally well perform with pointers, as per

char x, y ;
char *x_ptr = &x ;
char *y_ptr = &y ;

*x_ptr = *y_ptr + 3 ;

or:

x = y * 25 ;

*x_ptr = *y_ptr * 25 ;

The most important thing to understand about pointers is that

*ptr = var ;

means "set the value of the pointed-at address to value var", whereas

ptr = &var ;

means "make ptr point at var by putting the address of (&) in ptr, but do not move any data out of var itself".

Thus the rule is to initialize a pointer:

ptr = &var ;

To access the data indicated by *ptr:

var = *ptr ;

Pointers To Absolute Addresses

In embedded C, ROM, RAM and peripherals are at fixed addresses. This immediately raises the question of how to make pointers point at absolute addresses rather than just variables whose address is unknown (and largely irrelevant).

The simplest method is to determine the pointed-at address at compile time:

char *abs_ptr = 0x8000 ; // Declare pointer and force to 0x8000

However if the address to be pointed at is only known at run time, an alternative approach is necessary. Simply, an uncommitted pointer is declared and then forced to point at the required address thus:

char *abs_ptr ; // Declare uncommitted pointer

abs_ptr = (char *) 0x8000 ; // Initialize pointer to 0x8000

*abs_ptr = 0xff ; // Write 0xff to 0x8000

*abs_ptr++ ; // Make pointer point at next location in RAM

Arrays And Pointers - Two Sides Of The Same Coin?

Uninitialized Arrays

The variables declared via

unsigned char x ;
unsigned char y ;

are single 8 bit memory locations. The declarations:

unsigned int a ;
unsigned int b ;

yield four memory locations, two allocated to 'a' and two to 'b'. In other programming languages it is possible to group similar types together in arrays. In basic an array is created by DIM a(10).
Likewise 'C' incorporates arrays, declared by:

unsigned char a[10] ;

This has the effect of generating ten sequential locations, starting at the address of 'a'. As there is nothing to the right of the declaration, no initial values are inserted into the array. It therefore contains zero data and serves only to reserve ten contiguous bytes.

Initialized Arrays

A more usual instance of arrays would be

unsigned char test_array [] = { 0x00,0x40,0x80,0xC0,0xFF } ;

where the initial values are put in place before the program gets to "main()". Note that the size of this initialized array is not given in the square brackets - the compiler works-out the size automatically upon compilation.

Another common instance of an array is analogous to the BASIC string as per:

A$ = "HELLO!"

In C this equates to:

char test_array[] = { "HELLO!" } ;

In C there is no real distinction between strings and arrays as a C array is just a series of sequential bytes occupied either by a string or a series of numbers. In fact the realms of pointers and arrays overlap with strings by virtue of :

char test_array = { "HELLO!" } ;
char *string_ptr = { "HELLO!" } ;

Case 1 creates a sequence of bytes containing the ASCII equivalent of "HELLO!". Likewise the second case allocates the same sequence of bytes but in addition creates a separate pointer called *string_ptr to it. Notice that the "unsigned char" used previously has become "char", literally an ASCII character.

The second is really equivalent to:

char test_array = { "HELLO!" } ;

Then at run time:

char arr_ptr = test_array ; // Array treated as pointer - or;

char arr_ptr = &test_array[0] ;
// Put address of first element of array into pointer

This again shows the partial interchangeability of pointers and arrays. In English, the first means "transfer address of test_array into arr_ptr". Stating an array name in this context causes the array to be treated as a pointer to the first location of the array. Hence no "address of" (&) or '*' to be seen.

The second case reads as "get the address of the first element of the array name and put it into arr_ptr". No implied pointer conversion is employed, just the return of the address of the array base.

The new pointer "*arr_ptr" now exactly corresponds to *string_ptr, except that the physical "HELLO!" they point at is at a different address.

Using Arrays

Arrays are typically used like this



/* Copy The String HELLO! Into An Empty Array */

unsigned char source_array[] = { "HELLO!" } ; 
unsigned char dest_array[7];
unsigned char array_index ;

array_index = 0 ; // First character index

while(array_index < 7)   // Check for end of array
{
    dest_array[array_index] = source_array[array_index] ;      
    // Move character-by-character into destination array
    
    array_index++ ;  // Next character index
}
     

The variable array_index shows the offset of the character to be fetched (and then stored) from the starts of the arrays.

As has been indicated, pointers and arrays are closely related. Indeed the above program could be re-written as:



/* Copy The String HELLO! Into An Empty Array */

char *string_ptr = { "HELLO!" } ;
unsigned char dest_array[7] ;
unsigned char array_index  ;

array_index = 0 ; // First character index

while(array_index < 7)     // Check for end of array
{

    dest_array[array_index] = string_ptr[array_index] ;  
    // Move character-by-character into destination array.

    array_index++ ;
}
     

The point to note is that only the definition of string_ptr (previous source_array) changed. By removing the '*' on string_ptr and appending a '[ ]' pair, this pointer can be turned back into an array!

However in this case there is an alternative way of scanning along the HELLO! string, using the *ptr++ convention:



/* Copy The String HELLO! Into An Empty Array */

char *string_ptr = { "HELLO!" } ;
unsigned char dest_array[7] ;
unsigned char array_index  ;

array_index = 0 ; // First character index

while(array_index < 7)     // Check for end of array
{

    dest_array[array_index] = *string_ptr++ ;  
    // Move character-by-character into destination array.

    array_index++ ;
}
     

This is an example of C being somewhat inconsistent; this *ptr++ statement does not mean "increment the thing being pointed at" but rather, increment the pointer itself, so causing it to point at the next sequential address. Thus in the example the character is obtained and then the pointer moved along to point at the next higher address in memory.

Summary Of Arrays And Pointers

To summarize

  • Create An Uncommitted Pointer
    unsigned char *x_ptr ;
  • Create A Pointer To A Normal C Variable
    unsigned char x ;
    unsigned char *x_ptr = &x ;
  • Create An Array With No Initial Values
    unsigned char x_arr[10] ;
  • Create An Array With Initialized Values
    unsigned char x_arr[] = { 0,1,2,3 } ;
  • Create An Array In The Form Of A String
    char x_arr[] = { "HELLO" } ;
  • Create A Pointer To A String
    char *string_ptr = { "HELLO" } ;
  • Create A Pointer To An Array
    char x_arr[] = { "HELLO" } ;
    char *x_ptr = x_arr ;

  • Force A Pointer To Point At The Next Location
    *ptr++ ;

Pointers and Arrays | Structures and Unions | Generic and Spaced Pointers ]

ESAcademy, 2000

All materials
provided 'as is'
see Disclaimer

www.esacademy.com
info@esacademy.com