【问题标题】:gcc object passing by value or by addressgcc 对象按值或按地址传递
【发布时间】: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 给出这两种不同的结果。

  1. 为什么?
  2. 我们可以做些什么来保留析构函数但通过值而不是引用/地址传递?

我们不能做的是更改 swprintf 行 - 有成千上万个,我们正在努力避免更改它们。

【问题讨论】:

  • 你编译优化了吗?
  • 在这两种情况下都没有优化。
  • swprintf 接受的参数是有限的。而类对象不是其中之一。
  • printf 系列函数采用良好的 C 风格变量参数。它会吃任何东西并尝试根据格式字符串中的规则来消耗它。 MyStr 恰好包含一个 c 风格的字符串,它满足 %ls,所以你在这里避开了未定义的行为项目符号,但它仍然是未定义的行为。
  • 我假设您知道您的代码是未定义的行为,并且您正在询问如何“控制”未定义的方面?

标签: c++ linux gcc parameter-passing calling-convention


【解决方案1】:

你可以求助于宏

// Forward call to your method
#define swprintf(...) my_swprintf(__VA_ARGS__)

// Your class
class MyStr
{
public:
    MyStr() { }
    ~MyStr() { }
    const wchar_t* pStr;
};


// helper function to "fix" parameters
template <typename T>
std::enable_if_t<std::is_arithmetic<T>::value || std::is_pointer<T>::value, T>
normalize(T t) { return t; } // Default one which does nothing

// Fix for your class to return its member.
const wchar_t* normalize(const MyStr& t) { return t.pStr; }

// Your function which (re-)forward to real swprintf with fixed parameter
// Extra parent do avoid macro substitution
template <typename ... Ts>
auto my_swprintf(wchar_t* buffer, const wchar_t* format, const Ts&... args)
{
    return (swprintf)(buffer, format, normalize(args)...);
}

【讨论】:

  • 严格来说,它不是宏,是吗?
  • @PeterA.Schneider:我将swprintf定义为“宏函数”。
  • 哦,我错过了。谢谢。顺便说一句,(swprintf)my_swprintf 函数内的括号会阻止宏的扩展吗?如果是这样,我不知道这种机制(并且会 #undefed 它)。
  • @PeterA.Schneider:正如我在评论中所写,"// Extra parent do Avoid macro替换",所以是的(swprintf)避免了宏功能的扩展。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-16
  • 2012-10-14
  • 2019-02-10
  • 1970-01-01
  • 2018-07-20
  • 2011-11-24
相关资源
最近更新 更多