从 __int64 到 size_t 的安全转换

Safe conversion from __int64 to size_t

我正在 Windows OS 和 Visual Studio 2017,我获得了以下函数来确定来自 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() 函数的 return 值分配给 size_t 变量,但我收到以下警告:

main.cpp(67): warning C4244: 'initializing': conversion from '__int64' to '::size_t', possible loss of data

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

这在很大程度上是系统特定的。在某些系统上,size_t 可能小于 int64_t,这会发出警告。但是当然你不能 malloc 超过适合 size_t 的内容。

最有可能安全的做法是 size_t s = (size_t)some_int64;

但是,如果您感到偏执,可以添加一个 check/assert:

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

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

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

如果您正在为 32 位编译,size_t 只会是 32 位。

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

当您编译 32 位应用程序时,

size_t 小于 __int64

如果您知道您正在使用的文件是 "small"(< 2 GB),您可以通过转换稍微回避这个问题,如果文件太大则中止:

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
    ...
}

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