【发布时间】:2017-03-16 20:29:19
【问题描述】:
我们有这个简单的类:
class MyStr
{
public:
MyStr() { }
wchar_t* pStr;
};
以及使用它的代码:
MyStr ms1, ms2;
ms1.pStr = L"MyStr!";
ms2.pStr = L"MyStr2!";
wchar_t buffer[100];
swprintf(buffer, 100, L"%ls - %ls", ms1, ms2);
将 ms1 和 ms2 推入堆栈的程序集如下所示:
FF B5 24 FE FF FF push DWORD PTR [ebp-0x1dc]
FF B5 20 FE FF FF push DWORD PTR [ebp-0x1e0]
它实际上是将 MyStr(在本例中为 pStr)的值/内容推送到堆栈上。
如果我们将 MyStr 更改为仅添加一个简单的析构函数:
class MyStr
{
public:
MyStr() { }
~MyStr() { }
wchar_t* pStr;
};
现在正在传递 ms1 和 ms2 的地址,而不是它们的值/内容。
8D 85 24 FE FF FF lea eax,[ebp-0x1dc]
50 push eax
8D 85 20 FE FF FF lea eax,[ebp-0x1e0]
50 push eax
Windows 上的 Visual Studio 以两种方式给出相同的结果(总是传递值/内容),但 Linux 上的 gcc 给出这两种不同的结果。
- 为什么?
- 我们可以做些什么来保留析构函数但通过值而不是引用/地址传递?
我们不能做的是更改 swprintf 行 - 有成千上万个,我们正在努力避免更改它们。
【问题讨论】:
-
你编译优化了吗?
-
在这两种情况下都没有优化。
-
swprintf接受的参数是有限的。而类对象不是其中之一。 -
printf系列函数采用良好的 C 风格变量参数。它会吃任何东西并尝试根据格式字符串中的规则来消耗它。MyStr恰好包含一个 c 风格的字符串,它满足%ls,所以你在这里避开了未定义的行为项目符号,但它仍然是未定义的行为。 -
我假设您知道您的代码是未定义的行为,并且您正在询问如何“控制”未定义的方面?
标签: c++ linux gcc parameter-passing calling-convention