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。