Build errors
C4267 and C4244 in an MFC x64 Build: Fix size_t and ptrdiff_t Conversion Warnings
An MFC project that has built without a single warning for years gets an x64 configuration. The first x64 build prints warnings like these, in lines you have not touched for a long time:
Report.cpp(25,13): warning C4267: 'initializing': conversion from 'size_t' to 'int', possible loss of data
Report.cpp(28,14): warning C4244: 'initializing': conversion from 'INT_PTR' to 'int', possible loss of data
Report.cpp(33,14): warning C4244: 'initializing': conversion from '__int64' to 'int', possible loss of data
Report.cpp(36,20): warning C4267: 'initializing': conversion from 'size_t' to 'DWORD', possible loss of data
The same code builds with no warnings as Win32. In x64, each of these lines puts a value that is 8 bytes wide into a 4-byte variable.
Why they appear only in x64
On x64, int and DWORD stay 4 bytes, while size_t, ptrdiff_t (the type of a pointer difference), and INT_PTR become 8 bytes. In Win32 all of them are 4 bytes, so nothing is narrowed and the compiler has nothing to report. In x64, an 8-byte value that is stored in a 4-byte variable can lose its upper half, and the compiler warns about that.
Which warning you get depends on the source type:
| Source type | Typical expression | Warning |
|---|---|---|
size_t |
std::vector::size(), _tcslen() |
C4267 |
INT_PTR |
CStringArray::GetSize(), CArray::GetSize() |
C4244 |
ptrdiff_t |
pEnd - pStart |
C4244 |
C4267 is reported only for size_t. A value of type INT_PTR or __int64 gives C4244 instead, so a C4244 on a GetSize() line is the same kind of problem as the C4267 on a size() line. Both are level 3 warnings (C4267, C4244).
Give the value its own type
The four lines behind the warnings above:
std::vector<CString> lines;
CStringArray errors;
// ... fill both ...
int nLines = lines.size(); // C4267
int nErrors = errors.GetSize(); // C4244
LPCTSTR pszLine = lines[0];
LPCTSTR pszLevel = _tcsstr(pszLine, _T("INFO"));
int nColumn = pszLevel - pszLine; // C4244
DWORD cbFirstLine = (_tcslen(pszLine) + 1) * sizeof(TCHAR); // C4267
For a count, an index, or a distance, the fix is to declare the variable with the type the expression already has:
size_t nLines = lines.size();
INT_PTR nErrors = errors.GetSize();
ptrdiff_t nColumn = pszLevel - pszLine;
Loop counters follow the same rule. for (size_t i = 0; i < lines.size(); ++i) for a std::vector, and for (INT_PTR i = 0; i < errors.GetSize(); ++i) for an MFC collection, so that the counter and the limit have the same type.
The format strings that print these variables have to change too. %d expects an int, so use %zu for size_t and %td for ptrdiff_t. For INT_PTR, cast to long long and use %lld:
sReport.Format(_T("Lines: %zu, errors: %lld, column: %td"),
nLines, static_cast<long long>(nErrors), nColumn);
When an API requires a DWORD or an int
The fourth line is different. The value is a byte count that goes to an API with a DWORD parameter, and you cannot change the parameter. The value has to be narrowed, so check that it fits before you do, and handle the case where it does not:
static bool TrySizeToDword(size_t value, DWORD& result)
{
if (value > MAXDWORD)
return false;
result = static_cast<DWORD>(value);
return true;
}
DWORD cbFirstLine = 0;
if (!TrySizeToDword((_tcslen(pszLine) + 1) * sizeof(TCHAR), cbFirstLine))
return _T("The first line is too long to pass as a DWORD.");
For an int parameter, compare against INT_MAX in the same way. A bare static_cast<DWORD>(...) also removes the warning, but a value above 4 GB is then cut to its lower 32 bits and passed on without any message. Do not use ASSERT for this check: it is gone from a Release build, and the Release build is where large data shows up. When the value can exceed the limit, return an error or show a message.
Do not silence the warning for the whole project
#pragma warning(disable: 4267) or a lower warning level stops the report for every line, including ones that really lose data later. Fix each line by type first. If one line has to stay narrow and you have checked the range, #pragma warning(suppress: 4267) on the line before it affects only that line.