"Pointers make my head spin" etc ...

Rupert909:
Is it only because of the compact and cryptic notation, or is it rooted in a deeper lack of understanding computers?

For me the concept pointers (the addresses of memory locations at which data is stored) is trivially simple. But the ...ker that designed the C/C++ notation and its blatant inconsistencies made it very difficult to follow in that language. For example, why is byte myVal = 5; treated differently from byte myVal[1] = 5;

...R

Robin2:
For me the concept pointers (the addresses of memory locations at which data is stored) is trivially simple. But the ...ker that designed the C/C++ notation and its blatant inconsistencies made it very difficult to follow in that language. For example, why is byte myVal = 5; treated differently from byte myVal[1] = 5;

...R

Because one is a scalar and the other a vector (albeit a very short one)?

Robin2:
For me the concept pointers (the addresses of memory locations at which data is stored) is trivially simple. But the ...ker that designed the C/C++ notation and its blatant inconsistencies made it very difficult to follow in that language. For example, why is byte myVal = 5; treated differently from byte myVal[1] = 5;

...R

Just remember that array variables are poiinters. How hard is that? Assigning integer 5 to a pointer obviously is a dangerous thing to do.

:slight_smile:

Rupert909:
Just remember that array variables are poiinters. How hard is that? Assigning integer 5 to a pointer obviously is a dangerous thing to do.

:slight_smile:

You can treat them like pointers, however the reality is a little more complex.

An array is an array. An array id without a subscript can implicitly cast to a pointer, which points to the first element, but the array id is still an array.

Rupert909:
Just remember that array variables are poiinters. How hard is that?

That part is easy. What I don't understand is why they (whoever "they" are) decided that a simple variable name should not also be a pointer. Under the hood they are both memory addresses. Why treat them differently?

...R

Robin2:
That part is easy. What I don't understand is why they (whoever "they" are) decided that a simple variable name should not also be a pointer. Under the hood they are both memory addresses. Why treat them differently?

...R

Are you asking why "int a = 5;" isn't a pointer?

pYro_65:
Are you asking why "int a = 5;" isn't a pointer?

To be precise I am asking if int a[1] = 5; is treated as a pointer why is int a = 5; treated differently?

...R

int a[1] = 5;

The code above isn't valid. You need to use a brace-enclosed-initializer list.

But no, despite what @rupert909 mentioned, an array is not a pointer. It will cast to a pointer to the first element if used in an expression that accepts a pointer as an operand, however, it is most definitely an array and not a pointer.

Pointers become obvious if you program in assembly language for a while. (one of the reasons that some of us think that learning assembly language is still important, I guess!)

This is memory. Memory consists of a bunch of boxes, each one with an address, and each one having some data as contents.

        Addr    Data
         0       123
         1        42
         2        19
         3         0
         4       255
         5        'C'
         6        'A'
         7        'T'

Defining a variable is equivalent to attaching a name (or "label") to a memory location to make it easier to refer to in your program. Here, we have "answer = 42" and "char pet[] = "CAT";" This all happens in the compiler; the actual runtime binary doesn't know anything about the labels or variable names.

Label   Addr    Data
         0       123
answer:  1        42
         2        19
p:       3         0
         4       255
pet:     5       'C'
         6       'A'
         7       'T'
	 8	   0

