sections in this module City College of San Francisco - CS270
Computer Architecture

Module: MIPS-V
module list

More on Pointers

In a previous section we discussed the C construct

char *message = "Computer Architecture is fun!"

where message is a static variable. We said that a compiler will generate an asciiz string for the message, a temporary label, then initialize a variable message with the address of the temporary label:

.Lmessage: .asciiz "Computer Architecture is fun!"
    .align 2
message: .word .Lmessage

Now all we must do to use the string is to load the pointer message and manipulate it. This is easy for static strings. But what happens when the character string appears as an automatic (local) variable:

void outmessage(void) {
char *message = "Computer Architecture is fun!"
...
}

Since strings are considered to be constants, the string itself can be static. Thus, this part of the code is the same

.Lmessage: .asciiz "Computer Architecture is fun!"

The pointer, however, must be allocated on the stack and thus must be initialized by code that is executed on entry to the function:

outmessage:
addiu  $sp,$sp,-24
sw     $ra,20($sp)
# 16 ($sp) is our pointer
la    $t0,.Lmessage
sw    $t0,16($sp)
...

When a function is called, code like this must be executed for any local variables that are initialized.

Multi-dimensional Arrays

Depending on the language, multi-dimensional arrays are stored in linear memory with the last (called row-major order for a two-dimensional array) or the first (column-major order) index increasing sequentially as the individual cells are stored. That's a mouthful. Let's look at the example for C-style two-dimensional arrays, which use row-major order:

int arr[2][3];

The elements of this array would be placed in-memory in this sequence:

arr[0][0], arr[0][1], arr[0][2], arr[1][0], arr[1][1], arr[1][2]

in other words, a row at a time. This is keeping with C's idea that a two-dimensional array as that above is really an array of [ two ] arrays [ of three elements each ].

Of course, this arrangement in memory is the same as it would be for a single-dimensional array of 6 elements. If we call the corresponding array sarr[6], then locating an element of arr is really translating the indices of arr to the index of sarr.

Let's lay them out together to see what I mean

arr[0][0] arr[0][1] arr[0][2] arr[1][0] arr[1][1] arr[1][2]
sarr[0]
sarr[1]
sarr[2]
sarr[3]
sarr[4]
sarr[5]

Let's take an arbitrary two-dimensional array int arr[rows][cols]. To find indx, where sarr[indx] will be located at the same offset as arr[i][j], we would use indx = (i*cols + j). Once we have indx, we can access the array element by simply multiplying by the size of the base type, here int.

Arrays of strings

An array of strings is really an array of pointers to strings, and is handled the same as any scalar array. The only difference is its base type is char *. Remember, there is no difference between an array of char *'s and an array of anything else. The only difference is how you use the elements!

Consider the following simple program messages.c

char *messages[3] = { "message1", "message2", "message3" };
main() {
int i;
for (i=0;i<3;i++) puts(messages[i]);
}

We should all be able to write this is MIPS code. But how about a simple modification

char *messages[3] = { "message1", "message2", "message3" };

void outmessages(char ** themessages,int n) {
   int i;
   for (i=0;i<n; i++)
    puts(*themessages++);
}

main() {
    outmessages(messages, 3);
}

The unfortunate thing about this program is the need to keep track of the length of the messages array and pass that to outmessages. Such manual bookkeeping is prone to error. An alternate solution is to rewrite the program so that the end of the messages array is indicated by a NULL pointer. After this modification we will find that we can even dispense with the indexing operation.

Can you rewrite messages.c to use the NULL (0) pointer in the declaration

char *messages[] = { "message1", "message2", "message3", 0 };

and to avoid the use of an index?

[ We will write the MIPS code for a similar program in the Problems page for this module. ]

Function Pointers

A function pointer is simply a pointer that is initialized to the address of a function.

Suppose you want to call the function myfunc. There are three ways to write code for this call:

to call a function
that is "close"
to call a function
that is "far away"
using a function pointer
jal myfunc
la $t0,myfunc
jalr $t0
#if you want to initialize
#funcptr statically
   .data
funcptr: .word myfunc
#or at runtime
la  $t0,myfunc
sw $t0,funcptr
#then at the call
lw $t0,funcptr
jalr $t0

A basic, but well-known, use of a function pointer is the qsort package in the C library. As many of you know, the qsort algorithm (and any sorting algorithm) is independent of the data being sorte so long as a mechanism is provided to compare two data items. This is provided to qsort() by giving it a function pointer. Here is the prototype

void qsort(void *base, int nmemb, int size,
int(*compar)(const void *, const void *));

Here, base is a pointer to an array of nmemb elements. Each element is size bytes long. compar is a pointer to a function that can be passed two [pointers to] elements to compare them. It returns -1, 0, or +1 depending on whether the first element it is passed compares less than, equal, or greater than the second.

Here is an example of qsort used to sort an integer array ia

NOTE: there is some extra crap needed in this program to get the compiler to stop complaining, but the program as listed below compiles and runs correctly on linux, in spite of the warnings. I didn't want to confuse the issue with the required casts.

#include <stdlib.h>

#define NELEMS 10
int ia[NELEMS] = { 4, -5, 6,10, 25, -43, 22, 9, -2, 10 };

int compare_ints( int * first, int * second) {
    if (*first < *second) return -1;
    return( *first > *second);
}

int main() {
    int i;
    qsort(ia,NELEMS,sizeof(int),compare_ints);
    for (i=0;i<NELEMS;i++) printf("%d\n",ia[i]);
}

In this example, the address of compare_ints is passed to qsort as a function pointer. In the qsort routine, the function would be called indirectly, passing it the address of two array elements that it wants to compare.

At the high-level-language level, the compiler ensures that the function being passed is 'compatible' with the function required by qsort. It does this through argument typing. At the assembler level, of course, no such hand-holding occurs, and the programmer is responsible for ensuring the function call occurs correctly.

The C code for this version (iasort.c) and the version that compiles without warnings (iasortX.c) is in the online/mipsIV directory. There is no MIPS code, as we do not have code for a qsort function.

Hopefully, we will have sufficient time to write a similar program in-class.


Prev This page was made entirely with free software on linux:  
the Mozilla Project
and Openoffice.org    
Next

Copyright 2016 Greg Boyd - All Rights Reserved.