【问题标题】:How to convert a 32 bit signed integer value to 64 bit signed integer equivalent in C如何在 C 中将 32 位有符号整数值转换为等效的 64 位有符号整数
【发布时间】:2019-03-27 08:35:03
【问题描述】:

我有一个编译为使用 32 位有符号整数的库。当其他应用程序使用标志编译它们时,例如:-DODBC64 它将我在库中使用的相同类型提升为 64 位有符号整数。 例如:

 #ifdef ODBC64
       typedef sint64 SLEN;
 #else
       #define SLEN int
 #endif

当应用程序将引用传递给我的库时:

SLEN count;
mylibraryfunction(&count);

返回给应用程序的值如下所示:

sizeof(SLEN) = 8
sizeof(SLEN) in my library = 4
m_AffectedRows BEFORE = 0x3030303030303030
m_AffectedRows AFTER = 0x3030303000000000        0

您可以看到我的库中的分配正在复制 4 个字节(值 0)。 我需要知道一种将高 4 字节重置为 0 的方法。 例如:

0x0000000000000000

我尝试过 static_cast 和 reinterpret_cast,但都没有帮助。

【问题讨论】:

  • 这很关键。您通过地址将 64 位 int 传递给一个函数,该函数将其解释为 32 位 int 信任它的声明。这是因为链接器无法识别同名符号的不同类型,并且可能会在没有任何警告的情况下链接它,但它仍然是错误的。
  • 这听起来确实很危险。有什么理由让一侧有 32 位,另一侧有 64 位?如果是这样,我建议为每个函数编写一个小包装器,它只是进行类型转换(并且可能检查溢出)。
  • 我会提供 2 个不同版本的库吗?一次用于 8 字节使用量,一次用于它们可以链接的应用程序的 4 字节使用量。
  • 我不清楚为什么你需要不同的 int 大小。您是为 x86-32 和 x86-64 编译,还是 32 位版本节省大量内存?
  • 这就是为什么我们应该使用 stdint.h 类型而不是一些本地车库标准...

标签: c++ c casting


【解决方案1】:

我制作了一个类似于 OP 案例的 MCVE。

我什至不需要额外的库,只需要两个翻译单元(产生两个目标文件)。

第一lib.cc

#include <cstdint>

extern "C" void func(int32_t *pValue);

void func(std::int32_t *pValue)
{
  *pValue = 0;
}

第二个prog.cc

#include <iostream>
#include <iomanip>

// how prog.cc "knows" func():
extern "C" void func(int64_t *pValue);

int main()
{
  int64_t value = 0x0123456789ABCDEFull;
  std::cout << "value before: " << std::hex << value << '\n';
  func(&value);
  std::cout << "value after : " << std::hex << value << '\n';
  return 0;
}

编译器错误?不,每个翻译单元都使用func()的原型。

链接器错误?不。符号匹配,其他任何东西都超出了链接器的视野。

我必须承认我必须使用extern "C" 来实现这一点。否则,至少,C++ 名称修改阻止了正确的链接。 (当我意识到这一点时,我用 C 编写了代码。)

输出:

value before: 123456789abcdef
value after : 123456700000000

Live Demo on wandbox

这是非常危险的! 任何外部符号的使用都应使用 100% 兼容的声明。 (是的,C 和 C++ 提供了多种方式来攻击你自己的脚。)


想象一下,如果 lib.cc 函数 func() 写入 int64_t 会发生什么,prog.cc 将传递一个指向 int32_t 的指针:越界访问可能带来更多灾难性后果。

【讨论】:

  • 这是我的想法。虽然我不能让用户使用不同的 obj 链接,但我需要一种方法来根据他们的应用程序同时使用 32 位和 64 位变体。我提供的是最简单的说明形式,但实际用例需要用户链接到库,它应该能够正确解释类型。
  • 我不认为单个库可以处理对不同存储的相同名称的解释。因此,我认为 2 个库是解决方案的原因。
  • @SabaKauser 恕我直言,Lundin 在他的评论中明确表示:对于 API,stdint.h 类型的使用似乎更可靠。顺便提一句。即使在大多数 64 位平台上,int 也是 32 位的。此外,库应始终具有相同的平台。因此,声明应该足够干净以防止此类问题。
  • @SabaKauser 如果您需要最多 64 位整数,为什么不 int64_t?这在 32 位平台上也受支持。如果它不能在超过 32 位的 32 位平台上使用,则运行时检查可能会检测到意外的错误使用。
  • 谢谢。要求是某些驱动程序管理器将有问题的类型视为 32 位和某些 64 位。因此,设施应该是让他们链接到我的 lib 的正确版本,因为这个决定需要由开发人员根据他们使用的驱动程序管理器做出。
【解决方案2】:

您的库无法访问前 4 个字节,但您可以在调用它之前访问它。所以尝试先用 0 初始化它:

SLEN count = 0; // initialize with 0's
mylibraryfunction(&count);

【讨论】:

  • 不过,这不会进行符号扩展。为此,您可以在调用库函数后分配count = int(count);
  • 首先,这没有解决将 64 位 int 值寻址为 32 位 int 值的问题。无论变量如何初始化,这都是错误的。其次,OP 提供了一个库。该库怎么可能初始化一个在调用该库的客户编写的代码中的变量
猜你喜欢
  • 1970-01-01
  • 2013-12-22
  • 2019-08-15
  • 2019-05-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多