we can make variable p be a pointer: "byte *p = 0;" Now, rather than having the value of a variable, p should have the ADDRESS of a variable. By convention, a pointer initialized to 0 (aka "null") is one that doesn't point at anything. But in the above you'll notice that 0 is actually a valid address (containing 123), so at the moment "*p" (read as "the contents at the pointer p" has 123, but address 0 isn't actually anything that we've made a variable, so that's probably some kind of error.
We can say "p=5;" or "p=pet;" or "p=&pet[0];" ("p gets the address of the first element of pet"), and then we have:

Label   Addr    Data
         0       123
answer:  1        42
         2        19
p:       3         5    ; points to pet
         4       255
pet:     5       'C'
         6       'A'
         7       'T'
	 8	   0

Now, "*p" will be 'C'. Note that there is nothing in the pointer to indicate exactly what it might be pointing at, or how long it is. It could be pointing at a character 'C', or at the beginning of string "CAT", or at some numerical value that happens to be the same as the representation of the letter C (67) The compiler tries to keep track of that for you, but again this disappears in the binary.
You can do math and stuff with the pointer. If you add 1, it will now have the value of 6, which is pointing at "A" instead of "C". You could subtract 2, and now it's pointing at itself (seems like a bad idea!)
You can pass p to a function, which would save space over passing all of "CAT".

And so on. That's sort of the basics of pointers. C has its own complications and niceities, like being able to point to things other than single bytes, or using array access syntax to access pointer values (and vis versa.) But chunks of memory with addresses is where you start.

pYro_65:
int a[1] = 5;

The code above isn't valid. You need to use a brace-enclosed-initializer list.

Apologies for the inadvertent error in the syntax.

But no, despite what @rupert909 mentioned, an array is not a pointer. It will cast to a pointer to the first element if used in an expression that accepts a pointer as an operand, however, it is most definitely an array and not a pointer.

In that case it makes even less sense for the compiler not to also cast an ordinary variable name to a pointer when it is "used in an expression that accepts a pointer as an operand"

...R

Robin2:
In that case it makes even less sense for the compiler not to also cast an ordinary variable name to a pointer when it is "used in an expression that accepts a pointer as an operand"

It is just a rule decided for that type and is explained in the section "standard conversions". I'll try my best to give it some sense though:

4.2 Array-to-pointer conversion [conv.array]
1 An lvalue or rvalue of type “array of N T” or “array of unknown bound of T” can be converted to a prvalue of type “pointer to T”. The result is a pointer to the first element of the array.

An array is unrelated to 'ordinary' variables, and it has its own features just as primitive types have their own standard "integral conversions".

Similarly though, you can get a pointer to an array by using the address of operator (&). This gives you an actual "pointer to array", whereas the standard conversion only gives you a pointer to the first element (you can also create a reference to an array).

To make things a bit clearer, the standard conversion for arrays is needed to access array elements. This is because the subscripting operator works with pointers (not actually an array specific operator). It has a clause in its description mentioning this:

The expression E1[E2] is identical (by definition) to *((E1)+(E2))

Its identical form (second one listed above) shows that either E1 or E2 must be a pointer as it is dereferenced to access the value. You can also do a proof of this.

The code below is accessing the second element of the array two different ways using the subscripting operator, showing that at least one operand must be a pointer:

  int array[5];

  int a = array[1];

  int b = 1[array];

So even though an implicit array to pointer conversion may seem strange, it is in fact needed to access elements of arrays.

You might be wondering what use is an array that isn't converted to a pointer (which is why many consider arrays to be simply pointers). Well it is because arrays provide more information than a pointer. Size/length info is lost once you have a pointer. You can maintain this with an array reference or array pointer:

I used a typedef to make the syntax a bit easier on the eye.

typedef int ArrayType[5];

void setup() {

  ArrayType array = {1,2,3,4,5};

  AddOneToArray(array);
}

void AddOneToArray( ArrayType &array ){

  for( auto &element : array ){  //Size info is maintained
    element += 1;
  }
}

void loop() {}

You could use an ArrayType* as the function parameter, you'd just need to dereference the pointer to return to the actual array.

This next example is a little more complex and shows two things, returning an array (reference) from a function, and also an unrelated example showing a real array pointer.

typedef int ArrayType[5];

class MyArray{
  public:
  
    //Allows conversion of class type to internal array type (returns a reference to array)
    operator ArrayType&(){
      return data;
    }

  private:
    ArrayType data;
};

void setup() {

  MyArray foo;

  //Use conversion operator provided by class, then access an array element:
  foo[4] = 1;


  ArrayType bar = {1,2,3,4,5};
  //Take pointer to array
  ArrayType *ptr = &bar;

  //Do something with array pointer
  (*ptr)[4] = 2;

}

void loop() {}

:sleeping: :sleeping: :sleeping:

...R

Robin2:
:sleeping: :sleeping: :sleeping:

...R

whatever

As some of you know I took a little time off to learn C++. We covered pointers and how they worked. After reading through these post I'm so confused. Hey teach help!!!

Naneen:
As some of you know I took a little time off to learn C++. We covered pointers and how they worked. After reading through these post I'm so confused. Hey teach help!!!

What is confusing you?

pYro_65:

typedef int ArrayType[5];

void setup() {

ArrayType array = {1,2,3,4,5};

AddOneToArray(array);
}

void AddOneToArray( ArrayType &array ){

for( auto &element : array ){  //Size info is maintained
    element += 1;
  }
}

Nice, didn't consider that array types remember their size, which they of course do (sizeof()).

Wasn't aware that C(++?) supports for-each type of loop syntax.

:slight_smile:

Rupert909:
Nice, didn't consider that array types remember their size, which they of course do (sizeof()).

Wasn't aware that C(++?) supports for-each type of loop syntax.

Yeah, maintaining the size info is useful, no need to force to a pointer, then pass a length anyway. But all depends on how it is used for what is best.

Ranged for loops were added in C++11.

Reading this thread made my head spin.

pYro_65:
an array is not a pointer. It will cast to a pointer to the first element if used in an expression that accepts a pointer as an operand,

I have not been explaining myself clearly enough.

I know that an array will cast to a pointer. What I don't understand is why a simple variable will not also cast to a pointer?

...R

Robin2:
I have not been explaining myself clearly enough.

I know that an array will cast to a pointer. What I don't understand is why a simple variable will not also cast to a pointer?

...R

I guess it was deemed too confusing.

Consider this example from Java, and you see what fun can arise from automatically handling Integer as object (pointer) and arithmetic value. The glory of "autoboxing" ...

// Integer is an object, but it can be assigned an int value. The compiler
// provides "autoboxing", which means the int is converted into an Integer
// with the given value

Integer a=1;
Integer b=1;
Integer c=2;

// (1) 
if (a==b) {...} // pointer comparison - fails

// (1b)
if (a.intValue()==b.intValue()) {...}  // value comparison

// (2) 
a=b; // pointer assignment

// (3)
a=b+3; // see below

// (4)
c++;

Example (3) shows auto(un)boxing in all its uglyness. Since the right hand side is an arithmethic expression, the reference to c is unboxed, becoming int value 1, the values are added, becoming 4, and the result is autoboxed into a new object of type Integer, with value 4.

So the code in (2) and (3) do radically different things.

Example (4) follows the logic of example (3). Integer objects are immutable, so here the value of the original Integer object referred by c, is unboxed, then incremented, then autoboxed into a new Integer object.

The boxed versions of primitive types in Java are good for exactly one thing, and that is that variables of these types can be set to null, which is handy in many situtations.

However, this gives rise to interesting runtime situations, since parameters to methods are also auto(un)boxed.

void myMethod (int x) {...}

Integer i=4;
myMethod(i); // ok

Integer j=null;
myMethod(j); // fails at runtime

The autoboxing feature lets programmers get away with inconsistencies between parameter declarations and actual parameter expressions, which work okay for non-null values.

:slight_smile:

Its a mess.