Seeking better explaination of Pointers * &

Pointers are one of the more complicated subjects for beginners in learning C, and it is possible to write the vast majority of Arduino sketches without ever encountering pointers. However for manipulating certain data structures, the use of pointers can simplify the code, and and knowledge of manipulating pointers is handy to have in one's toolkit.

That is the explaination given for both (*) dereference and (&) reference operators in the Reference section. Can someone add to this with maybe some examples or other links to descriptive information about the subject?

I'm working my way through the Webserver Tutorial and came across the single (&) ampersand and my little hamster fell off his wheel so to speak.

There is so much information on this topic all over the internet, but as a small taster:

  • symbol after the type signifies its a pointer, so this is a pointer:
char *pointer_p;

Instead of storing the data directly, a pointer only stores the address at which the data resides. So on its own a pointer isn't of much use. The & symbol is used to get the address of a variable:

char *pointer_p = NULL;  // currently points to nothing, does not contain a valid memory address
char data[] = "TEST";      // array of chars containing the word "TEST" taking up 5 bytes
pointer_p = &data[0];      // pointer_p is assigned the address of the first element of data, now points to "TEST", pointer takes 2 bytes on this platform.

So in the above, the pointer_p is declared, but points at nothing.
We declare and initialise a char array to contain "TEST".
We then get the address of the char array with &, and assign it to pointer.
Because pointer_p contains an address and not the data, its type is only 2 bytes in size.

References are slightly different. To really confuse things, they use the same symbol used to get the address, &.

I personally never use references, you can do everything references can do and more using pointers, but here is a short explanation:

int value = 20;
int &rValue = value;

rValue is a reference to value, like a pointer, it occupies 2 bytes. Unlike a pointer, you can use it directly as you would the actual variable.

There are a few more restrictions you can read up on when using references, such as you have to initialise them when you declare them, and you cannot re-seat them to point at another memory location.

I haven't covered how to de-reference pointers, or how to access what they are pointing to using the ->, or why these are useful when passing into functions. Its such a massive topic you really do have to read up on it. I can however answer any specific questions you might still have?[/code]

tammytam:

int &rValue = value;

This doesn't look right to me. Does it compile?

aarg:
This doesn't look right to me. Does it compile?

Please say you're joking

myggle:
That is the explaination given for both (*) dereference and (&) reference operators in the Reference section. Can someone add to this with maybe some examples or other links to descriptive information about the subject?

I'm working my way through the Webserver Tutorial and came across the single (&) ampersand and my little hamster fell off his wheel so to speak.

The single ampersand sign can have two meanings in C/C++:

  1. Bitwise AND
  2. Address operator ("address-of...")

Might it be possible, that you have difficulties understanding that "& as bitwise and" and "& as adress-of-operator" are two different things?

aarg:
This doesn't look right to me. Does it compile?

Why don't you try it? And why doesn't it look right to you?

The single ampersand sign can have two meanings in C/C++:

  1. Bitwise AND
  2. Address operator ("address-of...")

Might it be possible, that you have difficulties understanding that "& as bitwise and" and "& as adress-of-operator" are two different things?

And the reference type ...

myggle:
That is the explaination given for both (*) dereference and (&) reference operators in the Reference section. Can someone add to this with maybe some examples or other links to descriptive information about the subject?

I'm working my way through the Webserver Tutorial and came across the single (&) ampersand and my little hamster fell off his wheel so to speak.

Take a look at this demo. Hopefully it will help.

// demo - free software

char buffer [64];

void setup (void)
{
    while (!Serial);
    Serial.begin (115200);

    uint16_t x;
    uint16_t *y; // note the "*"

    x = 0x1234; // set x to 0x1234
    y = &x; // pointer y == address of x in ram

    sprintf (buffer, "value of \"x\" (x) = 0x%02x\n\n", x);
    Serial.print (buffer);

    sprintf (buffer, "address of \"x\" in ram (&x) = 0x%02x\n\n", &x);
    Serial.print (buffer);

    sprintf (buffer, "value of pointer to address of \"x\" (y) = 0x%02x\n\n", y);
    Serial.print (buffer);

    sprintf (buffer, "value of what pointer \"y\" points to (*y) = 0x%02x\n\n", *y);
    Serial.print (buffer);

    sprintf (buffer, "address of pointer \"y\" in ram (&y) = 0x%02x\n\n", &y);
    Serial.print (buffer);

}

void loop (void)
{
    // nothing
}

...and it prints:

[b]value of "x" (x) = 0x1234

address of "x" in ram (&x) = 0x21f2

value of pointer to address of "x" (y) = 0x21f2

value of what pointer "y" points to (*y) = 0x1234

address of pointer "y" in ram (&y) = 0x21f4[/b]

(edit to add): Notice that the address in ram of x and y are sequential and 2 bytes apart. This is because both variables are 16 bit, so they each take up 2 bytes. Note that the variables happen to be sequential, but they don't have to be and you should never count on it.

X resides in 0x21F2 and 0x21F3,
Y resides in 0x21F4 and 0x21F5.

The AVR processor is little-endian which means, for example, looking at memory you would see this:

0x21F2 - 34
0x21F3 - 12
0x21F4 - F2
0x21F5 - 21

On a "big-endian" processor such as Motorola, the "0x1234" would be stored like this:

0x21F2 - 12
0x21F3 - 34

See that 0x21F4 and 0x21F5 (the variable Y) contain the value "0x21F3" which is the address of X in memory. Because of this, I can print the value of Y (Y), the value of what Y points to (*Y) or the address in ram where Y is stored (&Y).

Hope all this helps.

