变量争论函数如何将对象带到结构/类?例如 :
CString a_csName;
CString a_csAge(_T("20"));
a_csName.Format(_T("My Age is : %s"),a_csAge);
这是一个采用此对象CString::Format
的 printf 风格的变量论证函数。CString
这怎么可能?
来自:http: //msdn.microsoft.com/en-us/library/aa300688%28v=vs.60%29.aspx
- 您可以自由地将 CString 对象替换为 const char* 和 LPCTSTR 函数参数。
可能CString
是一个没有 vtable 的类,并且只有一个 type 的属性char*
。这意味着,sizeof(CString) == sizeof(const char*)
如果您将 a 重新解释CString
为 a,const char*
您将得到一个 working const char*
。
编译器不应接受将结构传递给函数的可变参数部分。但是如果我没记错的话,在 GCC 中,当您将结构作为可变参数传递时,它的内存被复制(即不使用复制构造函数)并发出警告。我猜 MSVC 在那里做同样的事情,然后,该Format
方法只是假设给定的数据是const char*
.
让我给你一个替代的例子。假设您有一个没有 vtable 的类,其中只有成员是 int。如果将它传递给可变参数,编译器将复制此对象的内容,这只是一个 int。在函数实现中,您(不知何故)知道您收到了一个int
. 然后你查询参数要求一个int
. 由于在内存级别,您的课程与 a 没有什么不同int
,因此一切都会“正常”。该函数将访问int
类的属性。
在通过 MFC 代码进行一些研究和调试后发现以下内容,希望它可以帮助那些面临着名的静态代码分析器错误“将 struct 'CStringT' 传递给省略号”的人,这实际上也是我怀疑的根源。
http://www.gimpel.com/html/bugs/bug437.htm
格式函数是一个可变参数函数,取决于第一个参数中存在的格式说明符。第一个参数始终是 char *。
它解析格式说明符(%s,%d,%i…) 并根据找到的格式说明符的索引读取 var_arg 数组,如果指定了 %s,则直接转换为 char * 或 int 。
因此,如果指定了 CString 并且相应的格式说明符是 %s,则在 CString 对象上进行对 char * 的直接强制转换尝试。
CString a_csName;
CString a_csAge(_T("20"));
a_csName.Format(_T("My Age is : %s"),a_csAge);
wcout<<a_csName;
会打印我的年龄是 20
CString a_csName;
CString a_csAge(_T("20"));
a_csName.Format(_T("My Age is : %d"),a_csAge);
wcout<<a_csName;
将打印我的年龄是 052134
所以这背后没有智慧。只是直接演员。因此,我们传递了一个 POD 或用户定义的数据结构,它没有任何区别。
我最近不得不清除这个 Lint 错误并偶然发现了这个线程。
尽管这是对正在发生的强制转换的有趣调查,但避免 Lint 警告的最简单方法是使用 CString 方法传递 CString 指针而不是 CString 结构,Get.String()
如下所示
a_csName.Format(_T("My Age is : %d"), a_csAge.GetString());
让我开始以不同的方式回答这个问题。
struct Value
{
int nValue;
float fOtherValue;
};
int main()
{
Value theValue;
theValue.nValue = 220;
theValue.fOtherValue = 3.14f;
printf("Value: %d", theValue);
}
此代码将打印220
没有任何问题。但是如果你在 之后传递第二个参数theValue
,它不会:
printf("Values: %d, %f", theValue, theValue.fOtherValue);
由于第一个变量参数theValue
不符合%d
参数指定的大小。因此,3.14
不会(可能不会)显示。让我们不要谈论如何printf
在堆栈上推送参数,va_start
以及类似的事情是如何工作的。
同样,当我们这样设计一个String
类时:
struct String
{
char* pData;
public:
String()
{
pData = new char[40];
strcpy_s(pData, 40, "Sample");
}
};
并以这种方式使用它:
String str;
printf("%s", str);
它肯定会奏效。
但是参数和调用堆栈损坏呢?如果类的大小(如Value
)大于Value
格式( )指定的数据大小( 4 )怎么办%d
。那么,在这种情况下,设计的类需要确保给定类的大小与printf
函数使用的参数大小相同。
我不会提及它的细节,而是CString
使用 internal class CStringData
。后一个类保存字符串、长度、引用计数等。CString
(实际上CSimpleStringT
)使用这个类,但只保存char
/的指针wchar_t
,由CStringData
.
很大程度上,CString
是作为String
提到的类实现的(就数据sizeof
而言)。