Pointers - where is the actual data stored ?

Just when I think that I have got the hang of pointers I think of another question

This appears to work OK, but appearances can be deceptive, hence this post

char * arrayA[4] = {"short0", "short1", "short2", "short3"};

void setup()
{
  Serial.begin(115200);
  while (!Serial);
  printArray();
  arrayA[0] = "longer0";
  arrayA[1] = "longer1";
  arrayA[2] = "longer2";
  arrayA[3] = "longer3";
  printArray();
  arrayA[0] = "even more characters0";
  arrayA[1] = "even more characters1";
  arrayA[2] = "even more characters2";
  arrayA[3] = "even more characters3";
  printArray();
}

void loop()
{
}

void printArray()
{
  for (int x = 0; x < 4; x++)
  {
    int add = &arrayA[x];
    Serial.print("pointer address : ");
    Serial.print(add);
    Serial.print("\tdata : ");
    Serial.println(arrayA[x]);
  }
  Serial.println();
}

The array of pointers is stored in a fixed location, which is no surprise, but where is the actual data stored and why does making it larger appear to cause no problem ? Is there a problem waiting to break the code at some point ?

Can the location of the strings be printed rather than the address of the pointer to it ?

This might be a more interesting thing to print:

    int add = &arrayA[x][0];

There's no lurking issue with the code. Each of those strings is effectively an anonymous global. All you're doing is pointing your four char pointers to different ones, so their size is not important.

You can print where your pointers print to with sprintf and the %p format specifier.

wildbill:
This might be a more interesting thing to print:

    int add = &arrayA[x][0];

Thanks Bill, that is what I was looking for. Using it I can see the address of the strings changing as the array is changed

There's no lurking issue with the code. Each of those strings is effectively an anonymous global. All you're doing is pointing your four char pointers to different ones, so their size is not important.

I have certainly never had a problem that I was aware of when changing such strings but my understanding was brought into question by a reply in another topic

It is interesting to see how the compiler adjusts the memory addresses of the array elements as they are changed

This is why the original definition should have been:

const char * const arrayA[4] = {"short0", "short1", "short2", "short3"};

The first 'const' prevents you from changing the string literals being pointed to. BTW, you should have gotten a compiler warning / error for that.

The second 'const' prevents you from doing what you did .... changing where the pointers were pointing. When you did that, you stranded the original string literals ("short0", etc) in memory with no way of accessing them again. Then you did it again with the second set of pointer assignments.

A waste of memory and bad coding practice.

gfvalvo:
The second 'const' prevents you from doing what you did .... changing where the pointers were pointing. When you did that, you stranded the original string literals ("short0", etc) in memory with no way of accessing them again. Then you did it again with the second set of pointer assignments.

A waste of memory and bad coding practice.

That's not necessarily true, the standard allows reusing the same memory for distinct string literals with the same content, and the GCC compilers used for Arduino indeed do this. String literals don't "strand in memory with no way of accessing them again".
For example:

const char *state_name = "Released";
void update_state() {
    state_name = digitalRead(2) == HIGH ? "Released" : "Pressed";
}

generates:

.LC0:
        .string "Released"
.LC1:
        .string "Pressed"
update_state():
        ldi r24,lo8(2)
        call digitalRead
        sbiw r24,1
        breq .L3
        ldi r24,lo8(.LC1)    ; load the pointer to "Pressed"
        ldi r25,hi8(.LC1)
        sts state_name+1,r25    ; store that pointer in state_name
        sts state_name,r24
        ret
.L3:
        ldi r24,lo8(.LC0)    ; load the pointer to "Released"
        ldi r25,hi8(.LC0)
        sts state_name+1,r25    ; store that pointer in state_name
        sts state_name,r24
        ret
state_name:
        .word   .LC0    ; initialized to point to "Released"

As you can see, even though the string literal "Released" is used twice, it's stored in memory only once.

In many cases, you don't want to change the string a variable points to, and adding the second const is the right thing to do.
However, there are plenty of cases where you do want to change which string you point to, which is perfectly fine, you don't need the second const, and you don't have to lose sleep over wasted memory.

Pieter

A waste of memory and bad coding practice.

Almost certainly

However, given the oft repeated advice to use strings instead of Strings to prevent memory fragmentation, how should changing a string declared as a string be accomplished without "stranding" the previous value in memory ? Padding the original string with enough spaces to accommodate the longest anticipated string does not work and actually exacerbates the situation by stranding more memory

As to the warnings, yes, they are there, but I have always been bemused to see warnings about something that is seemingly forbidden

i compiled

