12-Pointers

advertisement
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
1
Memory: How it looks
Address
• Let’s start with a
snapshot of
memory with N
bytes
0
1
2
3
N-1
8-bits
2
Layout of char variables
• Let’s declare and initialize 2 char variables
char
char
c1 =
c2 =
c1;
c2;
‘A’; /* same as c1 = 65 */
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
…
…
Address
c1
65
...
...
...
100
c2
66
200
…
…
3
Layout of char variables (cont)
…
…
Address
c1
65
...
...
...
100
c2
66
200
…
…
char c1;
char c2;
c1 = ‘A’; /* same as c1
c2 = 66; /* same as c2
printf(“c1: %c, c1: %d,
printf(“c2: %c, c2: %d,
= 65 */
= ‘B’ */
&c1: %d\n”, c1, c1, &c1);
&c2: %d\n”, c2, c2, &c2);
Printed output:
c1: A, c1: 65, &c1: 100
c2: B, c2: 66, &c2: 200
4
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
1. What will be the address of the object
2. How will we order the bytes in memory
5
(1) Address of Larger Objects
short s = 0x1234;
int i = 0x12345678;
/* 2 bytes long */
/* 4 bytes long */
• We store multi-byte objects
contiguously in memory and let
the address of the object be
the smallest address of the
bytes used
c1
c2
65
...
...
66
...
...
s
printf(“s: %hx, &s: %d\n”, s, &s);
printf(“i: %x, &i: %d\n”, i, &i);
Printed output:
s: 1234, &s: 300
i: 12345678, &i: 400
…
…
0x12
0x34
Address
100
200
300
301
&s
400
401
402
403
&i
...
...
i
0x12
0x34
0x56
0x78
…
…
6
(2) Byte Order of Larger Objects
…
…
c1
c2
65
...
...
66
...
...
s
0x34
0x12
100
i
…
…
c1
200
300
301
...
...
0x78
0x56
0x34
0x12
…
…
Address
400
401
402
403
Little-endian
c2
&s
65
...
...
66
...
...
s
0x12
0x34
Address
100
200
300
301
&s
400
401
402
403
&i
...
...
&i
i
0x12
0x34
0x56
0x78
…
…
Big-endian
7
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 =
char *q =
short *ps
int *pi =
•
&c1;
&c2;
= &s;
&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
8
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
9
Pointers (Pictorially)
•
Assume we have
the following
initializations
char *p =
char *q =
short *ps
int *pi =
•
&c1;
&c2;
= &s;
&i;
Then we can
depict p, q, ps
and pi pointing
to variables
c1, c2, s and i
…
…
p
q
ps
pi
c1
100
c2
200
300
Address
65
...
...
66
...
...
s
0x34
0x12
100
200
300
301
...
...
400
i
0x78
0x56
0x34
0x12
400
401
402
403
…
…
Memory
10
Pointers (Pictorially)
…
…
char *p =
char *q =
short *ps
int *pi =
printf(“&c1: %d, p:
printf(“&c2: %d, q:
printf(“&s: %d, ps:
printf(“&i: %d, pi:
Printed output:
&c1: 100, p:
&c2: 200, q:
&s: 300, ps:
&i: 400, pi:
&c1;
&c2;
= &s;
&i;
%d\n”,
%d\n”,
%d\n”,
%d\n”,
p
q
&c1, p);
&c2, q);
&s, ps);
&i, pi);
ps
pi
c1
100
200
300
c2
...
...
66
...
...
s
0x34
0x12
100
200
300
301
...
...
400
i
100
200
300
400
65
Address
0x78
0x56
0x34
0x12
…
…
400
401
402
403
Memory 11
Dereferencing (Indirection)
•
What if I want to refer to the object pointed
to by a pointer
–
Simply use the dereferencing operator
*p = ‘C’;
*q = ‘D’;
*ps = 5000;
*pi = 6000;
/*
/*
/*
/*
same
same
same
same
printf(“c1: %c, *p:
printf(“c2: %c, *q:
printf(“s: %x, *ps:
printf(“d: %x, *pi:
Printed output:
c1: C, *p: C
c2: D, *q: D
s: 5000, *ps: 5000
i: 6000, *pi: 6000
as
as
as
as
c1 = ‘C’
c2 = ‘D’
s = 5000
I = 6000
%c\n”,
%c\n”,
%x\n”,
%x\n”,
*/
*/
*/
*/
c1, *p);
c2, *q);
s, *ps);
i, *pi);
12
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’;
Address
p
100
c1
‘A’
100
q
200
c2
‘B’
200
Initial Condition
Address
q = p; /* same as q=&c1 since p=&c1*/
*q = ‘E’;
printf(“c1: %c, *p: %c, *q: %c\n”,
c1, *p, *q);
p
100
c1
‘E’
100
q
100
c2
‘B’
200
After Assignment
13
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 *
14
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.
*/
i
i
2
0x56
0x34
0x12
400
401
402
403
0xcd
0xab
400
401
402
403
0x34
0x12
15
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’ */
Address
pp
&p
p
200
c1
‘A’
100
c2
‘R’
200
16
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
17
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?)
18
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 */
19
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
20
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 */
21
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 */
22
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 */
Printed output:
x: 2, y: 3
23
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 */
Printed output:
x: 3, *p1: 3, y: 2, *p2: 2
x: 3, *p1: 2, y: 2, *p2: 3
24
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…
25
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 */
26
Pointers - Summary
•
•
Pointers store memory addresses
All pointers have the same size
–
•
You take the address of a variable using the
addressOf operator (&)
–
•
4-bytes or 8-bytes
&i
You can refer to an object using its address
using pointer dereferencing operator (*)
–
–
–
int *p=&i;
*pi = 4;
*(&i)=4
27
Pointers - Summary
•
•
•
•
NULL
–
means an invalid memory address
–
means that we do not know the type of object
stored at the memory location pointed to by the
pointer
void *
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
28
Download