Go Down

Topic: "Pointers make my head spin" etc ... (Read 3825 times) previous topic - next topic

westfw

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.

Code: [Select]
        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.
Code: [Select]
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:
Code: [Select]

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.

Robin2

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.

Quote
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
Two or three hours spent thinking and reading documentation solves most programming problems.

pYro_65

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:

Quote
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:

Quote
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:

Code: [Select]
  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.

Code: [Select]
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.

Code: [Select]
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() {}
Forum Mod anyone?
https://arduino.land/Moduino/

Robin2

Two or three hours spent thinking and reading documentation solves most programming problems.

pYro_65

Forum Mod anyone?
https://arduino.land/Moduino/

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!!!

pYro_65

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?
Forum Mod anyone?
https://arduino.land/Moduino/

Rupert909

Code: [Select]
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.

:-)

The internet is a great thing. Use it.
:s/internet/mind/g
Real Programmers use ++C

pYro_65

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.
Forum Mod anyone?
https://arduino.land/Moduino/

chummer1010

Reading this thread made my head spin.
#include <signature.h>

Robin2

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
Two or three hours spent thinking and reading documentation solves most programming problems.

Rupert909

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" ...

Code: [Select]

// 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.

Code: [Select]

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.

:-)

Its a mess.



The internet is a great thing. Use it.
:s/internet/mind/g
Real Programmers use ++C

pYro_65

#27
Jul 13, 2016, 12:06 pm Last Edit: Jul 13, 2016, 12:13 pm by pYro_65
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
Without trying to be rash, I explained this in the post you decided not to read.

If you do not want to read it all, start from the paragraph that starts with "To make things a bit clearer". It gives you the reasons why an array casts implicitly.
Forum Mod anyone?
https://arduino.land/Moduino/

Robin2

In spite of my smileys I did read your Reply.

I think the difference between us is that you are describing HOW C/C++ works whereas I am asking WHY does it work like that (because it seems to me to be inconsistent).

Another way that this "inconsistency" could be avoided would be if it was necessary to do an explicit cast to a pointer for an Array as well as for a simple variable.

(Don't lose sight of the fact that this is the Bar Sport section :) )

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

Rupert909

In spite of my smileys I did read your Reply.

I think the difference between us is that you are describing HOW C/C++ works whereas I am asking WHY does it work like that (because it seems to me to be inconsistent).

Another way that this "inconsistency" could be avoided would be if it was necessary to do an explicit cast to a pointer for an Array as well as for a simple variable.

(Don't lose sight of the fact that this is the Bar Sport section :) )

...R
Taking the address of an array returns a pointer to array (object), whereas the auto-conversion gives a pointer to the first element. Two functionalities can not be represented by a single implicit conversion, or explicit operator ('&').

Also, who said that the inventors of C, and later C++, were first and foremost concerned with internal consistency? With C being a step up from assembly, I'd think that terseness had high priority.

There's the old joke that the one regret Kernighan and Ritchie had about unix, was the "mail" command, should have been called "ml".

:-)
The internet is a great thing. Use it.
:s/internet/mind/g
Real Programmers use ++C

Go Up