CS 208 w20 lecture 14 outline

1 Array Access Polls

2 Multilevel Arrays

  • is this equivalent to previous sea?
int sea0[5] = {9, 8, 1, 9, 5};
int sea1[5] = {9, 8, 1, 0, 5};
int sea2[5] = {9, 8, 1, 0, 3};
int sea3[5] = {9, 8, 1, 1, 5};
int *sea_m[4] = {sea0, sea1, sea2, sea3};
  • each element of sea is a pointer
  • within each row, contiguous memory, but each row could be put anywhere
int get_digit (int index, int digit) {
    return sea_m[index][digit];
}
salq $2, %rsi # rsi = 4*digit
addq sea_m(,%rdi,8), %rsi # p = sea_m[index] + 4*digit
movl (%rsi), %eax # return *p
ret
  • must do two memory reads, but allows arrays to be of different lengths
  • array access looks the same sea[3][2] and sea_m[3][2], but underneath
    • Mem[ sea + 20*index + 4*digit ] vs Mem[ Mem[ sea_m + 8*index ] + 4*digit ]

2.1 Multilevel Array Access Poll

3 Structures

Two ways to create data types in C: structures (struct) and unions (union) (we won't worry about unions in this course)

// a way of combining different types of data together
struct song {
    char *title;
    int length_in_seconds;
    int year_released;
};
struct song song1;
song1.title = "What is Urinetown?";
song1.length_in_seconds = 213;
song1.year_released = 2001;
  • variable declarations like any other type: struct name name1, *pn, name_ar[3];
  • common to use typedef to give the struct type a more concise alias
    • typedef struct song song_t;
  • access fields with . or -> in the case of the pointer (p->field is shorthand for (*p).field)
  • like arrays, struct elements are stored in a contiguous region with a pointer to the first byte
    • sizeof(struct song)? 16 bytes
    • compiler maintains the byte offset information needed to access additional fields
    • can find offset of individual fields using offsetof(type, member)

3.1 Examples

struct rec {
    int a[4];
    long i;
    struct rec *next;
}

struct-rec.png

  • fields ordered in declaration order
  • machine code knows nothing about structures, all just byte offsets from a pointer
long get_i(struct rec *r) {
    return r->i;
}
get_i:
        movq 16(%rdi), %rax
        ret
long* addr_of_i(struct rec *r) {
    return &(r->i);
}
addr_of_i:
        lea 16(%rdi), %rax
        ret
struct rec** addr_of_next(struct rec *r) {
    return &(r->next);
}
addr_of_next:
        lea 24(%rdi), %rax
        ret
int* find_addr_of_array_elem (struct rec *r, long index) {
    return &r->a[index];
}
find_addr_of_array_elem:
        lea (%rdi, %rsi, 4), %rax
        ret

3.2 Data Alignment

  • suppose a processor always fetches 8 bytes from an address that must be a multiple of 8
    • if every double is guaranteed to have a memory address that is a multiple of 8, then it's guaranteed to take only a single operation to read
    • otherwise, it would take two operations if a double were split across two 8-byte blocks
  • this kind of behavior is typical of hardware interfacing between the processor and memory
    • hence, systems institute alignment restrictions to improve memory performance
    • Intel recommends data alignment to improve performance
  • x86-64 alignment principle: any primitive object of \(K\) bytes must have an address that is a multiple of \(K\)
  • this means for structures, the compiler sometimes must insert gaps between fields to maintain alignment (internal fragmentation)
    • even if this padding isn't required within a structure, it sometimes must be added to the end to ensure an array of structures is aligned (external fragmentation)
      • each structure has alignment requirement \(K_{max}\) = largest alignment of any element
      • counts array elements individually as elements
      • padding added to align array of structures

struct-defrag.png

struct-align-exercise.png