1 / 28

Today’s Material

Today’s Material. Layout of Data Objects in Memory Memory: How it looks Address and byte-ordering of variables Little-endian/Big-endian conventions Pointers AddressOf (&) operator Dereferencing void * Changing Function Arguments through Pointers Function Pointers. Memory: How it looks.

ulani
Download Presentation

Today’s Material

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Today’s Material • Layout of Data Objects in Memory • Memory: How it looks • Address and byte-ordering of variables • Little-endian/Big-endian conventions • Pointers • AddressOf (&) operator • Dereferencing • void * • Changing Function Arguments through Pointers • Function Pointers

  2. Memory: How it looks • Let’s start with a snapshot of memory with N bytes Address 0 1 2 3 N-1 8-bits

  3. Address … … c1 100 65 ... ... ... c2 200 66 … … Layout of char variables • Let’s declare and initialize 2 char variables char c1; char c2; c1 = ‘A’; /* same as c1 = 65 */ c2 = 66; /* same as c2 = ‘B’ */ • Recall that a char variable occupies 1 byte in memory • So here is how c1&c2 will look like in memory • We are assuming that c1 is stored at address 100 and c2 is stored at address 200

  4. Printed output: c1: A, c1: 65, &c1: 100 c2: B, c2: 66, &c2: 200 Layout of char variables (cont) … … Address c1 100 65 ... ... ... c2 200 66 … … char c1; char c2; c1 = ‘A’; /* same as c1 = 65 */ c2 = 66; /* same as c2 = ‘B’ */ printf(“c1: %c, c1: %d, &c1: %d\n”, c1, c1, &c1); printf(“c2: %c, c2: %d, &c2: %d\n”, c2, c2, &c2);

  5. Layout of Larger Objects • What about objects that span 2 or more bytes? • short – 2 bytes • int – 4 bytes • long – 4 or 8 bytes • float – 4 bytes • double – 8 bytes • For these objects, 2 conventions must be established • What will be the address of the object • How will we order the bytes in memory

  6. … Address c1 100 65 ... ... c2 200 66 ... ... &s 300 0x12 s 301 0x34 ... ... &i 0x12 400 401 0x34 i Printed output: 0x56 402 s: 1234, &s: 300 i: 12345678, &i: 400 0x78 403 … … (1) Address of Larger Objects • We store multi-byte objects contiguously in memory and let the address of the object be the smallest address of the bytes used short s = 0x1234; /* 2 bytes long */ int i = 0x12345678; /* 4 bytes long */ printf(“s: %hx, &s: %d\n”, s, &s); printf(“i: %x, &i: %d\n”, i, &i);

  7. … … … Address Address c1 c1 100 100 65 65 ... ... ... ... c2 c2 200 200 66 66 ... ... ... ... &s &s 300 300 0x12 0x34 s s 301 301 0x12 0x34 ... ... ... ... &i &i 0x78 0x12 400 400 401 401 0x34 0x56 i i 0x34 0x56 402 402 0x78 0x12 403 403 … … … … (2) Byte Order of Larger Objects Big-endian Little-endian

  8. Pointers • What if I want to store the address of an object in a variable and refer to the object through its address? • Enter pointer variables char *p = &c1; char *q = &c2; short *ps = &s; int *pi = &i; • Pointers are like any other variables in that they store values • But the value they store is the address of another variable, that is, a memory address • p = 100, q = 200, ps = 300, pi = 400

  9. Pointers (more) • How many bytes does a pointer occupy in memory? • On 32-bit machines, 4-bytes • On 64-bit machines, 8-bytes • Since the content of a pointer is a memory address, we can visualize a pointer pointing to the data object whose address it contains • Next slide for illustration

  10. p 100 q 200 ps 300 pi 400 Pointers (Pictorially) • Assume we have the following initializations … … … … Address Address c1 c1 100 100 65 65 ... ... ... ... c2 c2 200 200 66 66 char *p = &c1; char *q = &c2; short *ps = &s; int *pi = &i; ... ... ... ... 300 300 0x34 0x34 s s 301 301 0x12 0x12 ... ... ... ... • Then we can depict p, q, ps and pi pointing to variables c1, c2, s and i 0x78 0x78 400 400 401 401 0x56 0x56 i i 0x34 0x34 402 402 0x12 0x12 403 403 … … … … Memory

  11. Printed output: &c1: 100, p: 100 &c2: 200, q: 200 &s: 300, ps: 300 &i: 400, pi: 400 Pointers (Pictorially) … … … … Address Address char *p = &c1; char *q = &c2; short *ps = &s; int *pi = &i; p c1 c1 100 100 65 65 100 ... ... ... ... q c2 c2 200 200 66 66 200 ... ... ... ... ps 300 300 0x34 0x34 300 s s printf(“&c1: %d, p: %d\n”, &c1, p); printf(“&c2: %d, q: %d\n”, &c2, q); printf(“&s: %d, ps: %d\n”, &s, ps); printf(“&i: %d, pi: %d\n”, &i, pi); 301 301 0x12 0x12 ... ... ... ... pi 400 0x78 0x78 400 400 401 401 0x56 0x56 i 0x34 0x34 402 402 0x12 0x12 403 403 … … … … Memory

  12. Printed output: c1: C, *p: C c2: D, *q: D s: 5000, *ps: 5000 i: 6000, *pi: 6000 Dereferencing (Indirection) • What if I want to refer to the object pointed to by a pointer • Simply use the dereferencing operator *p = ‘C’; /* same as c1 = ‘C’ */ *q = ‘D’; /* same as c2 = ‘D’ */ *ps = 5000; /* same as s = 5000 */ *pi = 6000; /* same as I = 6000 */ printf(“c1: %c, *p: %c\n”, c1, *p); printf(“c2: %c, *q: %c\n”, c2, *q); printf(“s: %x, *ps: %x\n”, s, *ps); printf(“d: %x, *pi: %x\n”, i, *pi);

  13. Address Address p c1 100 ‘A’ 100 q ‘B’ c2 200 200 Initial Condition Address Address p c1 100 ‘E’ 100 q ‘B’ c2 200 100 After Assignment Pointer Assignment • We can make a pointer point to another object after initialization • Consider the following example: char *p = &c1; char *q = &c2; c1 = ‘A’; *q = ‘B’; q = p; /* same as q=&c1 since p=&c1*/ *q = ‘E’; printf(“c1: %c, *p: %c, *q: %c\n”, c1, *p, *q);

  14. Pointers (Recap) • Pointers store memory addresses • All pointers have the same size • 4-bytes or 8-bytes • The type of the pointer (char *, int *) is only a hint to the compiler • It tells the compiler the type of object stored at the memory location • This allows the compiler to access as many bytes as the type of object that the pointer points to during dereferencing • *pi = 1;  modifies 4 bytes since pi’s type is int * • *p = ‘D’  modifies 1 byte since p’s type is char *

  15. 2 400 400 401 401 0x56 i 0x34 402 402 0x12 403 403 0xcd 400 400 401 401 0xab i 402 402 0x34 403 403 0x12 Pointers (Example) p = (char *)&i; /* same as p = (char *)pi; *p = 2; /* Changes only the first byte of the integer I to 2. It does not change the other 3 bytes */ ps = (short *)&i; *ps = 0xabcd; /* Changes the first 2 bytes of integer i to 0xabcd. The last 2 bytes are not changed. */

  16. Address p pp c1 100 ‘A’ 200 &p c2 200 ‘R’ Pointers to Pointers • Just like other objects, pointers have memory addresses • This means that I can have another pointer contain the memory address of a pointer • This would be called a pointer to a pointer char c1 = ‘A’, c2 = ‘B’; char *p = &c1; char **pp = NULL; pp = &p; *pp = &c2; /* make p point to c2. Same as p=&c2 */ **pp = ‘R’; /* same as *p = ‘R’ or c2 = ‘R’ */

  17. Alignment • Some architectures require an integer/float variable to start at a 4-byte boundary • Otherwise the hardware cannot access the variable • So when you dereference a pointer, make sure that the proper alignment is available • For x86, alignment is not required • For Sparc, alignment is required • For example, assume that a char variable has address 102, which is not divisible by 4 • Typecasting this address to a int * and dereferencing it will work on a x86 machine, but will crash your program in a sparc machine

  18. void * • Sometimes you do not know the type of object stored at a memory location • So you want to declare a generic pointer • This is where void * is used void *pv = NULL; pv = (void *)&v; /* assign pv the address of v */ • You cannot dereference a void * pointer • That is, *pv = 8 is invalid • The reason is obvious: The compiler does not know the type of object stored at the memory location pointed to by “pv”. • So the compiler does not know how many bytes to access with *pv (can be 1 byte, 2 bytes, 4 bytes?)

  19. Dereferencing void * • To dereference a void *, you need to first typecast it to a known pointer type • This allows the compiler to determine how many bytes to access char *p = NULL; short *ps = NULL; p = (char *)pv; *p = 65; /* Changes 1 byte at the address pointed to by p & pv */ ps = (short *)pv; *ps = 65; /* Changes 2 bytes at the address pointed to by ps & pv */ /* Typecast first, dereference later */ *((int *)pv) = 70; /* changes 4 bytes at the address pointed to by pv */

  20. Dereferencing: Final Note • The bottom line with dereferencing is • When you dereference a pointer, you must let the compiler know the type of object you are referring to • That is the compiler must know the size of the object being referred to • The pointer type gives this information to the compiler • Without knowing the object type, the compiler cannot generate correct code because dereferencing an untyped pointer would be ambiguous

  21. Functions and Pointers • Recall from our discussion on functions that function arguments are passed by value. • That is, function arguments are copied to the corresponding function parameter • Any changes to the parameter within the function does not affect the argument void decompose(float x, int int_part, float frac_part){ int_part = (int)x; frac_part = x – int_part; } void main(void){ int i = 0; float f = 0.0; decompose(2.35, i, f); printf(“int_part: %d, frac_part: %.2f\n”, i, f); } /* Prints: int_part: 0, frac_part: 0.00 */

  22. Changing Arguments within Functions • How do we change arguments i&f within function decompose then? • The idea is to pass the addresses of i&f (instead of their values) and to refer to i&f through their addresses by pointer dereferencing void decompose(float x, int *p_int_part, float *p_frac_part){ *p_int_part = (int)x; *p_frac_part = x – *p_int_part; } /* end-decompose */ void main(void){ int i = 0; float f = 0.0; decompose(2.35, &i, &f); printf(“int_part: %d, frac_part: %.2f\n”, i, f); } /* end-main */ /* Prints: i: 2, f: 0.35 */

  23. Printed output: x: 2, y: 3 Changing Arguments within Functions • This way, we can change any argument within a function by passing its address and referring to the argument by pointer dereferencing /* swaps the contents of 2 ints */ void swap(int *a, int *b){ int tmp; tmp = *a; *a = *b; *b = tmp; } /* end-swap */ void main(void){ int x=3, y=2; swap(&x, &y); printf(“x: %d, y: %d\n”, x, y); } /* end-main */

  24. Printed output: x: 3, *p1: 3, y: 2, *p2: 2 x: 3, *p1: 2, y: 2, *p2: 3 Changing Arguments within Functions /* swaps the contents of 2 pointers */ void swap(int **a, int **b){ int *tmp; tmp = *a; *a = *b; *b = tmp; } /* end-swap */ void main(void){ int x=3, y=2; int *p1=&x, *p2=&y; printf(“x: %d, *p1: %d, y: %d, *p2: %d\n”, x, *p1, y, *p2); swap(&p1, &p2); printf(“x: %d, *p1: %d, y: %d, *p2: %d\n”, x, *p1, y, *p2); } /* end-main */

  25. Function Pointers • C allows you to declare pointers to functions & to invoke those functions through the pointer • The address of a function is the name of the function itself • Next slide for an example…

  26. Function Pointers: Example int Add(int x, int y){return x+y;} int Subtract(int x, int y){return x-y;} int Multiply(int x, int y){return x*y;} void main(void){ /*fptr is a function pointer to functions with prototype F(int, int)*/ int (*fptr)(int, int); int x; fptr = Add; x = fptr(3, 2); printf(“fptr: %p, Add: %p, x: %d\n”, fptr, Add, x); fptr = Subtract; x = fptr(3, 2); printf(“fptr: %p, Subtract: %p, x: %d\n”, fptr, Subtract, x); fptr = Multiply; x = fptr(3, 2); printf(“fptr: %p, Multiply: %p, x: %d\n”, fptr, Multiply, x); } /* end-main */

  27. Pointers - Summary • Pointers store memory addresses • All pointers have the same size • 4-bytes or 8-bytes • You take the address of a variable using the addressOf operator (&) • &i • You can refer to an object using its address using pointer dereferencing operator (*) • int *p=&i; • *pi = 4; • *(&i)=4

  28. Pointers - Summary • NULL • means an invalid memory address • void * • means that we do not know the type of object stored at the memory location pointed to by the pointer • Changing Arguments Within Functions • To change the value of an argument within a function, pass the argument’s address instead of its value and refer to the argument inside the function by pointer dereferencing • Function Pointers • A function’s name is its address • You can invoke functions by function pointers

More Related