Consider this (artificial) example:
#include <cstdio>
#include <iostream>
int main() {
volatile char test[] = "abc";
std::printf("%s\n", test);
std::cout << test << "\n";
}
Compiling it with GCC and running gives the following output:
$ g++ test.cc
$ ./a.out
abc
1
As you can see printf
prints the string correctly while cout
prints 1
. Why does writing to cout
produces 1
in this case?
The only suitable overload of operator<<
is that for bool
, so the array is converted (via a pointer) to bool
, giving true
since its address is non-null. This outputs as 1
unless you use the std::boolalpha
manipulator.
It can't use the overload for const char *
which would output the string, or that for const void *
which would output the pointer value, since those conversions would require removing the volatile
qualifier. Implicit pointer conversions can add qualifiers, but can't remove them.
To output the string, you'd have to cast away the qualifier:
std::cout << const_cast<const char*>(test) << "\n";
but beware that this gives undefined behaviour since the array will be accessed as if it were not volatile.
printf
is an old-school variadic function, giving no type safety. The %s
specifier makes it interpret the argument as const char *
, whatever it actually is.
The std::basic_ostream::operator<< only has an overload for const char*
or const void*
which does not match in this case since you can not discard the volatile qualifier without a cast, this is covered in the draft C++ standard section 4.4
Qualification conversions which says:
A prvalue of type “pointer to cv1 T” can be converted to a prvalue of type “pointer to cv2 T” if “cv2 T” is more cv-qualified than “cv1 T”.
so it is using the bool
version and since it is not a nullptr
the result is true
.
If you remove the volatile qualifier from test
this will provide the result you expect. Several answers suggest using a const_cast
to remove the volatile qualifiers but this is undefined behavior. We can see by going to section 7.1.6.1
The cv-qualifiers paragraph 6 which says:
If an attempt is made to refer to an object defined with a volatile-qualified type through the use of a glvalue with a non-volatile-qualified type, the program behavior is undefined.
const_cast
in this case yields a prvalue but dereferencing that pointer yields an lvalue which will invoke undefined behavior.
Answer found here by a minimal amount of web searching:
Short answer:
cout
is interpreting the object as abool
due to thevolatile
qualifier. It's a quirk of overloading for the<<
operator.Long answer: A volatile pointer can't be converted to a non-volatile pointer without an explicit cast, so neither the
char*
nor thevoid*
overload can be used when the<<
operator is called. There's no volatile qualified overload, and the closest match is thebool
overload, thus your array is interpreted as a boolean value rather than an address or a string.You can fix it a number of ways, but an explicit cast is probably what you wanted:
std::cout<< (char*)test <<std::endl;
(Personally I would cast to const char*
.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With