2

变量争论函数如何将对象带到结构/类?例如 :

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

这是一个采用此对象CString::Format的 printf 风格的变量论证函数。CString这怎么可能?

4

4 回答 4

4

来自: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类的属性。

于 2013-02-05T14:18:41.630 回答
4

在通过 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 或用户定义的数据结构,它没有任何区别。

于 2013-02-06T06:04:22.873 回答
2

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

尽管这是对正在发生的强制转换的有趣调查,但避免 Lint 警告的最简单方法是使用 CString 方法传递 CString 指针而不是 CString 结构,Get.String()如下所示

a_csName.Format(_T("My Age is : %d"), a_csAge.GetString());
于 2014-01-17T22:17:25.930 回答
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)大于Value格式( )指定的数据大小( 4 )怎么办%d。那么,在这种情况下,设计的类需要确保给定类的大小与printf函数使用的参数大小相同。

我不会提及它的细节,而是CString使用 internal class CStringData。后一个类保存字符串、长度、引用计数等。CString(实际上CSimpleStringT)使用这个类,但只保存char/的指针wchar_t,由CStringData.

很大程度上,CString是作为String提到的类实现的(就数据sizeof而言)。

于 2014-01-18T09:23:22.020 回答