And you are lazy and refuse to traverse the linked list.
The only action that you want to do is:
|
Convince yourself that this insert procedure will insert the new list element at the end of the list:
|
static List Insert( List h, List p ) { if ( the list at h is empty ) { Make list element p as the new list; // p is the only element and is also the last element in the list } else { Hire someone to insert p at the end of sublist (h.next); Get his result; Attach his result to h.next; return h; } } |
If you translates these actions into Java constructs, you will also get this algorithm as discussed in my CS170 webpage (but with some abreviation - I can shortend the else part, you are not in CS170 and should be able to handle this shorted code):
static List Insert( List h, List p ) { if ( h == null ) { p.next = null; return p; } else { // Insert( h.next, p ) = hire an "inserter" to insert // the new list element at the end of // the sub-list head.next // h.next = ... = you attach his result list to head.next h.next = Insert( h.next, p ); return h; } } |
static List Insert( List h, List p ) { if ( h == null ) { p.next = null; return p; } else { // Insert( h.next, p ) = hire an "inserter" to insert // the new list element at the end of // the sub-list head.next // h.next = ... = you attach his result list to head.next h.next = Insert( h.next, p ); return h; } } static void main( ) { ptr = new List( ); head = Insert(head, ptr); } |
Notice that:
|
push parameters (head and ptr) on the stack bl Insert add sp, sp, #8 // Clean up the parameter from the stack |
|
|
Stack frame of the Insert( ) method: SP FP -----> +---------------------+ | old Frame Pointer | +---------------------+ | Return Address | +---------------------+ | head | addr(n) = FP + 8 +---------------------+ | ptr | addr(n) = FP + 12 +---------------------+ |
You can see that the Insert( ) function will:
main: // High level programming statement: // // head = Insert(head, ptr) /* ------------------------------------------------- Pass parameter ptr (using stack) ------------------------------------------------- */ movw r0, #:lower16:ptr movt r0, #:upper16:ptr ldr r0, [r0] // r0 = ptr push {r0} // Pass ptr using the program stack /* ------------------------------------------------- Pass parameter head (using stack) ------------------------------------------------- */ movw r0, #:lower16:head movt r0, #:upper16:head ldr r0, [r0] // r0 = head push {r0} // Pass head using the program stack /* ------------------------------------------------------ call Insert(k) ------------------------------------------------------ */ bl Insert add sp, sp, #8 // Clean up the parameters /* ----------------------------------------------------------------- Save return value (in r0) to variable searchValue ----------------------------------------------------------------- */ movw r1, #:lower16:head // Do NOT use r0 !!! movt r1, #:upper16:head // (Because r0 contains the return value) str r0, [r1] // This will store return value in head |
/* ----------------------------------------------------------------------- Insert at tail - recursive function Algorithm: List Insert( List h, List p ) { if ( h == null ) { p.next = null; return p; } else { h.next = Insert( h.next, p ); return h; } } Stack frame of the Insert( ) method: SP FP -----> +---------------------+ | old Frame Pointer | +---------------------+ | Return Address | +---------------------+ | h | addr(n) = FP + 8 +---------------------+ | p | addr(n) = FP + 12 +---------------------+ ----------------------------------------------------------------------- */ Insert: /* ============================= Prelude: build stack frame ============================= */ push {lr} push {fp} mov fp, sp sub sp, sp, #0 // No local variables // if ( h == null ) ldr r0, [fp, #8] cmp r0, #0 bne else // p.next = null ldr r0, [fp, #12] mov r1, #0 // null str r1, [r0, #4] // p.next = null // return p ldr r0, [fp, #12] // Set up r0 = return value (p) /* ========================================== Postlude - clean up and return to caller ========================================== */ mov sp, fp pop {fp} pop {pc} else: // h.next = Insert( h.next, p ); //// pass p ldr r0, [fp, #12] // r0 = p push {r0} // Pass p with stack //// pass h.next ldr r0, [fp, #8] // r0 = h ldr r0, [r0, #4] // r0 = h.next push {r0} // Pass h.next with stack bl Insert add sp, sp, #8 // Clean up 2 parameters //// Store return value (in r0) in h.next ldr r1, [fp, #8] // r1 = h str r0, [r1, #4] // h.next = return value from Insert(h.next,p) // return( h ); ldr r0, [fp, #8] // Set up r0 = return value (h) /* =========================================== Postlude - clean up and return to caller =========================================== mov sp, fp pop {fp} pop {pc} |
How to run the program:
|
The main program passes the variables head and ptr to the Insert method by pushing the value of variables head and ptr onto the system stack with the following instruction:
// Pass ptr using the stack movw r0, #:lower16:ptr movt r0, #:upper16:ptr // r0 = addr(ptr) ldr r0, [r0] // r0 = ptr push {r0} // Pass ptr on the stack // Pass head using the stack movw r0, #:lower16:head movt r0, #:upper16:head // r0 = addr(head) ldr r0, [r0] // r0 = head push {r0} // Pass head on the stack |
This will create the following stack structure:
+---------------------+ <------------ Stack pointer (SP) | head | +---------------------+ | ptr | +---------------------+ |
The main program calls the Insert function with a bl instruction:
bl Insert |
This will save the return address to main( ) in the LR register and jump to the Insert method
The Insert( ) function will start running, so let's take a look at the Insert( ) function
The prelude of the Insert function consists of these instructions:
/* ========================================================== Function Prelude: complete the stack frame structure ========================================================== */ push {lr} // Save LR (return address) push {fp} // Save FP (used by caller) mov fp, sp // Mark the stack top location before // allocating any local variables sub sp, sp, #0 // Allocate 0 int variables on the stack // (I could omit this instruction....) |
I will explain what each one does below.
Make sure that you realise that the structure of the stack frame is like this when the prelude of the Insert( ) method is executed:
+---------------------+ <------------ Stack pointer (SP) | head | +---------------------+ | ptr | +---------------------+ |
This instruction will save the return address in the LR register on the program stack.
The program stack now looks like this:
+---------------------+ <------------ Stack pointer (SP) | return address | +---------------------+ | head | +---------------------+ | ptr | +---------------------+ |
This will save the frame pointer on the stack and the program stack now looks like this:
+---------------------+ <------------ Stack pointer (SP) | old Frame Pointer | +---------------------+ | return address | +---------------------+ | head | +---------------------+ | ptr | +---------------------+ |
This will make the frame pointer FP points to the stack frame that is being built:
+---------------------+ <------------ Frame pointer FP & Stack pointer SP | old Frame Pointer | point to the same location.... +---------------------+ | return address | +---------------------+ | head | +---------------------+ | ptr | +---------------------+ |
The will enable the Insert( ) to use offset from the frame pointer to access the parameters and local variables that are stored in the program stack
The subtract instruction is used to allocate local variables on the program stack
But since Insert( ) has no local variables, this instruction does nothing to the stack pointer SP... (and we could omit it - I left it in to keep the discussion uniform)
We have now completed the stack frame:
+---------------------+ <---- Frame pointer FP & Stack pointer SP | old Frame Pointer | point to the same location.... +---------------------+ | return address | +---------------------+ | head | +---------------------+ | ptr | +---------------------+ |
When the prelude is finish, the stack frame is complete and the actual function can begin.
+---------------------+ <-------------- Frame pointer FP | old Frame Pointer | (4 bytes) +---------------------+ | return address | (4 bytes) +---------------------+ | h=head | <--- offset = 8 +---------------------+ | p=ptr | <--- offset = 12 +---------------------+ |
So base register (FP) + offset 8 will let you access h (= head)
And base register (FP) + offset 12 will let you access ptr
That's why you see the use of the following instructions in Insert( ) to get the parameter:
ldr r0, [fp,#8] // r0 = h and ldr r0, [fp,#12] // r0 = p |
It is no different from how the main program calls the Insert function.
The Insert method can call the Insert method by passing the parameter on the program stack and then use bl Insert to call the Insert method.
But make sure you pop the parameter from the stack after the Insert function returns - because the parameter has not been cleaned up.
The following is the program fragment where Insert calls Insert(h.next, p) by passing the parameters h.next and ptr on the stack:
// Call Insert( h.next, ptr ) ldr r0, [fp,#12] // r0 = p push {r0} // Pass p as parameter on stack ldr r0, [fp,#8] // r0 = h ldr r0, [r0,#4] // r0 = h.next push {r0} // Pass h.next as parameter on stack // Call Insert( ) with parameters: h.next and p on stack bl Insert add sp, sp, #8 // Clean up parameters |