AUGH! Pointers!

You know, It's not that I don't understand what a pointer is... it's just an address, but there is something mind-numbing dumb about how C manages them that just makes me want to strangle Dennis Ritchie if it wasn't for the fact he's already dead.

Ok, I want a block of 32k that I can reference with a pointer. I have extra ram in my Mega do I want the space way at the bottom from 0x8000 to 0xFFFF

First thing's first, let's define the pointer and point it to the top of my block.

uint8_t *memory = 0x8000;

and I get a compile-time error. "error: invalid conversion from 'unsigned int' to 'uint8_t*'"

Huh? where is it getting the unsigned int from? I explicitly told it I wanted an unsigned 8 bit int as a pointer. Now, I don't know why it's doing this, but I know how to fix it, which an unacceptable solution. I want to know what's going on and not blindly guessing how how my own code is functioning. That's just bad coding.

anyway, the solution to the problem is to define the pointer with a cast... I think. It looks like this.

uint8_t * memory = (uint8_t *)0x8000;

I have no idea what this is doing. I can assume it's a cast because of the parenthesis. But what am I casting? From what to what? That figging star makes no sense.

Let me take a step back so I can make sure I have pointers and casts right.

  • = "address of" meaning that that you read "*foo=100" as "The address of foo is 100"
    & = "value at address of" meaning that you read "&foo=16" as "The value at the address of foo is 16"

Taking this my initial deceleration of {uint8_t *memory = 0x8000;} reads thusly.
"I want to to make a pointer called "memory" where each cell is an unsigned unsigned 8-bit integer wide, and put the pointer at memory location 0x8000"

