Printf如何知道一个字符串的字符数据的地址?

考虑此代码片段:

struct My {
  operator const char*()const{ return "my"; }
} my;

CStringA s( "aha" );
printf("%s %s", s, my );


// another variadic function to get rid of comments about printf :)
void foo( int i, ... ) {
  va_list vars;
  va_start(vars, i);
  for( const char* p = va_arg(vars,const char*)
     ; p != NULL
     ; p=va_arg(vars,const char*) ) 
  {
    std::cout << p << std::endl;
  }
  va_end(vars);
}
foo( 1, s, my );

这段代码产生了"直观"的输出"啊哈"。但我不知道这是怎么回事:

  • 如果变量函数调用被转换为推送参数的指针,则printf将收到一个被解释为const char*
  • CStringA*
  • 如果变量函数调用正在对它调用operator (const char*),为什么它不为我自己的类调用?

有人能解释一下吗?

编辑:添加了一个虚拟变量函数,该函数将其参数视为const char*s。请注意,当它到达my参数时,它甚至会崩溃...


解决方案

C++98标准的相关文本§5.2.2/7:

在参数表达式上执行左值到右值(4.1)、数组到指针(4.2)和函数到指针(4.3)的标准转换。在这些转换之后,如果参数没有算术、枚举、指针、指向成员的指针或类类型,则程序的格式不正确。如果参数具有非POD类类型(第9条),则行为未定义。

因此正式行为未定义。

但是,给定的编译器可以提供任意数量的语言扩展,而Visual C++确实提供了。关于将参数传递给...

,MSDN Library记录了Visual C++的行为
  • 如果实际参数的类型为Float,则在函数调用之前将其提升为Double类型。
  • 任何有符号或无符号的字符、短、枚举类型或位字段都使用整数提升转换为有符号或无符号整型。
  • 类类型的任何参数都通过值作为数据结构传递;副本是通过二进制复制创建的,而不是通过调用类的复制构造函数(如果存在)来创建。

这篇文章没有提到有关Visual C++应用用户定义的转换的任何内容。

MSCString被巧妙地布局,因此它的POD表示恰好是指向其以空结尾的字符串的指针。(sizeof(CStringA) == sizeof(char*))当它在任何printf样式函数中使用时,该函数Get只是传递了字符指针。

由于上面的最后一点和CString的布局,所以这是可行的。

相关文章