CMPE-013/L Introduction to C Programming Gabriel Hugh Elkaim Winter 2015 and memory Pointer/array equivalency Pointer arithmetic and the stack and strings Arrays of ointers 1
Syntax tye *trname; How to Create a Pointer Variable In the context of a declaration, the *merely indicates that the variable is a ointer tyeis the tye of data the ointer may oint to Pointer usually described as a ointer to tye int *iptr; // Create a ointer to int int *iptr, x; // Create a ointer to int and an int float *fptr1, *fptr2; // Create 2 float ointers Initialization To set a ointer to oint to another variable, we use the &oerator (address of), and the ointer variable is used withoutthe dereference oerator *: = &x; This assigns the address of the variable xto the ointer (now oints to x) Note: must be declared to oint to the tye of x(e.g. int x; int *;) 2
Dereferencing When accessing the data ointed to by a ointer, we use the ointer withthe dereference oerator *: y = *; This assigns to the variable y, the value of what is ointing to (xfrom the last slide) Using *, is the same as using the variable it oints to (e.g. x) Dereferencing examle int x = 6, *; // int and a ointer to int = &x; // Assign the address of x * = 5; // Same as x = 5; &xis a constant memory value It reresents the address of x The address of xwill never change is a variable ointer to int It can be assigned the address of any int It may be assigned a new address any time 3
Dereferencing examle int x = 6, *; // int and a ointer to int = &x; // Assign the address of x * = 5; // Same as x = 5; *reresents the data ointed to by *may be used anywhere you would use x *is the dereference oerator, also called the indirection oerator In the ointer declaration, the only significance of *is to indicate that the variable is a ointer rather than an ordinary variable Another view Contents of the Mailbox (variable x) of Mailbox (&x) 105 Bank of Mailboxes (memory locations) 4
Another view Contents of the Mailbox (x, *) of Mailbox (&x, ) = &x; 105 Bank of Mailboxes (memory locations) Another view Contents of the Mailbox (x, *) of Mailbox (&x, ) * = 2; 105 Bank of Mailboxes (memory locations) 5
Dereferencing non-rimitives Comlex x = 0.6, 1.2, *; = &x; ->re = 5; ->rereresents the data ointed to by ->remay be used anywhere you would use x.re ->is the structure dereference oerator, equivalent to (*).re In the ointer declaration, the only significance of *is to indicate that the variable is a ointer rather than an ordinary variable Dereferencing non-rimitives void MyFunc(Comlex *x) Comlex t = *x; x->re /= t.re * t.re + t.im * t.im; x->im /= t.re * t.re + t.im * t.im; 6
Dereferencing non-rimitives x Stack (to) t Return address x MyFunc() void MyFunc(Comlex *x) Comlex t = *x; x re im Dereferencing non-rimitives t.re t.im Stack (to) Return address x MyFunc() void MyFunc(Comlex *x) Comlex t = *x; 7
How Work int x, y; int *; x = 0xDEAD; y = 0xBEEF; = &x; * = 0x0100; = &y; * = 0x0200; Variable at x y 0x08B8 0x08BC 0x08C0 0x08C4 0x08C8 0x08CC 0x08D0 0x08D4 How Work int x, y; int *; x = 0xDEAD; y = 0xBEEF; = &x; * = 0x0100; = &y; * = 0x0200; Variable at x y 0000 DEAD 0x08B8 0x08BC 0x08C0 0x08C4 0x08C8 0x08CC 0x08D0 0x08D4 8
How Work int x, y; int *; x = 0xDEAD; y = 0xBEEF; = &x; * = 0x0100; = &y; * = 0x0200; Variable at x y 0000 DEAD 0000 BEEF 0x08B8 0x08BC 0x08C0 0x08C4 0x08C8 0x08CC 0x08D0 0x08D4 How Work int x, y; int *; x = 0xDEAD; y = 0xBEEF; = &x; * = 0x0100; = &y; * = 0x0200; Variable at x y 0000 DEAD 0000 BEEF 0000 08BC 0x08B8 0x08BC 0x08C0 0x08C4 0x08C8 0x08CC 0x08D0 0x08D4 9
How Work int x, y; int *; x = 0xDEAD; y = 0xBEEF; = &x; * = 0x0100; = &y; * = 0x0200; Variable at x y 0000 0100 0000 BEEF 0000 08BC 0x08B8 0x08BC 0x08C0 0x08C4 0x08C8 0x08CC 0x08D0 0x08D4 How Work int x, y; int *; x = 0xDEAD; y = 0xBEEF; = &x; * = 0x0100; = &y; * = 0x0200; Variable at x y 0000 0100 0000 BEEF 0000 08C0 0x08B8 0x08BC 0x08C0 0x08C4 0x08C8 0x08CC 0x08D0 0x08D4 10
int x, y; int *; x = 0xDEAD; y = 0xBEEF; = &x; * = 0x0100; = &y; * = 0x0200; How Work Variable at x y 0000 0100 0000 0200 0000 08C0 0x08B8 0x08BC 0x08C0 0x08C4 0x08C8 0x08CC 0x08D0 0x08D4 and Arrays A Quick Reminder Array elements occuy consecutive memory locations int x[3] = 1, 2, 3; x[0] x[1] x[2] FFFF FFFF 0000 0001 0000 0002 0000 0003 FFFF FFFF 0x07FC 0x0800 0x0804 0x0808 0x080C can rovide an alternate method for accessing array elements 11
and Arrays Initializing a Pointer to an Array The array name evaluates to the address of its first (0 th ) element If we declare the following array and ointer variable: int x[5] = 1, 2, 3, 4, 5; int *; We can initialize the ointer to oint to the array using either of these methods: = x; // Works only for arrays = &x[0]; // Same as the above and Arrays A Preview of Pointer Arithmetic Incrementing a ointer will move it to the next element of the array int x[3] = 1, 2, 3; int *; = x; ++; x[0] x[1] x[2] FFFF FFFF 0000 0001 0000 0002 0000 0003 FFFF FFFF 0x07FC 0x0800 0x0804 0x0808 0x080C More on this in just a bit 12
and Arrays A Preview of Pointer Arithmetic Incrementing a ointer will move it to the next element of the array int x[3] = 1, 2, 3; int *; = x; ++; x[0] x[1] x[2] FFFF FFFF 0000 0001 0000 0002 0000 0003 0000 0800 0x07FC 0x0800 0x0804 0x0808 0x080C More on this in just a bit and Arrays A Preview of Pointer Arithmetic Incrementing a ointer will move it to the next element of the array int x[3] = 1, 2, 3; int *; = x; ++; x[0] x[1] x[2] FFFF FFFF 0000 0001 0000 0002 0000 0003 0000 0804 0x07FC 0x0800 0x0804 0x0808 0x080C More on this in just a bit 13
Pointer Arithmetic Incrementing Incrementing or decrementing a ointer will add or subtract a multile of the number of bytes of its base tye If we have: float x; float * = &x; ++; We will the address of incremented by 4 since a floatoccuies 4 bytes float *tr; Pointer Arithmetic Incrementing tr = &a; ++tr; Incrementing tr moves it to the next sequential float array element (4 bytes ahead) float a[0] float a[1] float a[2] float a[3] float a[4] float a[5] float a[6] float a[7] float a[8] 0x0050 0x0054 0x0058 0x005C 0x0060 0x0064 0x0068 0x006C 0x0070 0x0074 Words 14
Pointer Arithmetic Larger Jums Adding or subtracting any other number with the ointer will change it by a multile of the number of bytes of its tye If we have short int x; short int * = &x; += 3; We will get the address of incremented by 6since a short intvariable occuies 2 bytes of memory float *tr; Pointer Arithmetic Larger Jums tr = a; Adding 6 to tr moves it 6 float array elements ahead (24 bytes ahead) float a[0] float a[1] float a[2] float a[3] float a[4] float a[5] float a[6] float a[7] float a[8] 0x0050 0x0054 0x0058 0x005C 0x0060 0x0064 0x0068 0x006C 0x0070 0x0074 tr += 6; 16-bit Data Memory Words 15
Pointer Arithmetic long long x[] = 1, 2, 3; long long * = x; * += 4; ++; * = 0xDEAD1234BEEF; ++; * = 0xF1D04321F00D; -= 2; * = 0xBAD0000F00D1; x[0] x[1] x[2] 0000 0001 0000 0002 0000 0003 0000 0800 0x07FC 0x0800 0x0804 0x0808 0x080C 0x0810 0x0814 0x0818 Pointer Arithmetic long long x[] = 1, 2, 3; long long * = x; * += 4; ++; * = 0xDEAD1234BEEF; ++; * = 0xF1D04321F00D; -= 2; * = 0xBAD0000F00D1; x[0] x[1] x[2] 0000 0005 0000 0002 0000 0003 0000 0800 0x07FC 0x0800 0x0804 0x0808 0x080C 0x0810 0x0814 0x0818 16
Pointer Arithmetic long long x[] = 1, 2, 3; long long * = x; * += 4; ++; * = 0xDEAD1234BEEF; ++; * = 0xF1D04321F00D; -= 2; * = 0xBAD0000F00D1; x[0] x[1] x[2] 0000 0005 0000 0002 0000 0003 0000 0808 0x07FC 0x0800 0x0804 0x0808 0x080C 0x0810 0x0814 0x0818 Pointer Arithmetic long long x[] = 1, 2, 3; long long * = x; * += 4; ++; * = 0xDEAD1234BEEF; ++; * = 0xF1D04321F00D; -= 2; * = 0xBAD0000F00D1; x[0] x[1] x[2] 0000 0005 1234 BEEF 0000 DEAD 0000 0003 0000 0808 0x07FC 0x0800 0x0804 0x0808 0x080C 0x0810 0x0814 0x0818 17
Pointer Arithmetic long long x[] = 1, 2, 3; long long * = x; * += 4; ++; * = 0xDEAD1234BEEF; ++; * = 0xF1D04321F00D; -= 2; * = 0xBAD0000F00D1; x[0] x[1] x[2] 0000 0005 1234 BEEF 0000 DEAD 0000 0003 0000 0810 0x07FC 0x0800 0x0804 0x0808 0x080C 0x0810 0x0814 0x0818 Pointer Arithmetic long long x[] = 1, 2, 3; long long * = x; * += 4; ++; * = 0xDEAD1234BEEF; ++; * = 0xF1D04321F00D; -= 2; * = 0xBAD0000F00D1; x[0] x[1] x[2] 0000 0005 1234 BEEF 0000 DEAD 4321 F00D 0000 F1D0 0000 0810 0x07FC 0x0800 0x0804 0x0808 0x080C 0x0810 0x0814 0x0818 18
Pointer Arithmetic long long x[] = 1, 2, 3; long long * = x; * += 4; ++; * = 0xDEAD1234BEEF; ++; * = 0xF1D04321F00D; -= 2; * = 0xBAD0000F00D1; x[0] x[1] x[2] 0000 0005 1234 BEEF 0000 DEAD 4321 F00D 0000 F1D0 0000 0800 0x07FE 0x0800 0x0802 0x0804 0x0806 0x0808 0x080A 0x080C Pointer Arithmetic long long x[] = 1, 2, 3; long long * = x; * += 4; ++; * = 0xDEAD1234BEEF; ++; * = 0xF1D04321F00D; -= 2; * = 0xBAD0000F00D1; x[0] x[1] x[2] 000F 00D1 0000 BAD0 1234 BEEF 0000 DEAD 4321 F00D 0000 F1D0 0000 0800 0x07FE 0x0800 0x0802 0x0804 0x0806 0x0808 0x080A 0x080C 19
Post-Increment/Decrement Syntax Rule Care must be taken with resect to oerator recedence when doing ointer arithmetic: Syntax Oeration Descrition by *++ *(++) (*)++ Post-Increment Pointer Post-Increment data ointed to by Pointer z = *(++); is equivalent to: z = *; = + 1; z = (*)++; is equivalent to: z = *; * = * + 1; Post-Increment / Decrement Syntax int x[3] = 1, 2, 3; int y; int * = x; x[0] y = 5 + *(++); y = 5 + (*)++; Remember: *(++) is the same as *++ x[1] x[2] y 0000 0001 0000 0002 0000 0003 0000 0800 0x07FC 0x0800 0x0804 0x0808 0x080C 0x0810 0x0814 0x0818 20
Post-Increment / Decrement Syntax int x[3] = 1, 2, 3; int y; int * = x; x[0] y = 5 + *(++); y = 5 + (*)++; Remember: *(++) is the same as *++ x[1] x[2] y 0000 0001 0000 0002 0000 0003 0000 0800 0000 0006 0x07FC 0x0800 0x0804 0x0808 0x080C 0x0810 0x0814 0x0818 Post-Increment / Decrement Syntax int x[3] = 1, 2, 3; int y; int * = x; x[0] y = 5 + *(++); y = 5 + (*)++; Remember: *(++) is the same as *++ x[1] x[2] y 0000 0001 0000 0002 0000 0003 0000 0804 0000 0006 0x07FC 0x0800 0x0804 0x0808 0x080C 0x0810 0x0814 0x0818 21
Post-Increment / Decrement Syntax int x[3] = 1, 2, 3; int y; int * = x; x[0] y = 5 + *(++); y = 5 + (*)++; Remember: *(++) is the same as *++ x[1] x[2] y 0000 0001 0000 0002 0000 0003 0000 0804 0000 0007 0x07FC 0x0800 0x0804 0x0808 0x080C 0x0810 0x0814 0x0818 Post-Increment / Decrement Syntax int x[3] = 1, 2, 3; int y; int * = x; x[0] y = 5 + *(++); y = 5 + (*)++; Remember: *(++) is the same as *++ x[1] x[2] y 0000 0001 0000 0003 0000 0003 0000 0804 0000 0007 0x07FC 0x0800 0x0804 0x0808 0x080C 0x0810 0x0814 0x0818 22
Pre-Increment/Decrement Syntax Rule Care must be taken with resect to oerator recedence when doing ointer arithmetic: Syntax Oeration Descrition by ++* *(++) ++(*) Pre-Increment Pointer Pre-Increment data ointed to by Pointer z = *(++); is equivalent to: = + 1; z = *; z = ++(*); is equivalent to: * = * + 1; z = *; Pre-Increment / Decrement Syntax int x[3] = 1, 2, 3; int y; int * = x; x[0] y = 5 + *(++); y = 5 + ++(*); Remember: *(++) is the same as *++ x[1] x[2] y 0000 0001 0000 0002 0000 0003 0000 0800 0x07FC 0x0800 0x0804 0x0808 0x080C 0x0810 0x0814 0x0818 23
Pre-Increment / Decrement Syntax int x[3] = 1, 2, 3; int y; int * = x; x[0] y = 5 + *(++); y = 5 + ++(*); Remember: *(++) is the same as *++ x[1] x[2] y 0000 0001 0000 0002 0000 0003 0000 0804 0x07FC 0x0800 0x0804 0x0808 0x080C 0x0810 0x0814 0x0818 Pre-Increment / Decrement Syntax int x[3] = 1, 2, 3; int y; int * = x; x[0] y = 5 + *(++); y = 5 + ++(*); Remember: *(++) is the same as *++ x[1] x[2] y 0000 0001 0000 0002 0000 0003 0000 0804 0000 0007 0x07FC 0x0800 0x0804 0x0808 0x080C 0x0810 0x0814 0x0818 24
Pre-Increment / Decrement Syntax int x[3] = 1, 2, 3; int y; int * = x; x[0] y = 5 + *(++); y = 5 + ++(*); Remember: *(++) is the same as *++ x[1] x[2] y 0000 0001 0000 0003 0000 0003 0000 0804 0000 0007 0x07FC 0x0800 0x0804 0x0808 0x080C 0x0810 0x0814 0x0818 Pre-Increment / Decrement Syntax int x[3] = 1, 2, 3; int y; int * = x; x[0] y = 5 + *(++); y = 5 + ++(*); Remember: *(++) is the same as *++ x[1] x[2] y 0000 0001 0000 0003 0000 0003 0000 0804 0000 0008 0x07FC 0x0800 0x0804 0x0808 0x080C 0x0810 0x0814 0x0818 25
The arentheses determine what gets incremented/decremented: Modify the ointer itself Pre- and Post- Increment/Decrement Summary *(++) or *++ and *(++) or *++ Modify the value ointed to by the ointer ++(*) and (*)++ Initialization Ti If a ointer isn't initialized to a secific address when it is created, it is a good idea to initialize it as NULL This will revent it from unintentionally corruting a memory location if it is accidentally used before it is initialized int * = NULL; NULis the character '\0'but NULLis the value of a ointer that oints to nowhere 26
and the Stack Beware the stack Memory addresses may not always be valid es referring to the stack have a lifetime tied to that variables scoe Only global, static, and ointers returned by malloc() will always be valid You should almost never use the memory addresses of variables on the stack main() foo() z and the Stack Beware the stack Stack (to) Return address x y b a Return address function int *foo(int x, int y) int z = x + (++y); return &z; caller int *main(void) int a = 6, b = 19; int *c = foo(a, b); rintf("%d\n", *c); 27
main() z and the Stack Beware the stack Stack (to) Return address x y b a Return address function int *foo(int x, int y) int z = x + (++y); return &z; caller int main(void) int a = 6, b = 19; int *c = foo(a, b); rintf("%d\n", *c); main() uts() and the Stack Beware the stack Stack (to) Return address s b a Return address function int *foo(int x, int y) int z = x + (++y); return &z; caller int a = 6, b = 19; int *c = foo(a, b); uts("hey!"); rintf("%d\n", *c); 28
and Strings So far, we have worked with strings strictly as arrays of char Strings may be created and used with ointers much more elegantly String declaration with a ointer: char *str = "PIC32MX"; str 0000 91C0 0x08C0 Imlementation varies deending on comiler and architecture used. 3 C I P \0 X M 2 3343 4950 0058 4D32 0x91C0 0x91C4 and Strings When initialized, a ointer to a string oints to the first character: char *str = "Microchi"; str M i c r o c h i \0 str += 4 Increment or add an offset to the ointer to access subsequent characters 29
and Strings may also be used to access characters via an offset: char *str = "Microchi"; *str == 'M' M i c r o c h i \0 *(str + 4) == 'o' Pointer always oints to "base address" Offsets used to access subsequent chars and Strings Pointer versus Array: Initialization at Declaration Deending on variable tye, art of the variable is constant : Pointer to String Constant char *str = "PIC"; : Character array char str[] = "PIC"; The NULL character '\0' is automatically aended to strings in both cases (array must be large enough). 30
and Strings Pointer versus Array: Initialization at Declaration : Pointer Variable char *str1 = "PIC"; char str2[] = "PIC"; str1 str2 RAM 0x9D0008C0 0xA0000FB0 P 0xA0000FB4 I 0xA0000FB5 C 0xA0000FB6 \0 0xA0000FB7 ROM P 0x9D0008C0 I 0x9D0008C1 C 0x9D0008C2 \0 0x9D0008C3 and Strings Pointer versus Array: Assignment in Code An entire string may be assigned to a ointer A character array must be assigned character by character : Pointer Variable char *str; str = "PIC"; : Array Variable char str[4]; str[0] = 'P'; str[1] = 'I'; str[2] = 'C'; str[3] = '\0'; Must exlicitly add NUL character '\0' to array. 31
and Strings Comaring Strings If you want to test a string for equivalence, the natural thing to do is: if (str == "Microchi") This is notcorrect, though it might aear to work sometimes This comares the address in strto the address of the string literal "Microchi" The correct way is to use the strcm()function in the standard library which comares strings character by character Arrays of Declaration An array of ointers is an ordinary array variable whose elements haen to all be ointers. char *[4]; This creates an array of 4 ointers to char The array []itself is like any other array The elements of [], such as [1], are ointers to char 32
Arrays of Array Elements are Themselves [0] [1] [2] [3] 9D00 3FC0 9D00 3FC3 9D00 3FC7 9D00 3FCC 3FC0 O n \0 3FC3 O f f \0 3FC7 M a i n \0 3FCC A u x \0 Arrays of Initialization A ointer array element may be initialized just like its ordinary variable counterart: [0] = &x; Or, when working with strings: [0] = "My string"; 33
char [4][] = "On", "Off", "Main", "Aux" ; Arrays of Different from Two-dimensional Array This creates an two-dimensional array of chars Amount of memory for every string the same Arrays of Array Elements are Sequential [0] [1] [2] [3] 0000 6E4F 6666 4F00 614D 0000 4100 6E69 0000 7875 3FA4-3FA8 O n \0 \0 \0 3FA9-3FAD O f f \0 \0 3FAE-3FB2 M a i n \0 3FB3-3FB7 A u x \0 \0 34
Arrays of Dereferencing To use the value ointed to by a ointer array element, just dereference it like you would an ordinary variable: y = *[0]; Using *[0]is the same as using the object it oints to, such as xor the string literal "My String" from the revious slide Arrays of Accessing Strings int i = 0; char *str[] = "Zero", "One", "Two", "Three", "Four", "\0"; int main(void) while(*str[i]!= '\0') rintf("%s\n", str[i++]); while(1); 35
Dynamic Memory malloc() free() Dynamic Memory Rationale Memory needs not known at comile time Memory needs to ersist outside of current scoe 36
Syntax void *malloc(size_t size); Dynamic Memory malloc() Request memory of sizebytes Usually returned by sizeof oerator Returns valid ointer or NULL tyedef struct float re; float im; Comlex; Comlex *x = malloc(sizeof(comlex)); Dynamic Memory malloc()'d memory tyedef struct float re; float im; Comlex; Comlex *x = malloc(sizeof(comlex)); rintf("comlexre:%f im:%f\n", x->re, x->im); 37
tyedef struct float re; float im; Comlex; Dynamic Memory malloc()'d memory Comlex *x = malloc(sizeof(comlex)); x->re = 0.0; x->im = 0.0; rintf("comlexre:%f im:%f\n", x->re, x->im); Dynamic Memory The Hea tyedef struct float re; float im; Comlex; Comlex *x = malloc(sizeof(comlex)); Hea (to) 38
Dynamic Memory The Hea tyedef struct float re; float im; Comlex; Comlex *x = malloc(sizeof(comlex)); Comlex *x = malloc(sizeof(comlex)); Comlex *x = malloc(sizeof(comlex)); Comlex *x = malloc(sizeof(comlex)); Comlex *x = malloc(sizeof(comlex)); Hea (to) NULL tyedef struct float re; float im; Comlex; Dynamic Memory NULL ointers Comlex *x = malloc(sizeof(comlex)); x->re = 0.0; x->im = 0.0; rintf("comlexre:%f im:%f\n", x->re, x->im); 39
tyedef struct float re; float im; Comlex; Dynamic Memory NULL ointers Comlex *x = malloc(sizeof(comlex)); Comlex y = *x; tyedef struct float re; float im; Comlex; Dynamic Memory NULL ointers Comlex *x = malloc(sizeof(comlex)); if (x) x->re = 0.0; x->im = 0.0; rintf("comlexre:%f im:%f\n", x->re, x->im); 40
tyedef struct float re; float im; Comlex; Dynamic Memory NULL ointers Comlex *x = malloc(sizeof(comlex)); if (x) x->re = 0.0; x->im = 0.0; rintf("comlexre:%f im:%f\n", x->re, x->im); Syntax void free(void *tr); Dynamic Memory free() Frees memory ointed to by tr Must have been returned by malloc() tyedef struct float re; float im; Comlex; Comlex *x = malloc(sizeof(comlex)); free(x); 41
Dynamic Memory Invalid free()ing // Non-initialized ointers Comlex *x; free(x); // Invalid! // NULL ointers Comlex *y = NULL; free(y); // Invalid! // Non-hea ointers char *z = "Hey!"; free(z); // Invalid! // Hea ointers not returned by malloc() Comlex *w = malloc(sizeof(comlex)); free(&w->re); // Invalid! 42
43
44