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]
andsea_m[3][2]
, but underneath- Mem[
sea + 20*index + 4*digit
] vs Mem[ Mem[sea_m + 8*index
]+ 4*digit
]
- Mem[
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 aliastypedef 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; }
- 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
- if every
- 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
- 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)