【问题标题】:Safe conversion from __int64 to size_t从 __int64 到 size_t 的安全转换
【发布时间】:2018-03-15 20:16:04
【问题描述】:

我正在使用 Visual Studio 2017 在 Windows 操作系统上工作,并且我获得了以下函数来确定来自 SO 答案之一的文件大小:

__int64 FileSize(const char *filename)
{
    HANDLE hFile = CreateFile(filename, GENERIC_READ,
        FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL, NULL);

    if (hFile == INVALID_HANDLE_VALUE) {
        return -1; // error condition, could call GetLastError to find out more
    }

    LARGE_INTEGER size;
    if (!GetFileSizeEx(hFile, &size)) {
        CloseHandle(hFile);
        return -1; // error condition, could call GetLastError to find out more
    }

    CloseHandle(hFile);
    return size.QuadPart;
}

所以,我使用它来确定文件大小,以便使用malloc() 相应地动态分配内存。由于函数malloc() 接受size_t 类型,我将FileSize() 函数的返回值分配给一个size_t 变量,但我收到以下警告:

main.cpp(67): 警告 C4244: 'initializing': 从 '__int64' 转换为 '::size_t',可能丢失数据

在这种情况下,我如何安全地将文件大小存储在 size_t 变量中?我知道我可以将返回值转换为 size_t 并消除警告,但它是否安全/正确?

【问题讨论】:

  • 是c还是c++?
  • 在我使用的 2015 MSVC 中,sizeof(size_t) 是 4。我想这就是为什么您会收到有关可能丢失数据的消息。
  • 你编译的是 32 位还是 64 位? size_t 基本上代表了你可以拥有的地址范围。
  • 如果在 32 位模式下编译,size_t 将是 32 位。文件可能大于适合 32 位的文件。如果你确定不是,你可以试试演员表。否则,您也许可以使用unsigned long longstd::uint64_t 而不是size_t
  • @NathanOliver 我正在编译 32 位。

标签: c++ c windows type-conversion warnings


【解决方案1】:

这是非常系统特定的。在某些系统上,size_t 可能小于int64_t,这会发出警告。但当然,无论如何你不能 malloc 超过 size_t 中的内容。

size_t s = (size_t)some_int64; 很可能是安全的。

但是,如果您感到偏执,可以添加检查/断言:

#include <stdint.h>
...
if(some_int64 > SIZE_MAX) // this line is fully portable
{
  halt_and_catch_fire();
}

SIZE_MAX 是一个常数,表示size_t 变量可以容纳的最大值。

【讨论】:

  • 如果不检查some_int64 &lt; 0 是否还需要从__int64size_t 的安全转换?当然不会出现负文件大小。
  • @chux 当然,如果GetFileSizeEx 吐出负值,那么所有的赌注都会被取消。但这应该由调用者代码使用FileSize 函数进行检查,该函数显然在出错时返回-1。
【解决方案2】:

size_t 类型是实现定义的。 因此无法确保 __int64 的值可以安全地存储在 size_t 类型中。 我建议使用 static_assert:

static_assert(sizeof(__int64)<=sizeof(size_t), 
"Unable to safely store an __int64 value in a size_t variable");

如果 size_t 小于 __int64,这将在编译过程中产生错误。

请参阅http://en.cppreference.com/w/cpp/types/size_t 了解更多信息。

【讨论】:

  • 这将阻止可能正确且正确的程序编译。 OP 有一个与int64_t 的接口,但这些变量的值不一定比size_t 中的值大。
【解决方案3】:

在编译 32 位应用程序时,size_t 小于 __int64

如果你知道你正在使用的文件是“小”(

UINT64 size = FileSize(...);
if (size > ~(size_t)0) // You might want to use SIZE_MAX instead of the ~ trick if you want to stay portable
{
  printf("File is too large!\n");
}
else
{
  void*data = malloc((size_t) size);
  if (!data)
    printf("Out of memory, file is too large?\n");
  else
    ...
}

另一方面,如果文件可能很大,那么您不能假设您能够一次将整个内容读入内存,因为机器可能没有足够的内存或者您可能会用完地址空间(通常在在 32 位 Windows 进程中为 2 GB)。在这种情况下,您应该使用 memory mapped files 来代替较小的视图。

【讨论】:

  • 在独角兽平台上~(size_t)0 可以大于SIZE_MAX(当int/unsignedsize_t 宽时。也许(size_t) -1?我相信它是完全可移植的。
  • 我已经在代码中使用 SIZE_MAX 进行了注释,但这是 Windows,它始终是二进制补码。
  • 这不是 2 的补码问题。但是 size_tint/unsigned 的等级 - 没有定义的东西。 - 抱歉,如果不清楚。
【解决方案4】:

如果您正在编译 32 位,size_t 将只有 32 位。

因此建议您改为返回size.LowPart,如果size.HighPart 不为零,则忽略或出错。

【讨论】:

  • 完全可以在 32 位进程中处理较大的文件,只是不能一次将整个文件读入内存。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-09
  • 1970-01-01
  • 1970-01-01
  • 2014-09-13
  • 2019-05-20
相关资源
最近更新 更多