Understanding arguments in avr/eeprom.h functions

Can't quite wrap my head around the use of pointers for the EEPROM address arguments in the avr/eeprom.h library, e.g.

23.14.3.2 uint8_t eeprom_read_byte ( const uint8_t * __p )
Read one byte from EEPROM address __p.

The argument is indeed an address, but it's not really a proper C pointer, it's an address in what is essentially external hardware. This usage necessitates casting, e.g.

uint8_t EEPROMClass::read(int address)
{
	return eeprom_read_byte((unsigned char *) address);
}

There's not much to the Arduino EEPROM library, but its existence is justified just for hiding this silliness. Or am I missing something?? Not sure I understand the use of const either...

I'm inclined to agree about the pointer. However generally when things "point" to data in memory you use a pointer. I suppose you could say the data in EEPROM is in memory, and thus a pointer is justified.

As for the const, it is because what the pointer is pointing to doesn't change.

I can never remember which way around they go, so here are some tests:


You can modify the pointer:

void setup ()
  {
  const char * p = "hello";
  p++;
  }  // end of setup

void loop () { }

But not what it points to:

void setup ()
  {
  const char * p = "hello";
  *p = 'a';
  }  // end of setup

void loop () { }

Error:

sketch_jan03a.cpp: In function 'void setup()':
sketch_jan03a:3: error: assignment of read-only location '* p'

Move the const and the opposite applies:

void setup ()
  {
  char * const p = "hello";
  p++;
  }  // end of setup

void loop () { }

Error:

sketch_jan03a.cpp: In function 'void setup()':
sketch_jan03a:3: error: increment of read-only variable 'p'

However this compiles OK:

void setup ()
  {
  char * const p = "hello";
  *p = 'a';
  }  // end of setup

void loop () { }

Two consts, and you can't change the pointer or what it points to:

void setup ()
  {
  const char * const p = "hello";
  *p = 'a';
  p++;
  }  // end of setup

void loop () { }
sketch_jan03a.cpp: In function 'void setup()':
sketch_jan03a:3: error: assignment of read-only location '*(const char*)"hello"'
sketch_jan03a:4: error: increment of read-only variable 'p'

Right, I can see it both ways, sort of. Not sure what to think.

As for the const, it is because what the pointer is pointing to doesn't change.

I can never remember which way around they go, so here are some tests:

I'll run through those, thanks very much, Nick!

The argument is a pointer because the functions are meant to be used with EEMEM...

unsigned long RandomSeed EEMEM = 13;

void setup( void )
{
  randomSeed( eeprom_read_dword( &RandomSeed ) );
  RandomSeed += 101;
  eeprom_write_dword( &RandomSeed, RandomSeed );
}

There are two advantages...

  1. The toolset organizes the EEPROM memory layout. You, the developer, don't need to track what is stored where.

  2. The toolset generates an upload image with the initial values (the "13" in my example).

Not sure I understand the use of const either...

If you treat const as strictly left-associative, the ambiguous case is eliminated.

const char * p = "hello";

...don't use this one.

char const * p = "hello";

...char is to the left of const so the char part of the type is constant. p can be changed (point to a different address) but the data it points to cannot be changed.

char * const p = "hello";

...* is to the left so the address is constant. p cannot be changed to a different address but the data it points to can be changed.

char const * const p = "hello";

...neither p nor what it points to can be changed. p is a read-only pointer to read-only data.

@CB, thanks very much, somehow I'd entirely missed the EEMEM attribute (and it was only a couple lines above where I was reading :blush:). Had wondered about that, too; thought it should be possible based on the avrdude doc, but never went looking for it.

All them const's make my head hurt just a bit but thanks for those too I think :wink: I'll definitely need to read it twice!

However in virtually every example of code I've seen the word const, if used, is on the left. eg.

const byte ledPin = 5;

Example, from stdlib.h:

extern unsigned long strtoul(const char *__nptr, char **__endptr, int __base);
...
extern long atol(const char *__s) __ATTR_PURE__;
...
extern int atoi(const char *__s) __ATTR_PURE__;

char const * p = "hello";

For me that is the ambiguous case because const is between the type and the asterisk. You have to remember which way "const" associates. And it looks like you are saying "const pointer".


At least:

const char * p = "hello";

Make it look like a "const char" ... pointer ... named p.

Historical artifact.

For me that is the ambiguous case...

"Ambiguous" was a very poor word choice. "Special-case" is more fitting. Modifying a simple data-type...

const char * p = "hello";
const uint8_t LedPin = 13;

...is a special case where const is right-associative. In all other situations, it is left-associative.

At least... const char * p = "hello"; ...Make it look like a "const char" ... pointer ... named p.

I agree. I would prefer const to always be right-associative. I think that is much more readable. (I assume it is not solely to allow for constant member functions.)

But, it isn't (except the one case). For me it's much easier to always (well, mostly) treat it as a left-associative modifier.

no, it's how adjectives are used in english. "The fat cat" => "The const int." In spanish they say "El gato gordo" where the adjectives go after the noun, but in english the adjectives almost always go first.

WizenedEE:

Historical artifact.

no, it's how adjectives are used in english.

Those two things are not mutually exclusive.