tammytam:
Why don't you try it? And why doesn't it look right to you?

I always do try eventually. I was just hoping there was a simple answer. It seems to just create duplicate names for a variable. If you can't modify the address like a pointer, I'm just having a hard time understanding what the usefulness would be. Since:

  int &rValue = value;

creates an rValue, apparently equivalent to:

  int rValue = value;

since both end up valued as 20 in this example.

 Serial.begin(9600);
  Serial.println("Demo");
  int value = 20;
  int &rValue = value;
  int sValue = value;
  rValue += 1;
    sValue += 1;
  Serial.println(rValue);
  Serial.println(sValue);

prints out:

Demo
21
21

In this case, I can't see anything different that "&" brings to the table except an additional confusing character.

@Krupski, your example has no "&" on the left side of an assignment. It's a different use.

Not quite, it creates a reference, so this is possible:

void func( int &var )
{
  var += 10;
}

void loop()
{
  int test = 0;
  func( test );  // test is now 10 in this scope
  func( test );  // test is now 20 in this scope
  func( test );  // test is now 30 in this scope
}

Without that &, test would be 0 in that scope.

*Edit: Also in your example, if you were to change rValue, it would change value, so if you changed rValue before assigning value to sValue, you'd have a different value in sValue!

aarg:
@Krupski, your example has no "&" on the left side of an assignment. It's a different use.

I left it off because I thought it would just confuse the picture, since it's really a "mirror image" kind of thing.

Notice that something like [b]uint16_t &z;[/b] is impossible because it has to reference something at compile time, otherwise what is it? It has to be &z = (equals) something!

Here's "Z" added to my "demo":

char buffer [64];

void setup (void)
{
    while (!Serial);
    Serial.begin (115200);

    uint16_t x = 0x1234;
    uint16_t *y = &x;
    uint16_t &z = *y;

    sprintf (buffer, "value of \"x\" (x) = 0x%02x\n\n", x);
    Serial.print (buffer);

    sprintf (buffer, "address of \"x\" in ram (&x) = 0x%02x\n\n", &x);
    Serial.print (buffer);

    sprintf (buffer, "value of pointer \"*y\" to address of \"x\" (y) = 0x%02x\n\n", y);
    Serial.print (buffer);

    sprintf (buffer, "value of what pointer \"*y\" points to (*y) = 0x%02x\n\n", *y);
    Serial.print (buffer);

    sprintf (buffer, "value of reference \"&z\" to pointer *y (z) = 0x%02x\n\n", z);
    Serial.print (buffer);

    sprintf (buffer, "address of reference \"&z\" to pointer *y (&z) = 0x%02x\n\n", &z);
    Serial.print (buffer);

}

and it prints:

[b]value of "x" (x) = 0x1234

address of "x" in ram (&x) = 0x21f4

value of pointer "*y" to address of "x" (y) = 0x21f4

value of what pointer "*y" points to (*y) = 0x1234

value of reference "&z" to pointer *y (z) = 0x1234

address of reference "&z" to pointer *y (&z) = 0x21f4[/b]

Notice that &z is the same address as *y...we could go nuts (&((&X))) but all that is... is X :slight_smile:

Yeah, I recognize the use in a function call. I've used it numerous times. That is a memory lapse on my part. But in that case, no initialization is performed. I can't think of any other useful application (not to say there aren't any).

Wow, that is interesting. I tried a few tests myself..

 void setup() {
  Serial.begin(9600);
  while(!Serial);
  Serial.println("Demo");
  char value[] = "Test";
  char &rValue = value[0];
  char *sValue = value;
  //char &tValue = value; //compiler won't let this compile "non-const reference"

  Serial.println(value);        //prints the string "Test"
  Serial.println(sValue);       //prints the string "Test"
  Serial.println(rValue);       //prints the first char "T"
  Serial.println(*sValue);      //prints the first char "T"
  Serial.println((int)sValue);  //prints the memory address of value (2805 in my test)
  
  Serial.println("Add 1...");
  Serial.println(value+1);      //prints the last part of the string "est"
  Serial.println(value[0]+1);   //prints 85, which is the ASCII for the letter after "T"
  Serial.println((char) (value[0]+1));   //prints the "U"
  Serial.println(rValue+1);     //prints 85
  Serial.println(*(sValue+1));  //prints the 2nd char "e" - equivalent to value[1] or sValue[1]
 }
 void loop() {}

I think the reason why int &rValue = value; is legal is because it's often better in C++ to write a function with references void f(int &rValue) instead of pointers. This declares rValue as a reference, which allows you to use it inside the function without the dereference operator (*) as if it was a normal variable. Essentially you are making a promise to the compiler that you will use it like a normal variable and you won't do any pointer arithmetic to make it point to something else.

Since int &rValue is legal as a function argument, it's also legal in other parts of the code, even if there's no good reason to do that.

Thank you all so much for the explanations and examples. I'm sure it's not hard to tell that I'm a noob, but I was unable to resolve the matter in the reference, which BTW is my primary "reference" for learning materials directly relating to Arduino. Never having coded anything before, I get confused remembering which of the various languages make up the Arduino language as I often see references to quite a few, but arduino.cc's reference section I am very confident is applicable. Unfortunately it's not always that easy to quickly link to what one is viewing, and I do apologize for not first doing my diligence and hitting up Google, that's usually my second stop, archived posts third and finally forum, but I had a brain fart.

Any chance a moderator can select a few of the examples and explanations given above and append them to the "pointers" page? It's not efficient to keep all this good info buried in archived posts, especially when one needs to discern which archived info is good and which isn't.