Pointers Part 1 Flashcards

(32 cards)

1
Q

Thinking about Addresses

A
  • No surprises:
    – Variables and code are stored in memory
    – Each memory location (typically each byte) has an address
  • The compiler has to:
    – Choose addresses where each thing is stored.
    – Typically allocating one or more bytes to each thing.
    – Use the right addresswhenever we try to access something with its name.
  • When we say : a = b + 1
  • The compiler writes
    code like :
    Get the int at address 26
    Add 1
    Store the result at address 12
  • Normally, this is all hidden from us … but the
    compiler will let you work with addresses if you want to.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
2
Q

Why would you want to?

A
  • There are three general reasons why we need
    to work with addresses:
    – Knowing where something is stored will let you read it and change it.
    – Knowing where something is stored will let you access nearby values in memory.
    – Dynamic memory allocation requires
    us to work with addresses.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
3
Q

Indirection

A
  • Pointers are a mechanism for indirection
  • Two alternatives
    – I could give you some thing (i.e., a value)
    – I could tell you where it is.
  • Instead of working directly with values, we work
    with the locations where values are stored.
    – We can go get the value.
    – We can change the value.
    – We can access nearby values.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
4
Q

Working with addresses

A
  • We’re going to see two new operators:
    – One for asking where things are stored in memory
    – One for accessing what’s stored at particular memory
    locations
  • The address-of operator : &
    – It’s a unary prefix operator that evaluates to the address of its operand
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
5
Q

Storing Addresses

A
  • We have special kinds of variables for storing
    addresses, pointer variables.
  • The * symbol can be used as a type operator.
    It lets us create new types.
    – Syntax: type * identifier;
    int a; -> I can hold an int.
    int * ptr; -> The name of this variable
    is ptr; I can hold the address of an int. Its type is int *
    ptr = &a; And now I do.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
6
Q

Using a Pointer

A
  • Given a pointer, we can access the value it points to.
  • We have the dereference operator: *
    – I know, it looks just like the pointer type operator, but we use this one differently.
    – It’s a unary prefix operator
    – It’s the inverse of &
    ptr -> If I am a pointer
    *ptr -> I’m the thing it points to
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
7
Q
  • and &
A
    • and & work together
      – but they’re inverses of each other.
  • & says “Give me the address of this thing”
    pointer = &thing;
    • says “give me the thing at this address”
      – dereference will give you the thing a pointer
      points to.
      x = *pointer;
      – Or, it will let you change it.
      *pointer = y;
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
8
Q

All Types Can Have Pointers

A

I’m a pointer to a char:
char c = ‘x’;
char * cp = &c;
I’m a pointer to a float:
float f = 22.7;
float * fp = &f;
I’m a pointer to a short
short s = -192;
short * sp = &s;
long * p = &val;
long ** pp = &p;

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q

Pointers to Pointers

A

int a = 5;
int b = 10;
int * pa = &a;
int * pb = &b;
int ** ppa = &pa;
int ** ppb = &pb;

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
10
Q

Type Operator Precedence

A
  • The * in a variable declaration just applies to
    the next variable.
    int * x, y; -> First is a pointer and second is just an int
  • Each pointer variable needs its own *.
    int * x, * y;
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
11
Q

A Pointer to Nowhere

A
  • C defines the constant, NULL
    – It’s a pointer to nothing
    – Like null in Java
    – Defined in stddef.h (and several other headers)
  • You can assign any pointer to NULL
  • You can test a pointer to see if it’s NULL
    – In fact, a NULL pointer evaluates to false,
    convenient.
  • But, don’t try to dereference it
    int *ip = NULL;
    double *dp = NULL;
    if ( ip )
    …;
    double bill = *dp;
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
12
Q

What are They Good For?

A
  • To use some parts of the standard library, you
    have to use pointers.
    – Files, sorting, …
  • We’ve already used them for pass-by reference.
    – We just didn’t describe it that way
    – Now we can understand this technique
    – And use it for ourselves
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
13
Q

Pass-By-Reference

A
  • By passing pointers to functions, we can let them modify the values pointed to.
  • For example, we could write a working swap
    function.
    void swap( int *x, int *y )
    {
    int temp = *x;
    *x = *y;
    *y = temp;
    }
    swap( &a, &b ); -> You have to pass
    addresses to call it.
  • Functions can take a mix of pointer an nonpointer parameter types.
    void clamp(int *x, int bound)
    {
    if ( *x > bound )
    *x = bound;
    }
    Read this as, you need to pass me the address
    of an actual int.
    clamp( &c, 5 );
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
14
Q

