I get what's happening in the first three columns generated by the sketch below. I'm mystified by the fourth column.
Any light to be shed?
void setup() {
Serial.begin(115200);
Serial.println("dec\thex\tasc\t?");
for (int a = 0; a < 128; a++) {
Serial.print(a); // prints decimal value
Serial.print("\t");
Serial.print(a, HEX); // prints hexadecimal value
Serial.print("\t");
Serial.print(a, 0); // prints ASCII character value
Serial.print("\t");
Serial.println(a,""); // prints xx??
}
}
void loop() {
// put your main code here, to run repeatedly:
}
It's going to try to print the number in a specified numeric base. Said base is the address of the string literal (illegally) converted to an int. Makes no sense.
TheMemberFormerlyKnownAsAWOL:
What numeric base is ""?
None that I know of. That's the weirdness.
gfvalvo:
What did you expect it to do?
It's going to try to print the number in a specified numeric base. Said base is the address of the string literal (illegally) converted to an int. Makes no sense.
I didn't expect anything particular, I was just tinkering trying different things to see if I could get Serial.print() to print a character value the same way you do by appending HEX or DEC or BIN. Turns out zero will do that. But I found that out *after * I observed the "" thing.
dougp:
I was just tinkering trying different things to see if I could get Serial.print() to print a character value the same way you do by appending HEX or DEC or BIN. Turns out zero will do that. But I found that out *after * I observed the "" thing.
Take a look at the source code in Print.cpp. You'll find all the possible variants.
dougp:
Turns out "" is equivalent to base 36 ( numerals 0-9 + letters a-z).
No. "" is a pointer to a c-string literal that only contains the null terminator. Using it as the second argument to print() illegally converts the pointer to an int. Turn up the compiler warnings, you'll see the complaints.
Perhaps, in the case of your program it is equal to 36. Perhaps it just appears that way to you.
This is a simple case of garbage in garbage out.
While the Print class API is not the best design, this is a bug in the sketch, not the Print class code.
The sketch gave a garbage parameter to the print() function and got garbage output.
I would also recommend enabling warnings in the IDE. IMO, warnings should be enabled by default.
While the print() function supports an int for the base value, the underlying printNumber () used for the output formatting uses a uint8_t as the base parameter which is 0-255 but the actual code only supports base values between 2 and 255.
The printNumber() code does little sanity checking on the base value passed in.
It only looks for a base of less than 2 and changes it to 2.
So the actual base values supported are 2-255.
Given the garbage base value passed in to print(), (a pointer to a null C-string), depending on where the string lands in memory, the lower 8 bits of the pointer (memory address) could be anything.
So, depending on the specific code build, it could end up being any base value between 0 and 255.
i.e. if you change the code, and the memory location of the string changes, the value that gets passed to print() as base would also change.
For example, add a few other strings above this and the memory location of the null string will likely change.
bperrybap:
I would also recommend enabling warnings in the IDE. IMO, warnings should be enabled by default.
They are now.
bperrybap:
While the print() function supports an int for the base value, the underlying printNumber () used for the output formatting uses a uint8_t as the base parameter which is 0-255 but the actual code only supports base values between 2 and 255.
The printNumber() code does little sanity checking on the base value passed in. It only looks for a base of less than 2 and changes it to 2.
So the actual base values supported are 2-255.
I did notice that compare in print.cpp. Which makes it all the more curious that Serial.print(a, 0); produces the ASCII character for the value of the variable - it even correctly prints blanks for the control characters from 0-3110.
bperrybap:
Given the garbage base value passed in to print(), (a pointer to a null C-string), depending on where the string lands in memory, the lower 8 bits of the pointer (memory address) could be anything.
So, depending on the specific code build, it could end up being any base value between 0 and 255.
i.e. if you change the code, and the memory location of the string changes, the value that gets passed to print() as base would also change.
For example, add a few other strings above this and the memory location of the null string will likely change.
That doesn't seem to be what happens. Or I'm not understanding. By adding a string and changing the number of characters it contains the (bizarre) radix changes but, it always outputs a sequence that starts at zero and progresses through 0-9 then A-Z. After that it's erratic what comes next. Sometimes there are no lower case letters, sometimes there are special characters, the ones in the ASCII following 'Z'.
dougp:
Which makes it all the more curious that Serial.print(a, 0); produces the ASCII character for the value of the variable - it even correctly prints blanks for the control characters from 0-3110.
The answers to all your questions are in Print.cpp. All you have to do is look:
size_t Print::print(unsigned long n, int base)
{
if (base == 0) return write(n);
else return printNumber(n, base);
}
That does make sense. I was looking at the compare < 2 in the private methods. I believe a little light has dawned.
Bear with me a bit more?
So, the size_t Print::print(xxx) statements are a collection of overloaded functions/methods for dealing with all the available printing possibilities? This would explain why using Serial.print adds so many bytes to a sketch.
And, write() is one of the last steps before data is delivered to the UART (with whatever's in the parens)?
I thought C++ was usually pretty picky about automatically casting pointers to integers and vis versa? How come this isn't an error, rather than just a warning?
Perhaps it's worth making "phony" overloaded functions that generate compile-time errors to catch this sort of thing?
the size_t Print::print(xxx) statements are a collection of overloaded functions/methods for dealing with all the available printing possibilities? This would explain why using Serial.print adds so many bytes to a sketch.
Your object file will only include the "printing possibilities" that you actually use (making it significantly more efficient that printf(), for example)...
Interesting. ARM compiles seem to do "the right thing"
/var/folders/gv/zn3wcml52jq0vvjnd95j8g6h0000gp/T/arduino_modified_sketch_836246/sketch_dec01a.ino: In function 'void setup()':
sketch_dec01a:12:24: error: invalid conversion from 'const char*' to 'int' [-fpermissive]
Serial.println(a,""); // prints xx??
^
westfw:
Your object file will only include the "printing possibilities" that you actually use (making it significantly more efficient that printf(), for example)...