tags:

views:

231

answers:

6
+3  Q: 

C Pointer Question

I am really getting confused on how pointers work. I am trying to write short little programs that will illuminate exactly how they work and I am having some troubles. For example:

char c[3]; //Creates an array of 3 bytes - the first 2 bytes can be used for characters and the 3rd would need to be used for the terminating zero

*c = 'a'; //sets c[0] to 'a'
*c++; //moves the pointer to c[1]
*c = 'b'; //sets c[1] to 'b'
*c++; //moves the pointer to c[2]
*c = '\0' //sets c[2] to the terminating zero

Obviously this code is not correct, or else I wouldn't be polling the forum :)

I am just having some troubles understanding this from a book, can anyone briefly explain the concept?

+15  A: 

c is not a pointer, it's an array. Although the name of an array decays to a pointer in most array contexts you can't treat an array name as a modifiable pointer. The result of the decay is just a temporary (technically an rvalue).

For this reason you can't apply ++ to the name of an array. If you want increment a pointer, use a pointer:

char *d = c;
Charles Bailey
Close paren missing, or did you intend to write more after "rvalue"? s/can/can't in the last paragraph?
dash-tom-bang
You should add that `*c++` is incrementing `c[0]`. If it were actually a pointer, it should be `c++`.
Jack Kelly
@Jack Kelly: No, postfix `++` take precedence over unary `*`. The pointer is being incremented although the dereference is redundant because the value is not used.
Charles Bailey
@Charles Bailey: Thanks for the clarification.
Jack Kelly
A: 

Try

c++;
*c = 'b';
c++;
*c = '\0';

The * operator is trying to dereference the pointer. All you need to do is move the pointer, then do your assignment.

Jeff Barger
However, in his program, `c` is an array, not a pointer.
Tyler McHenry
`c++` will not compile in this case. `c` is the name of an array, which decays to a pointer *value* (under the right circumstances). Trying to increment it is a bit like trying to increment 3.
Jerry Coffin
Regardless of language, this sort of thing should be discouraged.
dash-tom-bang
+4  A: 

First of all, c here is not a pointer, it's an array. Arrays can in some contexts be used like pointers, but they are not the same thing. In particular, you can use *c (as if it were a pointer) to access the value in the first position, but since c is not really a pointer, you can't change where c points by using c++.

Second, you are misunderstanding what * means. It's not just a decoration you use when using pointers. As an operator, it means "dereference", i.e. give me access to what is being pointed to. Therefore when you are manipulating the pointer itself (by, for example, incrementing it) and not manipulating the pointed-to data, you need not use it.

Here is what you probably wanted:

char c[3]; // Creates an array of 3 bytes - the first 2 bytes can be used for characters 
           // and the 3rd would need to be used for the terminating zero
char* p_c; // Creates a character pointer that we will use to refer into the c array

p_c = &c[0]; // Assign the address of the first element of the c array to the p_c pointer.
             // This could also be "p_c = c", taking advantage of the fact that in this
             // is one of the circumstances in which an array can be treated as if it were 
             // a pointer to its first element

*p_c = 'a'; //sets c[0] to 'a'
p_c++;      //moves the pointer to c[1] (note no *)
*p_c = 'b'; //sets c[1] to 'b'
p_c++;      //moves the pointer to c[2] (note no *)
*p_c = '\0' //sets c[2] to the terminating zero
Tyler McHenry
@Tyler: My apologies -- I hadn't updated the page for a few minutes.
Jerry Coffin
`p_c` and `` or `p_c = c;`
caf
Rob
@Rob See caf's comment. He is correct. I'll update my code to reflect that change.
Tyler McHenry
+2  A: 

Pointers and arrays are different things in C. The source of confusion is that arrays get converted (decay, as the Standard names it) to pointers at the slightest provocation. It's called "decay" because it loses some information about array type (namely, its size).

Let's see...

void f( char* p );

char c_array [3];       // define an array
char *c_ptr = c_array;  // define a pointer and set it to point at the beginning of the array
                        // here array "decays" to pointer

*c_ptr = '1';
assert(c_array[0] == '1');
assert(c_ptr[0] == '1');   // this works too... in fact, operator [] is defined
                           // for pointers, not arrays, so in the line above array
                           // decays to pointer too.

++c_ptr;                   // move the pointer
//++c_array;               // -- this won't compile, you can't move the array