How To Segfault

A
  • It’s easy to make pointer mistakes, if you’re
    just trying to get something that compiles
    void clamp(int *x, int bound)
    {
    if ( *x > bound )
    *x = bound;
    }
    int *p;
    int b = 5;
    clamp( p, b );
  • Pointer is not unintialized
  • This will compile … but bad things will happen
    when you run it.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
15
Q

Still Pass-By-Value Underneath

A
  • When you pass a pointer, you’re still passing a value … just a value that happens to be an
    address.
    void hulkSmash(int *x, int *y)
    {
    x = NULL;
    y = NULL;
    }
    int a = 5, b = 10;
    int *ap = &a, *bp = &b;
    hulkSmash( ap, bp );
    printf( “%d %d\n”, *ap, *bp );
  • When we call this function, it gets copies of
    pointers to a and b.
    But, the original pointers in main() are
    unchanged
    This function is just throwing away
    its copies of these pointers.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
16
Q

Returning a Pointer

A
  • A function can return a pointer.
    – Giving the caller a chance to look at the value it points to.
    – Or even to change it.
    int list[ 100 ];
    int *find( int v )
    {
    for (int i = 0; i < 100; i++)
    if ( list[ i ] == v )
    return &list[ i ];
    return NULL;
    }
    int *p = find(30); -> p could be null
17
Q

Pointers as Return Values

A
  • Functions can return pointers.
    – This can be useful … we saw an example on pointer day 1.
  • But, you have to consider …
    – will the destination address still be valid after you return?
  • Here’s a bad idea:
    int *getLarger( int a, int b )
    {
    Here’s your problem,
    these variables go
    away as soon as you
    return.
    if ( a > b )
    return &a;
    else
    return &b;
    }
    int *p = getLarger( x, y );
    This could be
    accessing stack space
    that’s being used for
    something else now.
    printf( “Max: %d\n”, *p );
18
Q

Poking Around in Memory

A
  • Pointers can let us read/write any memory
    location
  • Very useful, but it also makes it easy to write
    some bad code.
    Make up an address, then try
    to write there. Probably, you’d
    never do this on purpose..
    But, if you forget to initialize a
    pointer, you may get the same
    effect.
    int *p = (int *)12345
    int *p = (int *)12345678;
    *p = 100;
    int *q;
    *q = 250;
  • Kind of like buffer overflow errors with arrays.
    – Dereferencing a bad pointer can take you to who- knows-where in memory.
  • Really, something like 12 or 26 isn’t a typical
    memory address.
    – On a 32-bit machine, a pointer has, well, 32 bits.
  • So a pointer might look like: 0xAC8B2E38
    – On a 64-bit machine, a pointer has 64 bits.
  • So a pointer might look like: 0xAC8B2E38F3274E38
  • We can print out the numeric value of a pointer:
    printf( “%p\n”, ptr )
  • Pointers generally all take the same amount of storage … but not all pointers are the same.
19
Q

Type Matters … Sometimes

A
  • Recall, C will let you convert values all day long
    without a complaint.
    char c = ‘a’;
    float f = 1.23;
    short s = 123;
    c = f;
    f = s; –> No warnings here
    s = c; –> The compiler knows to convert among these types.
    But, for pointer types, it will give you a
    warning
    char c = ‘a’;
    float f = 1.23;
    short s = 123;

char *cp = &c; –> These look good.
float *fp = &f;
short *sp = &s;
fp = &c; -> But here, are you sure you know what you’re doing?
sp = &f;
cp = &s;
* A short pointer isn’t the same as a float
pointer.

20
Q

Pointers and Size

A
  • The pointer type keeps up with the type of the thing pointed to.
    – Including the size.
    char c = ‘H’;
    int i = 273;
    char *cPtr = &c;
    int *iPtr = &i;
    *cPtr = ‘J’; –> I change 1 byte
    *iPtr = 291; –> I change 4 bytes
  • With a cast, you can suppress compiler
    warnings for pointer type conversion.
    – But, you may write nonsense into memory.
21
Q

Fun with Pointer Types

A
  • In general, a pointer variable only wants a
    pointer to the same type of value.
    int i, *ip;
    double d, *dp;
    char c, *cp;
    // Pretend we initialize all of these
    i = *cp; –> Fine, a char value can
    convert to an int.
    dp = &i; –> But, a double pointer can’t
    hold the address of an int.
    *cp = *dp; –> A double value can convert
    to a char … if you want.
    &i = ip; -> But, you can never move
    where a variable lives.
