Why does ostream prints `1` for a string defined as `volatile char[]`? [duplicate]

泄露秘密 提交于 2019-12-21 07:04:11

问题


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?


回答1:


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.




回答2:


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.




回答3:


Answer found here by a minimal amount of web searching:

Short answer: cout is interpreting the object as a bool due to the volatile 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 the void* overload can be used when the << operator is called. There's no volatile qualified overload, and the closest match is the bool 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*.)




回答4:


It's the volatile qualifier that casts it to a bool, try instead:

std::cout << const_cast<char*>(test) << "\n";


来源:https://stackoverflow.com/questions/24555288/why-does-ostream-prints-1-for-a-string-defined-as-volatile-char

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!