*c_ptr++ = '2';
*c_ptr   = '\0';
assert(c_array[1] == '2');
assert(c_array[2] == 0);

assert(sizeof(c_array) == 3);  // no decay here!

assert(sizeof(c_ptr) == sizeof(void*));  // a pointer is just a pointer

f(c_array);                // array-to-pointer decay, again


// now, what happens here?
void g( char param [100] )
{
    ++param;  // it works!
              // you can't pass an array as a parameter by value.
              // The size in the parameter declaration is ignored; it's just a comment.
              // param is a pointer.

    assert(sizeof(param) == sizeof(void*));
              // yes, it's just a pointer

    assert(*param == '2'); // in the call below
}


g(c_array);   // array-to-pointer decay, again

Hope this helps a bit.

(Note that I've intermingled declarations and statements for the purpose of illustration. You'll have to rearrange things a bit to make it a valid C program).

EDIT: added sizeof examples

atzz
@atzz - thanks a bunch for the code but a lot of this goes over my head. I don't really understand the decay concept and haven't worked with the assert function yet. I do appreciate the attempt though
Rob
+1  A: 

Stepping through programs in a debugger and inspecting the values of everything was helpful to my understanding of pointers. Also draw lots of pictures on your whiteboard to solidify your understanding. The thing that really cemented it for me was learning assembly and bringing up a MIPS from scratch...

Try stepping through this in your debugger, and draw some diagrams on your whiteboard to trace out the execution.

#include <stdio.h>

int main()
{
    char c_arr[3] = {'a', 'b', '\0'};       // Array of 3 chars.
    char* c_ptr = c_arr; // Now c_ptr contains the address of c_arr.

    // What does it mean that c_ptr "contains the address of c_arr"?
    // Underneath all this talk of "pointers" and "arrays", it's all
    // just numbers stored in memory or registers. So right now, c_ptr is
    // just a number stored somewhere in your computer.

    printf("%p\n", c_ptr);
    // I got `0xbf94393d`. You'll get something different each time you run it.

    // That number (0xbf94393d) is a particular memory location. If you
    // want to use the contents of that memory location, you use the *
    // operator.
    char ch = *c_ptr;
    // Now ch holds the contents of whatever was in memory location 0xbf94393d.
    // You can print it.

    printf("%c\n", ch);
    // You should see `a`.

    // Let's say you want to work with the next memory location. Since
    // the pointer is just a number, you can increment it with the ++ operator.
    c_ptr++;
    // Let's print it to see what it contains.

    printf("%p\n", c_ptr);
    // I got 0xbf94393e. No surprises here, it's just a number -- the
    // next memory location after what was printed above.

    // Again, if we want to work with the value we can use the *
    // operator. You can put this on the left side of an assignment
    // to modify the memory location.
    *c_ptr = 'z';

    // Since c_ptr was pointing to the middle of our array when we
    // performed that assignment, we can inspect the array to see
    // the change.
    printf("%c\n", c_arr[1]);

    // Again, c_ptr is just a number, so we can point it back to
    // where it was. You could use -- for this, but I'll show -=.
    c_ptr -= 1;

    // We can also move by more than one. This will make the pointer
    // contain the address of the last memory location in the array.
    c_ptr = c_ptr + 2;

    return 0;
}

Here's my attempt at a picture. This box is your computer's memory. Each location in memory is assigned a number, we call that number an address.

++++++++++++++++++++++++++++++++++++++++++++
|  NAME   |   ADDRESS    |   VALUE         |
+=========+==============+=================+
|  c_arr  |  0xbf94393d  |   'a'           |
|         |  0xbf94393e  |   'b'           |
|         |  0xbf94393f  |   '\0'          |
+---------+--------------+-----------------+
|  c_ptr  +  <someaddr>  |   0xbf94393d    |
+------------------------------------------+

When you access, say, c_arr[0], you are working with the top row in the table. Note that c_ptr has as its value the address of the top row in the table. When you say *c_ptr, you are telling the CPU to use 0xbf94393d as the address to operate on. So *c_ptr = 'z' is a bit like saying "Hey, go to 0xbf94393d and leave a 'z' there" -- on this street the addresses are really big.

bstpierre
I wish i could accept more than one answer - this is a fantastic description - thank you so much.
Rob
+1  A: 

Name of the array can be treated as the pointer to it's first element, though its a constant pointer hence it cannot be made to point to any other location. So c++ is not allowed.

007