22
Q

Sense…

A
  • Some pointer operations make sense.
    int a = 30, b = 50, *px = &a, *py = &b;
    a = *py; // Copy the value py points to into a

*px = 35; // Copy 35 into the value pointed to by px

*px = b; // Copy the value of b into the value px
// points to.

*px = *py; // Copy the value py points to into the
// value px points to.

px = &b; // Copy the address of b into px

px = py; // Make px point to the same thing as py

23
Q

Nonsense

A
  • others, not so much.
    int a = 30, b = 50, *px = &a, *py = &b;
    px = &35; // Numbers don’t have addresses
    a = *35; // Constants don’t make good pointers
    px = 35; // Constants still don’t make good pointers
    a = &b; // An int isn’t good for holding an address
    a = *b; // b (int) doesn’t make a good pointer
    *a = 35; // An int still doesn’t make a good pointer
    &a = py; // Can’t move where a lives.
    a = py; // a (int) isn’t good for holding an address
    a = **py; // *py (int) doesn’t make a good pointer
    px = b; // b (int) doesn’t make a good address
24
Q

Precedence

25
Inventing Types
* In C, * does three jobs – It’s a binary infix operator, for doing multiplication – It’s a unary prefix operator, for dereference pointers – It’s a type operator, for defining new types * Just like [ ] lets us define array types. * In fact, we can invent infinitely many new types … if you gave me enough time. int *p; -> I’m a pointer to an int. int a[] = { 1, 2 }; -> I’m a pointer to a int **pp; -> pointer to an int. int *ap[ 10 ]; ->I wonder what I am.
26
Reading Types
* We can create some complex types – Without even defining classes – And, it will get worse … more interesting. * We need to be able to understand the types we’re creating or looking at. * Two simple rules for reading types – Start at the variable name and work your way out. – Respect type operator precedence (which is the same as operator precedence) int * * pp; -> pointer to a pointer of int int * ap [ 10 ]; -> array of 10 pointers to int
27
Meet const
* In C, const is another type operator. – It says, “Don’t let me change this thing’s value during its lifetime.” int a = 25; int const b = a + 1; ->reads better const int c = b + 1; -> can also do this
28
Const isn’t Constant
* Const variables can get a new value every time they are initialized * So, they aren’t considered constant expressions (compile-time constants) for ( int i = 0; i < 10; i++ ) { const int c = i + 10;<-- You can't use me as a case in a switch statement. ... }
29
Const and Pointers
* With const, we can say what we’d like to be able to do via a pointer. – Remember how to read types. int a = 25, b = 35, c = 45; int const * x = &a; -> I’m a pointer to a const integer. int * const y = &b; -> I’m a const pointer to an integer. int const * const z = &c; -> I’m a const pointer to a const integer. * With const, we can say what we’d like to be able to do via a pointer. – Remember how to read types. int a = 25, b = 35, c = 45; int const * x = &a; *x = 26; -> can't do this int * const y = &b; y = &c; -> or this int const * const z = &c; z = &b; -> or either of these *z = 44;
30
Why Use Const?
* Const is useful in function types. It says what the function plans to do with your values. int f( double const *d ); I’d like to use your double, but I promise not to change it. int f( double const *d ) { int x = *d * *d * 3.14; return x; } See, I just want to look at what d points to. * Const works with arrays – They’re always pass-by-reference – But, you can promise not to change them void look( int n, int const a[] ) { for ( int i = 0; i < n; i++ ) printf( "%d\n", a[ i ] ); } I solemnly swear not to change the contents of your array
31
Using const
* Const is just another part of the type (like ‘unsigned’) * There are conversion rules for const-ness. – You can implicitly convert from a less restrictive type to a more restrictive one. – Going the other way requires a type cast. int* to const int* implicit const int* to int* implicit requires a cast * Const-ness will affect what you can do with a value * We say a program is const correct when: – Functions use const to say which (reference) parameters they might modify and which ones they won’t modify – And whether or not their return (by reference) value can be modified int f( double const *d ) { * d += 1; return 5; }
32
Trying to Cheat
* If you try to violate const-ness, the compiler will notice. * But, const is part of the type … and you can type cast it away. * Think of const as a mechanism for programmers trying to cooperate … – … not a security measure. int f( double const *d ) { * (double *) d += 1; return 5; }