Pointers/Arrays/Pointer Arithmetic
| Type | Quiz 4 Material |
|---|
Notes
Pointers
Pointers are variables that contain a memory address
- Act like dot operator
- No runtime checking
- Pointers contain memory addresses (or NULL)
- If you try to dereference a pointer containing NULL you will cause a segmentation fault
- They also have a type
- This refers to the type of data AT that memory address.
- There is also a special case: void pointers which point to a memory address but there is no type for the data at the address
* is the symbol for pointer type
& is the âaddress ofâ operator; you use it to obtain a pointer to an existing data item, used in place of object references
An & symbol can be used to find the address of a code element such as a variable or function, and then assigned to a pointer:
char *x; // declares that x is a pointer to a char.
char **y; // declares that y is a pointer to a pointer to a char.
void *z; // declares that z is a pointer to an unspecified type
char i = 97; // i stores the value 97 or 'a'
char *x = &i; // x stores the address of the variable i
char **y = &x; // y stores the address of the variable x
void *z = &i; // z also stores the address of the variable iexample1
int y = 7; int z = 9; int *x = &y; x = &z; //One possible assembly implementation: .orig x3000 LEA R0, Z ; R0 = x3022 ST R0, X ; x = x3022 = &z .end .orig x3020 X .fill x3021 Y .fill 7 ; address x3021 Z .fill 9 ; address x3022 .end
&b â> Address of b. & means the address of
* is a pointer to the address
To reference what a pointer points to, we use the dereference operator * (acts like âindirectâ in LC-3 loads and stores)
- In a type declaration, * means âpointer toâ and is part of the type
- ** would mean pointer to pointer to
- In an expression, * is the âdereferenceâ operator â it acts as the âindirectâ in the LC-3 loads and stores
- When applied to a pointer, * makes the expression mean âWhat this pointer points toâ
Pointer Declaration:
int i, x;
int *px = &x;
Declaration: *px â> pointer to px
Pointer Expression:
i = *px;
Expression: get the data px points to
Dereferencing a Pointer
int y = 7;
int *x = &y;
R0 = *x;
R0++;
*x = R0;// C canât directly modify registers like this, but this is C pseudocode Ì\_(ă)_/ Ì
//One possible assembly implementation:
.orig x3000
LDI R0, X ; R0 = mem[mem[x3021]]
ADD R0, R0, 1 ; R0++;
STI R0, X ; mem[mem[x3021]] = R0;
.end
.orig x3020
X .fill x3021
Y .fill 7 ; address x3021
.endPointers and Arrays
Arrays in c (like in assembly) is a fixed-size sequence of same-typed elements
int arr[5]; // declares arr as an array of 5 ints
char arr[] = {'A',66,'C'}; // arr is an array of 3 chars
int *arr[3]; // arr is an array of 3 int pointersint a[10]; â> a is an array, size 10, of int
int *p; â> p is a pointer to int (memory address)
In an expression, an array name becomes a constant pointer to the first element
- a is the memory address where the array starts
- a canât be changed (but its contents can)
- In other words, itâs the address of a[0] (or specifically &a[0])
- arr[0] = 7 is equivalent to arr = 7
Arrays can decay to pointers to their first element. And you can treat a pointer like an array since array notation is a shorthand for pointer arithmetic
- But they are not the same:
- Arrays are fixed-size and cannot be reassigned. They refer to a specific region of memory.
- A pointer is just a variable that holds a memory address.
int *ptr;
int arr[10];
*(ptr + x) == ptr[x];
*(arr + x) == arr[x];Strings and Pointers
Strings in C are accessed through a pointer to the first character
C-strings are null-terminated (just like in assembly)
char *a; //denotes a pointer to a character
char *a = "Hello"; // null terminator is added implicitly
char b[] = {'W', 'o', 'r', 'l', 'd', '\0'};char str[6] = âHelloâ;
char *str; â> pointer to the first character of str
Pointer Arithmetic
Adding and subtracting to/from pointers; it adds offset times the size of the pointer type.
int *p = &i;
p = p + 1; â> is interpreted as p = p + 1 * sizeof(*p);
(if p is an int pointer, p + 1 is the address of the next int â the address in p is incremented by the size of an int)
int *y;
y + 2 evaluates to y + 2*sizeof(int)
y[2] (array notation) is an equivalent shorthand
int arr[4];
arr[3] = 12; is equivalent to
*(arr + 3) = 12; is equivalent to
arr + 3*sizeof(int)
This also applies to arrays:
- int arr[4];
- arr[3] = 12;
- *(arr + 3) = 12
- The above evaluates to arr + 3*sizeof(int) to get the correct physical address of the fourth element.
- pointer arithmetic automatically takes the sizeof the element type into account in pointer arithmetic
- Ex 1:
int arr[4]; arr[3] = 12; //Same as prev line...evaluates to *(arr + 3*sizeof(int))
- Ex 2:
int *y; y+2; // evaluates to y+2*sizeof(int)
- Ex 1:
Pointers to Structs
â> Operator
The left operand must be a pointer to struct and the right operand a struct member
A shorthand for *
Implementing a call-by-reference function with pointers
SWAP function
No pointers(bad!)
Arrays
- They declare a fixed-size sequence of same-typed elements
- They are laid out consecutively in memory
- You can also make arrays of pointers
- Unlike in Java, you cannot count on uninitialized data to be NULL or 0; it is simply un-initialized (just like assembly!)
- Like in assembly, arrays are essentially just two pieces of information: the address of the first element and the length of the array
- âAddress of first elementâ == âpointer to first elementâ
- Arrays decay to pointers: we can choose to lose the length and implicitly convert an array to a pointer to its first element
Find the dimension of an Array IN SCOPE(not a parameter):
sizeof(ary) / sizeof(ary[0])
If the array is passed as a parameter:
Must pass the length with the array
Initializing Arrays
Use values in braces "{}"
int ib[5] = {5,4,3,2,1};
or:
int ib[] = {5,4,3,2,1,0};
or
char cb[] = {'x', 'y', 'z'};
or
char cb[] = "hello"; â> cb: h e l l o \0, size = 6
or
char *cb = "hello"; â> pointer to the address that contains "hello", size is the size of the memory address
Array as arguments
char s[10] = âHelloâ;
void test(char *s) {
printf(â%s\nâ, s);
}
int main(int argc, char *argv[]) {
test(s);
test(&s[0]);
}
To pass an array into a function, we pass a pointer to the first element.
Note that an array name in an expression (function call) is automatically promoted to a pointer to its first element.
This includes strings (arrays of char)
Sizeof()
- Know how the sizeof operator works in C
- Know that the sizes of common datatypes like int are not guaranteed
- Know that the size of a struct is the sum of the sizes of its components (we will be assuming that the compiler does not pad structs with empty space)
Questions & Answers
Practice question: Assume we're on a machine where sizeof(int) = 4 and sizeof(int *) = 4. Given
int c[3];Which of the following are equivalent to c[1]? There may be more than one correct answer.
&(c + 1)[0].
*(c + 1)
*(int **)((char *) c + 4)
*((char *) c + 4)
(c + 3)[-2]
*(int *)((char *) c + 1)
1[c]
Aiden Solution
- &(c + 1)[0] - Incorrect ; address of element at index 1 rather than the element itself
- (c + 1) Correct
- (int *)((char *) c + 4); Correct - ???
- ((char *) c + 4); Incorrect (Dereferencing a char pointer when we want an int)
- ((char *) c + 1); Incorrect (Dereferencing a char pointer when we want an int). Also offset is incorrect, that 1 is being multiplied by sizeof(char), which is 1, not sizeof(int), which is 4
- &c[1][0]; Correct
Student & TA Solution
- &(c + 1)[0] Incorrect ; address of element at index 1 rather than the element itself
- (c + 1) (this one)
- (int *)((char *) c + 4)(this one)
- ((char *) c + 4)
- (c + 3)[-2] (This one) 3-2 = 1
- (int *)((char *) c + 1)
- &c[1][0] (this one)
Practice question: Assume we're on a machine where sizeof(int) = 4. What would the following program print?
int main(void) {
 struct coord {
  int x; // 0x4 bytes
  int y; // 0x4 bytes
 };
 struct coord coords[10];  // assume this starts at address 0x4000
 printf("%p\n", &coords[4].y);
}Student & TA Solution
0x4000 + #36 = 0x4024
- why is &(c+1)[0] not equal to c[1] but &c[1][0] is equal to c[1]?
TA Solution
Basically, the pointer arithmetic is the same but using brackets (like [0]) also dereferences, so the second expression dereferences twice and the first only once. &(c+1)[0] gets the ADDRESS of the first index, while c[1] and &c[1][0] gets the actual VALUE of the first index




























