Memory and Addresses
Memory (RAM) is a giant array of 1 byte-sized storage locations.
Each byte storage location has an integer address.
0would be the lowest address, but we don't want programs to access address0. -E.g., in C, when we create a null pointer (int *p = NULL), it is typically represented by address0. This way, we can makeNULLmean that it is inaccessible/non-existent.- The highest address is determined by number of address bits processor uses:
- 32-bit processors ⇒ addresses have 32 bits, so it can directly address bytes (4GB).
- 64-bit processors ⇒ addresses have 64 bits, so it can directly access bytes = 16,777,216 TB
- This is a huge address space, more than what actual systems have. However, tens-hundreds of GB of physical memory is not uncommon.
Alignment
To store the value of a n-bit word in memory, n/8 contiguous bytes are used.
The address of the first byte is the address of the overall word. It's like how a pointer points to the first byte of a series of bytes.
"Natural" Alignment: Typically, an n-byte word must have an address that is an exact multiple of n.
- E.g., when you have an 8 byte data type, the first byte of that variable (the address) should be a multiple of 8.
- A char (1 byte) can be placed anywhere.
- When variables with different alignment requirements are defined, the compiler may insert padding to ensure correct alignment.
- In C/C++, this is done automatically. In assembly, you do it yourself.
- When you define fields, the data is allocated in the order that the compiler reads it.
Attempt to load or store an n-byte word at an address that is not a multiple of n is an unaligned access.
- Best case: The x86 apporach is that the access works, but there is reduced performance.
- Worst case: runtime exception that kills the program. The OS kernal will forcibly terminate the progam.
C Pointer
A pointer in C is just a memory addresses.
- The address-of operator (
&), when applied to a variable, yields a pointer to the variable (i.e., the address of the first memory byte that is part of the variable's storage). - The dereference operator (
*), when applied to a pointer value (address), refers to the variable whose storage location is indicated by the address
#include <stdio.h>
#include <stdlib.h>
long g;
int main(void) {
long* p = malloc(sizeof(long));
long x;
int a, b;
short c, d, e, f;
scanf("%ld %ld %ld %d %d %hd %hd %hd %hd",
p, &g, &x, &a, &b, &c, &d, &e, &f);
long sum = *p + g + x + a + b + c + d + e + f;
printf("%ld\n", sum);
printf("%p\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n",
p, &g, &x, &a, &b, &c, &d, &e, &f);
return 0;
}
Remember, in C you can store variables at different addresses
- global (
long g;) - heap (
long* p = malloc(sizeof(long));)- Stored pretty close to global
- stack (
long x;)- The addresses of the
intvariables will differ by 4 while theshortvariables differ by 2.
- The addresses of the
Bitwise Operations
Bitwise operations operate on the binary (bit-level) representation of an integer data value
- Logical operations: and (
&), or (|), exclusive or (^), complement (~) - Shifts: left shift (
<<), right shift (>>)
Bitwise operations are useful because they allow precise manipulations of data values at the level of individual bits. Used when:
- Selecting arbitrary bits
- Clearing or setting arbitrary bits
Some important bitwise idioms:
- Set bit
nof variablexto1:x |= (1 << n); - Set bit
nof variablexto0:x &= ~(1 << n); - Get just the lowest
nbits of variablex:x & ~(~0U << n)
Linked from Computer System Fundamentals