const char * arrayA[4] = {"short0", "short1", "short2", "short3"};

i use od (octal dump) to display the .o in hex (left) and ascii (right)

at address 0x32E is the start of arrayA containing the (relative) addresses of the strings: 0x0, 0x7, 0xe, 0x15. each string is terminated with a nul.

so the strings in arrayA are simply allocated after the array.

0000000 014c 0005 0000 0000 0144 0000 000d 0000    0000000   L soh enq nul nul nul nul nul   D soh nul nul  cr nul nul nul
0000020 0000 0104 742e 7865 0074 0000 0000 0000    0000020 nul nul eot soh   .   t   e   x   t nul nul nul nul nul nul nul
0000040 0000 0000 0000 0000 0000 0000 0000 0000    0000040 nul nul nul nul nul nul nul nul nul nul nul nul nul nul nul nul
0000060 0000 0000 0000 0000 0020 6030 642e 7461    0000060 nul nul nul nul nul nul nul nul  sp nul   0   `   .   d   a   t
0000100 0061 0000 0000 0000 0000 0000 0010 0000    0000100   a nul nul nul nul nul nul nul nul nul nul nul dle nul nul nul
0000120 00dc 0000 011c 0000 0000 0000 0004 0000    0000120   \ nul nul nul  fs soh nul nul nul nul nul nul eot nul nul nul
0000140 0040 c030 622e 7373 0000 0000 0000 0000    0000140   @ nul   0   @   .   b   s   s nul nul nul nul nul nul nul nul
0000160 0000 0000 0000 0000 0000 0000 0000 0000    0000160 nul nul nul nul nul nul nul nul nul nul nul nul nul nul nul nul
0000200 0000 0000 0000 0000 0080 c030 722e 6164    0000200 nul nul nul nul nul nul nul nul nul nul   0   @   .   r   d   a
0000220 6174 0000 0000 0000 0000 0000 001c 0000    0000220   t   a nul nul nul nul nul nul nul nul nul nul  fs nul nul nul
0000240 00ec 0000 0000 0000 0000 0000 0000 0000    0000240   l nul nul nul nul nul nul nul nul nul nul nul nul nul nul nul
0000260 0040 4030 342f 0000 0000 0000 0000 0000    0000260   @ nul   0   @   /   4 nul nul nul nul nul nul nul nul nul nul
0000300 0000 0000 0014 0000 0108 0000 0000 0000    0000300 nul nul nul nul dc4 nul nul nul  bs soh nul nul nul nul nul nul

0000320 0000 0000 0000 0000 0040 4030 0000 0000    0000320 nul nul nul nul nul nul nul nul   @ nul   0   @ nul nul nul nul
0000340 0007 0000 000e 0000 0015 0000 6873 726f    0000340 bel nul nul nul  so nul nul nul nak nul nul nul   s   h   o   r
0000360 3074 7300 6f68 7472 0031 6873 726f 3274    0000360   t   0 nul   s   h   o   r   t   1 nul   s   h   o   r   t   2
0000400 7300 6f68 7472 0033 4347 3a43 2820 4e47    0000400 nul   s   h   o   r   t   3 nul   G   C   C   :  sp   (   G   N

0000420 2955 3720 332e 302e 0000 0000 0000 0000    0000420   U   )  sp   7   .   3   .   0 nul nul nul nul nul nul nul nul
0000440 0008 0000 0006 0004 0000 0008 0000 0006    0000440  bs nul nul nul ack nul eot nul nul nul  bs nul nul nul ack nul
0000460 0008 0000 0008 0000 0006 000c 0000 0008    0000460  bs nul nul nul  bs nul nul nul ack nul  ff nul nul nul  bs nul
0000500 0000 0006 662e 6c69 0065 0000 0000 0000    0000500 nul nul ack nul   .   f   i   l   e nul nul nul nul nul nul nul
0000520 fffe 0000 0167 7261 6172 2e79 7063 0070    0000520   ~ del nul nul   g soh   a   r   r   a   y   .   c   p   p nul
0000540 0000 0000 0000 0000 742e 7865 0074 0000    0000540 nul nul nul nul nul nul nul nul   .   t   e   x   t nul nul nul
0000560 0000 0000 0001 0000 0103 0000 0000 0000    0000560 nul nul nul nul soh nul nul nul etx soh nul nul nul nul nul nul
0000600 0000 0000 0000 0000 0000 0000 642e 7461    0000600 nul nul nul nul nul nul nul nul nul nul nul nul   .   d   a   t
0000620 0061 0000 0000 0000 0002 0000 0103 0010    0000620   a nul nul nul nul nul nul nul stx nul nul nul etx soh dle nul
0000640 0000 0004 0000 0000 0000 0000 0000 0000    0000640 nul nul eot nul nul nul nul nul nul nul nul nul nul nul nul nul
0000660 622e 7373 0000 0000 0000 0000 0003 0000    0000660   .   b   s   s nul nul nul nul nul nul nul nul etx nul nul nul
0000700 0103 0000 0000 0000 0000 0000 0000 0000    0000700 etx soh nul nul nul nul nul nul nul nul nul nul nul nul nul nul
0000720 0000 0000 722e 6164 6174 0000 0000 0000    0000720 nul nul nul nul   .   r   d   a   t   a nul nul nul nul nul nul
0000740 0004 0000 0103 001c 0000 0000 0000 0000    0000740 eot nul nul nul etx soh  fs nul nul nul nul nul nul nul nul nul
0000760 0000 0000 0000 0000 0000 0000 000f 0000    0000760 nul nul nul nul nul nul nul nul nul nul nul nul  si nul nul nul
0001000 0000 0000 0005 0000 0103 0011 0000 0000    0001000 nul nul nul nul enq nul nul nul etx soh dc1 nul nul nul nul nul
0001020 0000 0000 0000 0000 0000 0000 615f 7272    0001020 nul nul nul nul nul nul nul nul nul nul nul nul   _   a   r   r
0001040 7961 0041 0000 0000 0002 0000 0002 001a    0001040   a   y   A nul nul nul nul nul stx nul nul nul stx nul sub nul
0001060 0000 722e 6164 6174 7a24 7a7a 2e00 6472    0001060 nul nul   .   r   d   a   t   a   $   z   z   z nul   .   r   d
0001100 7461 2461 7a7a 007a                        0001100   a   t   a   $   z   z   z nul
0001110    0001110

If you have several .o files being built into an executable file won't all the positions be rearranged? If you have more than one array in a single .o file is all the string data together or separated? I don't know how the avr compiler works, but I know on some architectures the order of variable declarations are moved about to produce better alignment, so for example longs do not span pages in memory.

alignment issues are fixed in the .o

i believe the linker more or less concatenates the sections (probably on word boundaries) and translates relative addresses to absolute addresses.

UKHeliBob:
As to the warnings, yes, they are there, but I have always been bemused to see warnings about something that is seemingly forbidden

In this case, that is caused by the IDE setting the fpermissive flag for the compiler, which was done to prevent breaking existing libraries when an updated version of gcc upgraded some warnings to errors. A common example is calling a function with a string literal as an argument when the function expected a char* instead of a const char*. The IDE by default does not display all warnings, so there are numerous libraries with problematic code.

Note that this does not apply to all cores supported by the IDE, something like an ESP8266 may flag the error.

that is caused by the IDE setting the fpermissive flag for the compiler,

That is very much good news/bad news

If only there was a way to easily adjust the compiler flags for more experienced users

david_2018:
Note that this does not apply to all cores supported by the IDE, something like an ESP8266 may flag the error.

As do the ARM-based Teensy cores.

UKHeliBob:
That is very much good news/bad news

If only there was a way to easily adjust the compiler flags for more experienced users

Can't that be done by modifying build.txt, boards.txt, or something like that? Seems like I've changed compiler and linker flags by editing one of the config files.

If only there was a way to easily adjust the compiler flags for more experienced users

Seems like I've changed compiler and linker flags by editing one of the config files.

possible != easy

UKHeliBob:
possible != easy

Whatever config file it was, had explicit definitions for things like compile_flags, linker_flags, or similar. It was quite obvious, once I found the right file. I'm sure there are folks here who could give explicit instructions on where the file is, and what to change.

platforms.txt:

compiler.warning_flags=-w
compiler.warning_flags.none=-w
compiler.warning_flags.default=
compiler.warning_flags.more=-Wall
compiler.warning_flags.all=-Wall -Wextra

I believe changing/adding to the above should do what you want, for the different warning options. This file should be in "c:\username\AppData\Local\arduino15\packages\Arduino\hardware\avr\your_ide_version_number" if you're using Windows.

Took under a minute to find.

Depending on how many libraries it breaks, I'd be reluctant to make that change, even though it would be nice to have those additional warnings for my own code.

wildbill:
Depending on how many libraries it breaks, I'd be reluctant to make that change, even though it would be nice to have those additional warnings for my own code.

The compiler flag changes some of the errors to warnings, the displaying of the warnings is controlled by the preferences in the IDE, which by default does not show all warnings.