But that doesn't work. My new declaration {uint8_t * memory = (uint8_t *)0x8000;} reads like this..
"I want to to make a pointer called "memory" where each cell is an unsigned unsigned 8-bit integer wide, and then convert 0x8000 into the address of an unsigned 8-bit integer wide pointer." (Which is dumb because it's the same memory location)

I don't understand this. I'm assigning an explicit address why should the compiler "care" that type it is.
Since when do addresses have "types" other than "this location"

Now, to stem off an further issue, after I get this whole memory-pointer thing straightened out I will want to create functions that put data into a particular memory location, and take data out.

void memStoreByte(int addr,uint8_t value ) {
     memory = address;
    &memory = value; 
}

uint8_t memReadByte(int addr ) {  
     memory = address;
     return &memory;
}

I have a feeling these are going to be incorrect because of the cast... Can you point out the error of my ways?

You are making it too complicated. Try this:

byte * const memory = (byte *) 0x8000; 

void setup ()
  { 
  // put into memory
  
  memory [1] = 0x10;
  memory [2] = 0x42;
  
  // get from memory
  
  byte a = memory [1];
  byte b = memory [2]; 
  }  // end of setup
  
void loop () {}

The original cast (on the first line) is converting the int 0x8000 into a byte* pointer. Thus "memory" will now point to 0x8000.

From then on we just index into memory as illustrated, so you can get to anywhere, starting from 0x8000, that you want to.

I threw in the "const" to make sure you don't accidentally change 0x8000 to something else by misusing the pointer.

Ok, so the reason for the cast is because I need to let the compiler know that the address is an address and not some number. I'm used to managed code were I can assume the system's just going to "know" what to do with the data a feed it.

Things I learned:
You can access pointer data as an array when you create the pointer without declaring it explicitly as an array.
Using this trick you can set an array anywhere in memory. (What I was trying to accomplish in the first place)

halkun:
My new declaration {uint8_t * memory = (uint8_t *)0x8000;} reads like this..
"I want to to make a pointer called "memory" where each cell is an unsigned unsigned 8-bit integer wide, and then convert 0x8000 into the address of an unsigned 8-bit integer wide pointer." (Which is dumb because it's the same memory location)

I don't understand this. I'm assigning an explicit address why should the compiler "care" that type it is.
Since when do addresses have "types" other than "this location"

The issue is that while you know 0x8000 is an address because you want it to be,
However, in reality as far as the compiler is concerned 0x8000 is just a number (an unsigned int). And that is where the issue is.
The compiler is seeing a an assignment of an unsigned integer to a variable that is a pointer.
It is complaining because the types are different.
In C the compiler makes does not make any assumptions and does not automatically cast the data type when
it is assigned.

So if you want to assign the number 0x8000 to a pointer you must tell the compiler that it is a pointer
as well as what the pointer points to.
You do that by casting it.

uint8_t * memory = (uint8_t *)0x8000;

The cast on the right side of the = tells the compiler that the 0x8000 is a pointer (address) that points
to a uint8_t

--- bill

halkun:
Ok, so the reason for the cast is because I need to let the compiler know that the address is an address and not some number.

No, it's to let the compiler know that some number is an address, and not some number.

If you were to tell me you were trying to assign a variable value to 0x8000, I wouldn't assume that 0x8000 is an address; neither does the compiler.

halkun:
I don't understand this. I'm assigning an explicit address why should the compiler "care" that type it is.
Since when do addresses have "types" other than "this location"

Once upon a time C didn't care, and you could freely treat pointers like ints and vice versa. While you could get away with that on many architectures, there were a few where this was problematic, so explicit type casting was made mandatory, probably in ANSI C. (? Michael Meissner would no doubt know.)

So yes, these days, the compiler wants you indicate that this variable is a pointer type, not an integer type.

I'm not sure where your confusion actually lies regarding the casting syntax.

(int) 0x1234
(long) 0x1234
(int *) 0x1234
(long *) 0x1234

The first two examples cast to int types. The last two cast to pointer types. Four different types in all.

The compiler needs to be able to distinguish between a pointer to an int and a pointer to a long int if they are different sizes, because the instruction to increment a pointer

p++

will produce a different result depending on the size of the object it is pointing to. If an int is 4 bytes, but a long is 8 bytes, for example.

Sometimes the compiler will let you omit the explicit type casting, for example, when assigning one size int to another. But other times it is more strict. It would be easy to assign to 0x1234 to a pointer itself, instead of assigning 0x1234 to the variable the pointer points to, by mistake. Forcing you to explicitly cast makes this mistake much less likely. Compiler options can affect how strict the compiler is about various things.

In any case, it's all perfectly reasonable and sensible. If you have a question, perhaps less of the rant and a bit more humility in appreciating the "problem" may actually be that you simply don't understand the subject at hand as you assume you do.

Hope this has increased your understanding by at least a little bit.

I don't understand this. I'm assigning an explicit address why should the compiler "care" that type it is.

As others have pointed out, you aren't assigning an address, so yes, the compiler cares. It is designed to care. It is there to help you. Putting "0x" in front of something doesn't turn it into an address.

Since when do addresses have "types" other than "this location"

As pico pointed out as well, pointers have to have a defined way of incrementing. So a pointer to long, if you add 1 to it, actually points 4 bytes further. So the compiler also needs to know more than "this location".

I get it now. I didn't realize there was a pointer type. and "out of the box" my 0x8000 was just an unsigned int, which makes sense. What I thought I was seeing was the 0x8000 being "rounded" to a byte. For example...

float a = 5.25;
int b = (int)a;

here b=5 because it was cast as an int. I usually see this as a "round and squeeze" where the cast will round the number and then squeeze it into the proper format for that particular type.

I was getting confused because the (byte *)0x8000 seemed unnecessary because there was no "rounding and squeezing" being done. I assumed the 0x8000 was just a generic, untyped number. Now I get get it, a cast is a type shift, not a rounding mechanism.

halkun:
...makes me want to strangle Dennis Ritchie if it wasn't for the fact he's already dead.

Just so you know: It wasn't Dennis Ritchie that did that, it was the ANSI C committee.

K&R C is a lot more permissive.