2013-02-05 62 views
2

变量争论函数如何将对象传递给struct/class? 例如:将对象传递给var arg函数

CString a_csName; 
CString a_csAge(_T("20")); 
a_csName.Format(_T("My Age is : %s"),a_csAge); 

这里CString::Format是一个printf风格可变arguement函数,它接受这个CString对象。这怎么可能?

+0

使用可变参数模板的CString :: Format? CString是否只包含一个属性,它是一个指向char的指针? –

+0

void __cdecl CString :: Format(_In_ _Printf_format_string_ PCXSTR pszFormat,...); – surega

+0

http://stackoverflow.com/questions/205529/c-c-passing-variable-number-of-arguments-around –

回答

4

经过MFC代码的一些研究和调试发现下面,希望它有助于谁面临着名的静态代码分析器错误“将struct'CStringT'传递给省略号”,这实际上也是我怀疑的根源。

http://www.gimpel.com/html/bugs/bug437.htm

Format函数是一个可变参数函数,取决于存在于第一参量的格式说明符。第一个参数总是一个char *。

它分析格式说明符(%s,%d,%i ...),并根据找到的格式说明符的索引读取var_arg数组,并直接转换为char * if%s或int if% d被指定。

因此,如果一个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或用户定义的数据结构,它没有什么差别。

+0

你为什么回答你自己的问题? –

+1

我研究了一下,调试了一下,发现了这个。因此思想会分享它。 – surega

4

来源:http://msdn.microsoft.com/en-us/library/aa300688%28v=vs.60%29.aspx

  • 您可以为const char *与LPCTSTR函数参数自由替代CString的对象。

大概CString是没有虚函数表的一类,并用仅char*类型的属性。这意味着sizeof(CString) == sizeof(const char*),并且如果您将一个CString重新解析为const char*,那么您将得到一个工作const char*

编译器不应该接受将结构传递给函数的可变参数部分。但如果我没有错,在GCC中,当你将一个结构传递给可变参数时,它的内存被复制(即不使用拷贝构造函数)并发出警告。我猜MSVC在那里做的是一样的,然后,Format方法只是假设给定的数据是const char*

让我给你一个替代的例子。假设你有一个没有vtable的类,只有成员是int。如果你把它传递给一个可变参数,编译器会复制这个对象的内容,这只是一个int。在函数实现中,你(不知何故)知道你收到了一个int。然后你查询参数,询问int。因为在内存级别上,你的班级与int没什么区别,所以事情会“很好”。该函数将访问该类的int属性。

+0

那么这是否意味着Format会接受任何只有const char *成员的结构?还是有一些CString对象的特殊处理? – surega

+0

在代码(而不是编译器)级别有CString处理*特殊*处理对象在内存中的布局非常仔细地管理以实现这种效果。一般来说,你不应该通过可变参数传递类对象。如果您绝对*必须*执行此操作,请将指针传递给该对象。 –

+0

@surega编辑说明更好 –

2

我最近不得不清除这个Lint错误,并偶然发现这个线程。

虽然这是一个有趣的调查正在发生,避免lint警告的最简单的方式转换是通过使用CString的方法Get.String()作为传递CString的指针,而不是CString的结构如下

a_csName.Format(_T("My Age is : %d"), a_csAge.GetString()); 
0

让我开始以不同的方式回答这个问题。

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)大于格式(%d)所指定的数据大小(4为Value)会怎么样?那么,在这种情况下,所设计的类需要确保给定类的大小与printf函数使用的参数大小相同。

我不会提及它的细节,但CString使用内部类CStringData。后一类包含字符串,长度,参考计数等。CString(实际CSimpleStringT)使用此类,但只保留指针char/wchar_t,由CStringData管理。

很大程度上,CString实现为String类提到(只要数据和sizeof去)。