0

I can't find where it's documented that casting a string to char results in a "c" character:

Serial.println(char(67));  // => C
Serial.println(char(81));  // => Q
Serial.println(char('C')); // => C
Serial.println(char('Q')); // => Q
Serial.println(char("C")); // => c
Serial.println(char("Q")); // => c
AJP
  • 26,547
  • 23
  • 88
  • 127
  • 4
    Possible duplicate of [Single quotes vs. double quotes in C or C++](https://stackoverflow.com/questions/3683602/single-quotes-vs-double-quotes-in-c-or-c) – yakobyd Jun 23 '19 at 23:35
  • 4
    Remove the cast. Why is it there in the first place? (I can understand casting `81`, but not `"C"`). – Paul Sanders Jun 23 '19 at 23:36
  • 3
    You might consider configuring your compiler to treat warnings as errors. Then the compiler would sensibly fail to compile that code with the message `error: cast from 'const char*' to 'char' loses precision [-fpermissive]` rather than looking at an address (yes, "strings" are pointers), find its most significant byte, and print that as a Basic Latin character, assuming the byte corresponded to a printable character.... – Ray Toal Jun 23 '19 at 23:47
  • 1
    avr-gcc on godbolt correctly gives an error message, I wonder what on earth the arduino IDE is doing and what other errors it doesn't diagnose – M.M Jun 23 '19 at 23:55
  • Someone on https://arduino.stackexchange.com/ might know – M.M Jun 24 '19 at 00:05
  • 2
    @RayToal Technically string literals are arrays, not pointers. – aschepler Jun 24 '19 at 00:12
  • @RayToal can you add as an answer please as this is explaining the behaviour. @PaulSanders I'm a newbie to C++ and thought I understand what `char()` meant, obviously not. Thank you for the help. @M.M the IDE doesn't do much. Looks like this is something I should change asap. – AJP Jun 24 '19 at 00:19
  • @aschepler Woops you are right, thanks! Too late to edit tho.... – Ray Toal Jun 24 '19 at 01:31

2 Answers2

4

"Q" actually is a character array of two characters: { 'Q', '\0' } (null-terminated C-string of length 1), residing at some specific address in memory.

Arrays, explicitly defined or coming from string literals doesn't matter, decay to pointers automatically in most contexts, e. g. when passing as function arguments, dereferencing them, … – and especially, too, when applying a cast to them!

So what happens here is actually equivalent to

char const* ptr = "Q";
char(ptr);

Actually, this is undefined behaviour, as char, be it signed or not, is not large enough to hold a pointer value, so anything could happen. Under the hoods, the code will most likely be treated as if you (fully legally) did:

char(unsigned char(uintptr_t(ptr)))

simply cutting off the most significant three bytes.

What remains is the least significant byte of the memory address, and it's just pure accident that it matches 99, the ASCII value of c, it could have been any value else.

Aconcagua
  • 24,880
  • 4
  • 34
  • 59
1

A string literal such as "C" is an array of characters. char("C") converts the array into a character. In value contexts such as this, an array implicitly decays into pointer to first element. Therefore this is a conversion from pointer to an integer type (character types are integer types).

There exists no conversion from pointer to integer types except to such integer types that can represent all pointer values. As such char("C") is an ill-formed expression on any system where char cannot represent all pointer values.

A compiler that does not diagnose the error does not conform to the standard. If a compiler successfully compiles an ill-formed program, it's completely up to the compiler how it should behave; it is out of the scope of the language standard.

eerorika
  • 232,697
  • 12
  • 197
  • 326