简体   繁体   中英

Safe conversion from __int64 to size_t

I am working on Windows OS with Visual Studio 2017, and I obtained the following function to determine the size of a file from one of SO's answers:

__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;
}

So, am using it to determine a file size, in order to allocate memory dynamically accordingly with malloc() . Since the function malloc() accept a size_t type, I assigned the return value of FileSize() function to a size_t variable, but I got the following warning:

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

In this case, How would I safely store the size of the file within a size_t variable? I know I could cast the return value to size_t and dismiss the warning, but would it be safe/correct?

This is very much system-specific. On some systems, size_t may be smaller than int64_t , which would give the warning. But of course you can't malloc more than what will fit inside a size_t anyway.

It is most likely safe to do size_t s = (size_t)some_int64; .

However, if you are feeling paranoid, you can add a check/assert:

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

SIZE_MAX is a constant representing the maximum value that a size_t variable can hold.

The size_t type is implementation-defined. So there is no way to be sure the value of __int64 can be safely stored in a size_t type. I would suggest to use a static_assert:

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

This would create an error during the compile process if size_t is smaller than __int64.

See http://en.cppreference.com/w/cpp/types/size_t for more Information.

size_t is smaller than __int64 when you are compiling 32-bit applications.

If you know the files you are working with are "small" (< 2 GB) you can sidestep the issue a bit by casting and just aborting if the file is somehow very large:

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

If on the other hand the files can be large then you cannot assume that you will be able to read the whole thing into memory at once because the machine might not have enough memory or you might run out of address space (usually around 2 GB in a 32-bit Windows process). In this case you should use memory mapped files instead with a smaller view.

If you are compiling for 32-bit, size_t will only be 32 bit.

So it is recommended that you return size.LowPart instead and either ignore or error out if size.HighPart is not zero.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM