CPSC 355: Tutorial 12

Structs

PhD Student

Fall 2017

Outline

Today we'll cover one large example that will include everything you need for assignment 4.

Assignment 4

#include <stdio.h>

#define FALSE 0
#define TRUE  1

struct point {
    int x,y;
};

struct dimension {
    int width, length;
};

struct pyramid {
    struct point origin;
    struct dimension base;
    int height;
    int volume;
};

struct pyramid newPyramid(){
    struct pyramid p;
    p.origin.x    = 0;
    p.origin.y    = 0;
    p.base.width  = 2;
    p.base.length = 2;
    p.height      = 3;
    p.volume      = (p.base.width * p.base.length * p.height)/3.;
    return p;
}

Assignment 4

void move(struct pyramid *p, int deltaX, int deltaY)
{
    p->origin.x += deltaX;
    p->origin.y += deltaY;
}

void scale(struct pyramid *p, int factor)
{
    p->base.width   *= factor;
    p->base.length  *= factor;
    p->height       *= factor;
    p->volume       *= (p->base.width * p->base.length * p->height)/3;
}

void printPyramid(char *name, struct pyramid *p)
{
    printf("Pyramid %s origin: (%d, %d)\n", name, p->origin.x, p->origin.y);
    printf("\tBase width=%d Base length = %d\n", p->base.width, p->base.length);
    printf("\tHeight = %d\n", p->height);
    printf("\tVolume = %d\n", p->volume);
}

int equalSize(struct pyramid *p1, struct pyramid *p2)
{
    int result = FALSE;

    if(p1->base.width == p2->base.width) {
        if(p1->base.length == p2->base.length) {
            if(p1->height == p2->height) {
                result = TRUE;
            }
        }
    }
    return result;
}

Assignment 4

int main()
{
    struct pyramid first, second;

    first  = newPyramid();
    second = newPyramid();

    printf("Initial pyramid values:\n");
    printPyramid("first",  &first);
    printPyramid("second", &second);

    if(equalSize(&first, &second)){
        move(&first, -5, 7);
        scale(&second, 3);
    }

    printf("\nChanged pyramid values:\n");

    printPyramid("first",  &first);
    printPyramid("second", &second);
}

Example

#include <stdio.h>

struct physique {
    int height;
    int age;
};

struct student {
    unsigned long id;
    struct physique b;
};

struct student newStudent(unsigned long id){
    struct student s;
    s.id        = id;
    s.b.age     = -1; // -1 means unknown
    s.b.height  = -1; // -1 means unknown
    return s;
}

void printStudent(struct student *s) {
    printf("Student: %ld\n",s->id);
    if(s->b.age > 0) 
        printf("\tAge:%d\n",s->bod.age);
    else 
        printf("\tAge unknown!");
   
}

Example

...

void studentBirthday(struct student *s) {
    if(s->b.age > 0) 
        s->b.age += 1;
}

void main(int argc, char *argv) {
    struct student s = newStudent(30100000);
    printStudent(&s);
    s.b.age = 22;
    printStudent(&s);
    studentBirthday(&s);
    printStudent(&s);
}

Structs

struct physique {
    int height;
    int age;
};

struct student {
    unsigned long id;
    struct physique b;
};

In memory

struct student
id_number (8 bytes)
height  (4 bytes)
age  (4 bytes)

Structs are like objects in higher level languages, but they only model objects, they have no methods.

Structs

struct physique {
    int height;
    int age;
};

struct student {
    unsigned long id;
    struct physique b;
};

void main() {
    struct student s;
    s.id     = 30100000;
    s.b.age    = 22;
    s.b.height = 180;
}

Structs are like objects in higher level languages, but they only model objects, they have no methods.

Structs

    // Offsets from the start of the physique struct
    phys_height = 0 // offset 0, size = 4 bytes
    phys_age    = 4 // offset 4, size = 4 bytes
    phys_struct_size = 8 // total 8 bytes

    // Offsets from the start of the student struct
    student_id  = 0  // offset 0 from start, size 8 (unsigned long)
    student_b   = 8  // offset 8, size = 8 (phys_struct_size)
    student_struct_size = 16 // total size

fp .req x29
lr .req x30

    alloc = -(16 + student_struct_size) & -16
    dealloc = -alloc
    s_fo = 16 // offset to s variable on the stack (via frame pointer)
.balign 4
.global main
main:
    stp fp, lr, [sp, alloc]! // Allocate enough stack space for s struct variable
    mov fp, sp

    ldr x0, =30100000               // x0 = 30100000 (64 bit)
    str x0, [fp, s_fo + student_id] // Store in s.id

    mov w0, 22                       // w0 = 22 (32 bit)
    str w0, [fp, s_fo + student_b + phys_age] // s.b.age = 22

    mov w0, 180                      // w0 = 180 (32 bit)
    str w0, [fp, s_fo + student_b + phys_height] // s.b.height = 22

    ldp fp, lr, [sp], dealloc
    ret

Structs

struct physique { ... };
struct student { ... };

struct student newStudent(unsigned long id){
    struct student s;
    s.id        = id;
    s.b.age     = -1;
    s.b.height  = -1;
    return s;
}

void main() {
    struct student s;
    s = newStudent(3010000);
}

Structs are like objects in higher level languages, but they only model objects, they have no methods. Here's a constructor like function for a student.

In newStudent(), a student struct is created, assigned values, then copied out in the return

    // Offsets from the start of the physique struct
    phys_height = 0 // offset 0, size = 4 bytes
    phys_age    = 4 // offset 4, size = 4 bytes
    phys_struct_size = 8 // total 8 bytes
    // Offsets from the start of the student struct
    student_id  = 0  // offset 0 from start, size 8 (unsigned long)
    student_b   = 8  // offset 8, size = 8 (phys_struct_size)
    student_struct_size = 16 // total size
fp .req x29
lr .req x30

    alloc = -(16 + student_struct_size) & -16
    dealloc = -alloc
    s_fo = 16 // offset to s variable on the stack (via frame pointer)

.balign 4

// Creates a new student struct
newStudent:
    stp fp, lr, [sp, alloc]! // Allocate enough stack space for s struct variable
    mov fp, sp

    str x0, [fp, s_fo + student_id] // Store arg 0 in s.id

    mov w0, -1                       // w0 = -1 (32 bit)
    str w0, [fp, s_fo + student_b + phys_age] // s.b.age = -1
    str w0, [fp, s_fo + student_b + phys_height] // s.b.height = -1
    
    // By convention, the above operations HAD to be done on the local stack
    // We now copy this out to the return struct (x8 points to the ret struct)
    // You need to copy ALL the struct elements

    ldr x9, [fp, s_fo + student_id] // copy s.id
    str x9, [x8, student_id]        // We don't add s_fo, because the address
                                    // of the destination is not on the stack
    ldr w9, [fp, s_fo + student_b + phys_age] // copy s.b.age
    str w9, [x8, student_b + phys_age]

    ldr w9, [fp, s_fo + student_b + phys_height] // copy s.b.height
    str w9, [x8, student_b + phys_height]
    ldp fp, lr, [sp], dealloc
    ret

    // redefine alloc for main
    alloc = -(16 + student_struct_size) & -16
    dealloc = -alloc
    s_fo = 16 // offset to s variable on the stack (via frame pointer)
.global main
main:
    stp fp, lr, [sp, alloc]! // Allocate enough stack space for s struct variable
    mov fp, sp

    // Set x8 to the address of the student struct on the stack
    add x8, fp, s_fo
    ldr x0, =3010000
    bl newStudent
    
    // s_fo now contains the new student

    ldp fp, lr, [sp], dealloc
    ret

Structs

struct physique { ... };
struct student { ... };
struct student newStudent(unsigned long id){ ... }

void printStudent(struct student *s) {
    printf("Student: %ld\n",s->id);
    if(s->b.age >= 0) 
        printf("\tAge:%d\n",s->b.age);
    else 
        printf("\tAge unknown!\n");
   
}

void main() {
    struct student s;
    s = newStudent(3010000);
    printStudent(&s);
}

Passing by reference. *s is a pointer to a location in memory. &s gets the address of a variable on the stack

    // Offsets from the start of the physique struct
    phys_height = 0 // offset 0, size = 4 bytes
    phys_age    = 4 // offset 4, size = 4 bytes
    phys_struct_size = 8 // total 8 bytes
    // Offsets from the start of the student struct
    student_id  = 0  // offset 0 from start, size 8 (unsigned long)
    student_b   = 8  // offset 8, size = 8 (phys_struct_size)
    student_struct_size = 16 // total size
fp .req x29
lr .req x30

//..
// newStudent code goes here
//..

printStudent:
    stp fp, lr, [sp, -32]! // We have no stack variables, but we'll be using
                           // x19, so we need to allocate space to save x19
    mov fp, sp
    str x19, [fp, 16]      // store x19 on the stack. The function that called 
                           // this function might depend on x19 being preserved
                           // so we must save it

    // x0 contains the pointer to a student struct, so lets save that in 
    // x19, a register we know won't get clobbered
    mov x19, x0
    ldr x0, =fmt1
    ldr x1, [x19, student_id] // load s->id
    bl printf

    ldr w0, [x19, student_b + phys_age] // load s->b.age

    cmp w0, 0      
    b.lt else1     // go to else case if age < 0

    mov w1, w0     // w0 had age in it, so move it to w1 (arg 1)
    ldr x0, =fmt2  // load arg 0 as fmt2
    bl printf      // print

    b return_ps    // return
else1:
    ldr x0, =fmt3
    bl printf
return_ps:
    ldr x19, [fp, 16]
    ldp fp, lr, [sp], 32
    ret

main:
    stp fp, lr, [sp, alloc]! // Allocate enough stack space for s struct variable
    mov fp, sp

    // Set x8 to the address of the student struct on the stack
    add x8, fp, s_fo
    ldr x0, =30100000
    bl newStudent
    
    // s_fo now contains the new student
    add x0, fp, s_fo // x0 = &s = address of s variable on stack
    bl printStudent

    ldp fp, lr, [sp], dealloc
    ret

Structs

struct physique { ... };
struct student { ... };
struct student newStudent(unsigned long id){ ... }
void printStudent(struct student *s) { ... }

void studentBirthday(struct student *s) {
    if(s->b.age > 0) 
        s->b.age += 1;
}

void main(int argc, char *argv) {
    struct student s = newStudent(30100000);
    printStudent(&s);
    s.b.age = 22;
    printStudent(&s);
    studentBirthday(&s);
    printStudent(&s);
}

Passing by reference. *s is a pointer to a location in memory. &s gets the address of a variable on the stack

    // Offsets from the start of the physique struct
    phys_height = 0 // offset 0, size = 4 bytes
    phys_age    = 4 // offset 4, size = 4 bytes
    phys_struct_size = 8 // total 8 bytes
    // Offsets from the start of the student struct
    student_id  = 0  // offset 0 from start, size 8 (unsigned long)
    student_b   = 8  // offset 8, size = 8 (phys_struct_size)
    student_struct_size = 16 // total size
fp .req x29
lr .req x30

//..
// newStudent code goes here
// printStudent code goes here
//..

studentBirthday:
    stp fp, lr, [sp, -16]! // We don't need to save anything other than fp and lr
    mov fp, sp

    // x0 is the address of the struct s
    ldr w9, [x0, student_b + phys_age]  // load s->b.age

    cmp w9, 0      
    b.lt return_sb                      // if age < 0, return 
    
    add w9, w9, 1                       // add one to the age
    str w9, [x0, student_b + phys_age]  // and store it

return_sb:
    ldp fp, lr, [sp], 16                // restore fp, lr
    ret                                 // return

main:
    stp fp, lr, [sp, alloc]! // Allocate enough stack space for s struct variable
    mov fp, sp

    // Set x8 to the address of the student struct on the stack
    add x8, fp, s_fo
    ldr x0, =30100000
    bl newStudent
    
    // s_fo now contains the new student
    add x0, fp, s_fo // x0 = &s = address of s variable on stack
    bl printStudent

    mov x2, 22
    str x2, [fp, s_fo + student_b + phys_age] // s.b.age = 22

    add x0, fp, s_fo // x0 = &s = address of s variable on stack
    bl printStudent
   
    add x0, fp, s_fo // x0 = &s = address of s variable on stack
    bl studentBirthday

    add x0, fp, s_fo // x0 = &s = address of s variable on stack
    bl printStudent

    ldp fp, lr, [sp], dealloc
    ret
// File: ex1.asm
// Author: Joshua Horacsek
// Date: November 6th 2017
// 
// Description: 
// A simple example showing how to pass around structs.

// Setup some macros for ourselves to make our lives easier
    // Offsets from the start of the physique struct
    phys_height = 0 // offset 0, size = 4 bytes
    phys_age    = 4 // offset 4, size = 4 bytes
    phys_struct_size = 8 // total 8 bytes

    // Offsets from the start of the student struct
    student_id  = 0  // offset 0 from start, size 8 (unsigned long)
    student_b   = 8  // offset 8, size = 8 (phys_struct_size)
    student_struct_size = 16 // total size

fp  .req x29
lr  .req x30

    alloc   = -(16 + student_struct_size) & -16
    dealloc = -alloc
    s_fo    = 16 // offset to s variable on the stack (via frame pointer)

// format strings
fmt1: .string "Student: %ld\n"
fmt2: .string "\tAge:%d\n"
fmt3: .string "\tAge unknown!\n"

Completed Example

.balign 4

///////////////////////////////////////////
// function: newStudent
///////////////////////////////////////////
    alloc = -(16 + student_struct_size) & -16
    dealloc = -alloc
    s_fo = 16 // offset to s variable on the stack (via frame pointer)

newStudent:
    stp fp, lr, [sp, alloc]! // Allocate enough stack space for s struct variable
    mov fp, sp

    str x0, [fp, s_fo + student_id]              // Store arg 0 in s.id

    mov w0, -1                                   // w0 = -1 (32 bit)
    str w0, [fp, s_fo + student_b + phys_age]    // s.b.age = -1
    str w0, [fp, s_fo + student_b + phys_height] // s.b.height = -1

    // By convention, the above operations HAD to be done on the local stack
    // We now copy this out to the return struct (x8 points to the ret struct)
    // You need to copy ALL the struct elements

    ldr x9, [fp, s_fo + student_id]           // copy s.id
    str x9, [x8, student_id]                  // We don't add s_fo, because the address
                                              // of the destination is not on the stack
    ldr w9, [fp, s_fo + student_b + phys_age] // copy s.b.age
    str w9, [x8, student_b + phys_age]

    ldr w9, [fp, s_fo + student_b + phys_height] // copy s.b.height
    str w9, [x8, student_b + phys_height]

    ldp fp, lr, [sp], dealloc
    ret
///////////////////////////////////////////
// function: printStudent
///////////////////////////////////////////
    alloc   = -32 // We have no stack vars, but we need to keep x19
    dealloc = -alloc

printStudent:
    stp fp, lr, [sp, alloc]!
    mov fp, sp

    str x19, [fp, 16]         // store x19 on the stack. The function that called 
                              // this function might depend on x19 being preserved
                              // so we must save it

    // x0 contains the pointer to a student struct, so lets save that in 
    // x19, a register we know won't get clobbered
    mov x19, x0               // save x0 into x19
    ldr x0, =fmt1             // arg0 = fmt1
    ldr x1, [x19, student_id] // arg1 = s->id
    bl printf                 // printf(fmt1, s->id);

    // load age into w0
    ldr w0, [x19, student_b + phys_age]

    cmp w0, 0          // compare age with 0
    b.lt else1         // go to else case if age < 0

    mov w1, w0         // w0 had age in it, so move it to w1 (arg 1)
    ldr x0, =fmt2      // load arg 0 as fmt2
    bl printf          // print
    b return_ps        // return

else1:
    ldr x0, =fmt3      // just print unknown age
    bl printf

return_ps:
    ldr x19, [fp, 16] // restore x19
    ldp fp, lr, [sp], dealloc
    ret
///////////////////////////////////////////
// function: studentBirthday
///////////////////////////////////////////
studentBirthday:
    stp fp, lr, [sp, -16]! // We don't need to save anything other than fp and lr
    mov fp, sp

    // x0 is the address of the struct s
    ldr w9, [x0, student_b + phys_age]  // load s->b.age

    cmp w9, 0 
    b.lt return_sb                      // if age < 0, return 

    add w9, w9, 1                       // add one to the age
    str w9, [x0, student_b + phys_age]  // and store it

return_sb:
    ldp fp, lr, [sp], 16                // restore fp, lr
    ret                                 // return

///////////////////////////////////////////
// function: main
///////////////////////////////////////////
    alloc = -(16 + student_struct_size) & -16
    dealloc = -alloc
    s_fo = 16 // offset to s variable on the stack (via frame pointer)
.global main
main:
    stp fp, lr, [sp, alloc]! // Allocate enough stack space for s struct variable
    mov fp, sp

    // Set x8 to the address of the student struct on the stack
    add x8, fp, s_fo
    ldr x0, =30100000
    bl newStudent

    // s_fo now contains the new student
    add x0, fp, s_fo // x0 = &s = address of s variable on stack
    bl printStudent

    // set the student's age
    mov x2, 22
    str x2, [fp, s_fo + student_b + phys_age] // s.b.age = 22

    add x0, fp, s_fo   // x0 = &s = address of s variable on stack
    bl printStudent    // printStudent(&s);

    add x0, fp, s_fo   // x0 = &s = address of s variable on stack
    bl studentBirthday // studentBirthday(&s);

    add x0, fp, s_fo   // x0 = &s = address of s variable on stack
    bl printStudent    // printStudent(&s);

    ldp fp, lr, [sp], dealloc
    ret

struct wheel {
    int width, radius;
};

struct body {
    int width, length, depth;
};

struct car {
    struct body b;
    struct wheel w1;
    struct wheel w2;
    struct wheel w3;
    struct wheel w4;
    int doors;
};

Multiple Nested Structs

    // Offsets from the start of the physique struct
    wheel_width       = 0 // offset 0, size = 4 bytes
    wheel_radius      = 4 // offset 4, size = 4 bytes
    wheel_struct_size = 8 // total 8 bytes

    // Offsets from the start of the physique struct
    body_width        = 0  // offset 0, size = 4 bytes
    body_length       = 4  // offset 4, size = 4 bytes
    body_depth        = 8  // offset 8, size = 4 bytes
    body_struct_size  = 12 // total 12 bytes

    // Offsets from the start of the student struct
    car_b     = 0   // offset 0 from start, size 12 (body_struct_size)
    car_w1    = 12  // offset 12, size = 8 (wheel_struct_size)
    car_w2    = 20  // offset 20, size = 8 (wheel_struct_size)
    car_w3    = 28  // offset 28, size = 8 (wheel_struct_size)
    car_w4    = 36  // offset 36, size = 8 (wheel_struct_size)
    car_doors = 40  // offset 40, size = 4 (integer)
    car_struct_size = 44 // total size

Next Day

Work day

CPSC 355: Tutorial 12

By Joshua Horacsek

CPSC 355: Tutorial 12

  • 2,476