从 __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 而不是较小的视图。
我正在 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
不为零,则忽略或出错。
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 而不是较小的视图。