5

I need to hard code 8 byte addresses into char arrays of length 8. This must be done many places in my code (in function scope), so I have tried to come up with a one-liner. The following works perfectly in C when compiling with gcc.

char a[8];

void cpaddr(char target[], char *source) {
    int i;
    for (i=0; i<8; i++)
        target[i] = source[i];
}

int main() {
    char b[] = {0x00, 0x10, 0xFF, 0xCA, 0x00, 0x00, 0xA2, 0x7D};
    cpaddr(a, b);

    // line below does not compile with Arduino IDE
    cpaddr(a, (char[]) {0x00, 0x10, 0xFF, 0xCA, 0x00, 0x00, 0xA2, 0x7D});
}

When compiling on Arduino the last line, which is the one-liner I was aiming for, does not compile. It gives:

/home/bob/Desktop/ate/Ate.ino: In function 'int main()':
Ate:101: error: taking address of temporary array
cpaddr(a, (char[]) {0x00, 0x10, 0xFF, 0xCA, 0x00, 0x00, 0xA2, 0x7D});
^
exit status 1
taking address of temporary array

What is the problem here, that apparently is not a problem with gcc?

How do I fix it?

VE7JRO
  • 2,554
  • 18
  • 25
  • 29
Mads Skjern
  • 1,125
  • 3
  • 13
  • 23
  • Have you tried void cpaddr(char target[], const char *source) – jantje Mar 02 '16 at 13:25
  • I tried your exact code with g++ (and gcc) version 4.8.4 and got the exact same error message you got. – Nick Gammon Mar 02 '16 at 20:33
  • I'm using 4.9.3. Screenshot of result: http://i.imgur.com/2RJbGwg.png – Mads Skjern Mar 03 '16 at 08:19
  • ( type[] ){val, ues} is not a valid way to create a temporary in C++: http://stackoverflow.com/questions/15458883/using-array-init-list-as-temporary-in-c11#comment26614881_18163000 The solution is to typedef it. – underscore_d Jun 26 '16 at 07:13

3 Answers3

4

It is quite right, using that kind of syntax is not allowed. It's a bit of a pain, but it's ok since there is an alternative method - kind of a "trick" if you will.

That trick is to use a string, not an array. After all, a string is just an array, it's just handled slightly differently by the compiler.

Instead of using {...} use "..." and use the hexadecimal character escape sequence \xNN, such as:

cpaddr(a, "\x00\x10\xFF\xCA\x00\x00\xA2\x7D");

You could even lose your custom function and use a standard library function - memcpy():

memcpy(a, "\x00\x10\xFF\xCA\x00\x00\xA2\x7D", 8);

On the 8-bit AVRs you can save RAM by using the progmem variant and the F() macro:

memcpy_P(a, F("\x00\x10\xFF\xCA\x00\x00\xA2\x7D"), 8);
Majenko
  • 105,095
  • 5
  • 79
  • 137
  • I think that it should be PSTR() instead of F(). F() is a very special Arduino String hack. – Mikael Patel Mar 02 '16 at 22:07
  • F() just imposes a type to PSTR() for overloading. You can use either. – Majenko Mar 02 '16 at 22:17
  • Because of the type you cannot use F() directly without a cast. The compiler will say "cannot convert 'const __FlashStringHelper' to 'const char'". – Mikael Patel Mar 02 '16 at 22:30
  • Mine didn't. It compiled perfectly fine. I always test before posting code. It's not a cast, it's a reinterpret_cast. – Majenko Mar 02 '16 at 22:31
  • But apparently not on memcpy_P as it takes whatever, i.e. const void*. – Mikael Patel Mar 02 '16 at 22:32
  • In any case F() is part of the "black-listed" String class. That should be enough to use PSTR() instead :) – Mikael Patel Mar 02 '16 at 22:34
  • This is true ;) – Majenko Mar 02 '16 at 22:41
  • No it isn't. The define #define F(string_literal) (reinterpret_cast<const __FlashStringHelper *>(PSTR(string_literal))) is in WString.h, however there is no connection between F() and the String class except that the String class uses it in certain cases (as does the Print class). In fact as you can see from the #define saying to use PSTR() instead doesn't make any sense. F() already uses PSTR(). It's purpose is just to create a new type that can be used to detect if a string is in PROGMEM or RAM. That's all. – Nick Gammon Mar 03 '16 at 19:42
  • @NickGammon Isn't that mainly for overloading, like I mentioned above? ;) – Majenko Mar 03 '16 at 20:11
  • Er, yes it turns # define PSTR(s) (__extension__({static const char __c[] PROGMEM = (s); &__c[0];})) (which is still type const char []) into another type, which then assists with overloading. So I agree that (after casting) they would be the same thing. However neither PSTR nor F() are "part of the black-listed String class". I see no objection to using F() here. In fact your suggestion would save RAM compared to what I did in my reply, although mine is probably "cleaner" in that you aren't throwing in a memcpy_P (and making sure you get the length right). – Nick Gammon Mar 03 '16 at 20:45
1

See Using array init list as temporary in C++11?

You can solve it by using const. This compiles:

char a[8];

void cpaddr(char target[], const byte *source) {
    int i;
    for (i=0; i<8; i++)
        target[i] = source[i];
}

int main() {
    byte b[] = {0x00, 0x10, 0xFF, 0xCA, 0x00, 0x00, 0xA2, 0x7D};
    cpaddr(a, b);
    cpaddr(a, (const byte[]) {0x00, 0x10, 0xFF, 0xCA, 0x00, 0x00, 0xA2, 0x7D});
}

Note I had to change your array from char to byte because I was getting (valid) warnings that things like 0xCA don't fit into a char.

Nick Gammon
  • 38,184
  • 13
  • 65
  • 124
  • As Majenko said, memcpy would be better than doing a byte-by-byte copy. – Nick Gammon Mar 02 '16 at 20:41
  • I hate it when people use non-standard arduino specific types like byte, when there is a perfectly good and standard uint8_t available... – Majenko Mar 03 '16 at 20:14
  • As explained in the very thread you linked, ( type[] ){val, ues} is not a valid way to create a temporary in C++: http://stackoverflow.com/questions/15458883/using-array-init-list-as-temporary-in-c11#comment26614881_18163000 The solution is to typedef it. – underscore_d Jun 26 '16 at 07:12
  • Recent versions of the IDE use C++11, so whether or not it is valid in C++ doesn't really matter. – Nick Gammon Jun 26 '16 at 09:25
0

How do I fix it?

"Char []" shouldn't compile under c. It is just invalid.

You can use string, cast to char * . or just another pointer as you did already.

dannyf
  • 2,770
  • 10
  • 13