C++ printf如何知道CString的地址;s字符数据?

C++ printf如何知道CString的地址;s字符数据?,c++,type-conversion,printf,variadic-functions,C++,Type Conversion,Printf,Variadic Functions,考虑到这个代码片段: 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,

考虑到这个代码片段:

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 );
struct My{
运算符const char*()const{return“my”;}
}我的;
CStringA s(“aha”);
printf(“%s%s”,s,my);
//另一个用于消除printf注释的变量函数:)
void foo(int i,…){
va_列表变量;
va_启动(vars,i);
for(const char*p=va_arg(vars,const char*)
;p!=NULL
;p=va_arg(变量,常量字符*)
{

std::cout您所做的是未定义的行为,或者是编译器提供的非标准扩展,或者是完全靠运气工作的。我猜CString将字符串数据存储为结构中的第一个元素,因此,从
CString
中读取字符串数据,就像它是
char*
一样,会产生一个有效的null termi细绳

如果变量函数调用正在对其调用运算符(const char*),为什么不对我自己的类这样做


是的,但是您应该在代码中显式地强制转换它:
printf(“%s”,(LPCSTR)s,…);
您的printf语句是错误的:

printf("%s", s, my );
应该是:

printf("%s %s", s, my);
它会打印出“啊哈,我的”

CString有一个用于
const char*
的对话运算符(它实际上用于
LPCTSTR
,它是一个
const TCHAR*
-
CStringA
有一个用于
LPCSTR
的转换函数)

printf
调用不会将您的
CStringA
对象转换为
CStringA*
指针。它本质上将其视为一个
void*
。对于CString来说,这纯粹是运气使然(或者可能是微软开发人员的设计利用了一些不符合标准的东西)如果你使用
\u bstr\u t
来代替(首先是字符串大小),尽管有转换功能,它还是会失败


调用
printf
(或任何可变函数)时,将对象/指针显式转换为所需的对象/指针是一种很好的做法(在许多情况下也是必需的)。

不能将非POD数据插入可变函数。

如果变量函数调用被转换为推动参数的指针

变量函数不是这样工作的。参数的值,而不是指向参数的指针,是在内置类型(如char到int)的特殊转换规则之后传递的

C++03§5.2.2p7:

当给定参数没有参数时,参数的传递方式应确保接收函数可以通过调用va_arg(18.7)、从左值到右值(4.1)、从数组到指针(4.2)和从函数到指针(4.3)来获取参数值对参数表达式执行标准转换。在这些转换之后,如果参数没有算术、枚举、指针、指向成员的指针或类类型,则程序格式错误。如果参数具有非POD类类型(第9条),行为未定义。如果参数具有服从整数提升(4.5)的整数或枚举类型,或具有服从浮点提升(4.6)的浮点类型,参数的值将在调用之前转换为升级类型。这些升级称为默认参数升级

特别是从上述方面:

如果参数具有非POD类类型(第9条),则行为未定义

C++将va_arg定义为C,C99 TC3§7.15.1.2p2说明:

…如果类型与实际下一个参数的类型不兼容(根据默认参数提升),则行为未定义,以下情况除外:[此处不适用的情况列表]

因此,如果您传递一个类类型,它必须是POD,并且接收函数必须应用正确的类型,否则行为是未定义的。这意味着在最坏的情况下,它可能完全按照您的预期工作


Printf不会为任何用户定义的类类型应用正确的类型,因为它不知道这些类型,因此您无法将任何UDT类类型传递给Printf。您的foo也会通过使用字符指针而不是正确的类类型来执行相同的操作。

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

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

因此,在形式上,行为是未定义的

编译器可以提供任意数量的语言扩展,VisualC++可以。Visual C++的行为如下:关于将参数传递给<代码>…<代码>:< /P>

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

这不涉及VisualC++应用用户定义的转换。

您真正感兴趣的函数可能是
CString::Format
,这取决于上面的最后一点


欢呼和H.T.P/> < P> >它甚至不调用<代码>操作符conchch**/Cuff>。VisualC++将类数据传递给<代码> Prtff<代码>,如<代码> MeMCPY 。它的工作是因为 cStule>代码>类:它只包含一个成员变量,这是一个POIN。