Does C4800 have any real world value? - c++

The C4800 warning in the microsoft c++ compiler as described here:
https://msdn.microsoft.com/en-us/library/b6801kcy.aspx
makes this code:
// C4800.cpp
// compile with: /W3
int main() {
int i = 0;
// try..
// bool i = 0;
bool j = i; // C4800
j++;
}
throw the C4800 warning: "'type' : forcing value to bool 'true' or 'false' (performance warning)"
Microsoft seems to think it's reasonably important, and has it as a Level3 warning, however Clang does apparently think it's not, as has zero complaints about it at -Weverything, its maximum warning level.
Is there any real world bug someone can come up with that C4800 would point out that would make it worth having it enabled?

Basically, this is just warning you that you've converting some other integer type to a bool, and this conversion isn't entirely free.
It's mostly present (at least as I see things) to warn that you're mixing bools with other integer types, which not only leads to a minor reduction in performance, but may also indicate some confusion in the code. Looking at the code in the question:
int i = 0;
// try..
// bool i = 0;
bool j = i; // C4800
j++;
...what we have looks like an incomplete conversion of some code that previously defined j to be of type int. The definition of j has been edited so it's now of type bool, but we're still assigning it a value from an int, and (worse) using a post-increment on it, both of which would make sense if j had type int, but really don't with j having type bool.
So, the question is whether what we really wanted to assign j with the result of a comparison: bool j = (i != 0); or maybe a more complete conversion that would turn i into a bool as well:
bool i = false;
// ...
bool j = i; // no warning
j = true; // cleaner way of producing the same result as the post-increment.

Related

when does static_cast compile to no CPU instructions

This question is motivated by the following (simplified) example.
Consider the following code:
void f(unsigned int x) { }
int main()
{
int z = 3;
if (z > 0)
for (unsigned int i = 0; i < z; ++i)
f(i);
return 0;
}
Compiling with all warnings enabled, the compiler issues a
warning: comparison of integer expressions of different signedness: ‘unsigned int’ and ‘int’
This is clearly not a real problem, since the code explicitly checks that the upper limit in the for loop is positive.
One way to eliminate the warning is to explicitly static_cast z:
int main()
{
int z = 3;
if (z > 0)
for (unsigned int i = 0; i < static_cast<unsigned int>(z); ++i)
f(i);
return 0;
}
At least in this simplified example, both codes compile to the same assembler, see it on godbolt.
Indeed, I would not expect that a cast from an int to unsigned int to result in any instructions, since both types are stored with the same number of bits.
Which leads to the question: when does static_cast result in no compiled instructions? Is this always the case for a static_cast between integers having the same size in bits?
When you don't have the cast there, the compiler adds a conversion for you anyway, so it makes sense that they both compile to identical machine code. But when it is implicit, it's likely that you weren't expecting or even aware of that (just search on StackOverflow for the number of cases people try to compare vector.size() with -1). So your compiler generates a warning to ask you to confirm that's really what you want.

How do usual arithmetic conversions work?

I was running this code in VS2019:
#include<iostream>
#include<string>
#include<typeinfo>
using namespace std;
int main() {
string mystring = "hello world";
for (int j = 0; j < 10; j++) {
if (mystring[j + 1] == 'w') {
cout<<"string contains w letter\n";
}
else {
continue;
}
return 0;
}
}
And I realized that when I run it on Debug mode on an x86 platform, everything is ok, but if I change the platform to x64, the following warning appears:
C26451 Arithmetic overflow: Using operator '+' on a 4-byte value and then casting the result to an 8-byte value. Cast the value to the wider type before calling operator '+' to avoid overflow (io.2).
It seems to be related to Usual arithmetic conversions, such that, if the operands are of different types, a conversion is applied to one of them before calculation. But if they are equal, that still happens?
If I print typeid(j).name() and typeid(1).name(), it prints int for both, so what is the reason for this warning? The warning is fixed if I change the if condition to (mystring[j + static_cast<__int64>(1)] == 'w'). The explanation, I think, should be that the number '1' is not considered of type int on x64, or it is but occupies different bits of memory than the int type on x64.
I would really like to clarify the issue, thanks.
The "C26451" warning is not a standard compiler warning. It's part of the C++ Code Guidelines Checker which is giving you 'recommendations'. For more on this feature, see Microsoft Docs.
In C++ Core Guidelines the specific recommendation the checker is using here is: ES.103: Don't overflow.
The reason this only happens in x64 is because size_t is 64-bits while int is 32-bits. In x86, both int and size_t are 32-bits.
The std::string operator[] takes a size_t. The cleanest simplest fix here is:
for (size_t j= 0; j <10; j++)
You could also address this by explicitly promoting the int to size_t before the addition takes place:
if (mystring[size_t(j) + 1] == 'w') {
You could also ignore the warning by adding:
#pragma warning(disable : 26451)
Or you could disable the C++ Core Guidelines Checker.
This is how the std::string [] operator defined. It takes std::size_t.
char& operator[] (size_t pos);
const char& operator[] (size_t pos) const;
That's where the casting is taking place: int -> size_t.
https://www.learncpp.com/cpp-tutorial/fixed-width-integers-and-size-t/
edit: See Chuck Walbourn answer
It seems to be related to Usual arithmetic conversions, such that, if the operands are of different types, a conversion is applied to one of them before calculation. But if they are equal, that still happens?
Your conclusion is incorrect.
VS thinks that j + 1 has the potential to overflow. It recommends that you perform the arithmetic operation, +, on a wider integral type to reduce the chances of overflow. Note that static_cast<std::size_t>(j) + 1 could, in theory, still overflow but VS does not care about that.
You don't get the warning in x86 mode since the size of std::size_t and int are same on that platform.

Getting a <' : signed/unsigned mismatch

The error is when I compare the value an int to the strnlen of a char with a set string length. what I am doing is searching the string for a certain # and tracking if it is found or not.
for (value = 0; value < strlen(stringValue) && !searchFound; value++)
{
if (stringValue[value] == searchDigit)
{
numberTimesSearchFound = numberTimesSearchFound + 1;
//ends loops once it is found
searchFound = true;
} //end of string value if else loop}
strlen returns size_t which is an unsigned type. You compare this to value which is a signed type. In C, values have to be of the same type to be compared, and the rule here is that value will be converted to size_t before the comparison.
In your code this is no problem (except for the case I mention later about INT_MAX), there are other situations where signed-unsigned comparisons do contain bugs. See here for example .
Because of this possibility, some compilers warn about all signed-unsigned comparisons, so that you can manually check your code for bugs.
To get rid of this warning I'd suggest changing value to have type size_t. If you go on to use value after the end of this code snippet then make sure that you weren't relying on any more signed-unsigned comparisons afterwards!
Ideally, scope it to the loop:
for ( size_t value = 0; ......
NB. If the string is longer than INT_MAX then your original code causes undefined behaviour when value is incremented beyond that; making value be size_t also fixes that problem.

Why is 3<–1 in code?

Take a look at the following code:
int start = 3;
vector<int> data;
data.push_back(0);
data.push_back(0);
for (int i=start; i<data.size()-start; i++)
printf("In...\n");
When running the above code, it will run printf("In...\n"); infinitely. Although based on the condition (3<-1) of the for loop, it should never do this. Weird, huh?
To avoid this, you have to compute the long condition equation first, like:
… …
int end = data.size()-start;
for (int i=start; i<end; i++)
printf("In...\n");
Why this happens?
size() returns an unsigned value (of type size_t) which causes the expression on the right of the comparison to be promoted to unsigned which then makes the comparison unsigned.
So there are no negative numbers where you think there are, just very large positive ones.
As other people have said, most compilers will warn you about this if you turn up the warning level, and c++ is not a language that can safely be used at a low warning level.

C++ for loop structure

Hope its not a lame question but I have to ask this :)
When I program in C++ and use for loops the parameters I give are i.e.
for(int i = 0; i< something; i++)
Which is correct way forward but..this gives me compile warnings such as this:
1>c:\main.cpp(185): warning C4018: '<' : signed/unsigned mismatch
Now going through books and reading online most for loops examples are of this structure.
I was always ignoring warnings as my programs always worked and did what they suppose to do, until I got interested with this warnings and did a small research....by copying this Waring and Google it to find that it is better if I use this structure to avoid the warning:
for(vector<int>::size_type i= 0; i < something; i++ )
Now my question here is why......if the initial structure works and is described and documented in many books and online resources.
Also what is the benefit or is there any significant difference in the techniques.....?
Why would I use this
for(vector<int>::size_type i= 0; i < something; i++ )
apart from getting rid of the warnings.....?
Don't ignore the warnings. They're trying to tell you something.
I suspect something is unsigned.
If you have
unsigned int something = 0;
something--; // Now something is a really large positive integer, not -1
If you ignore the warnings, and you don't have your compiler set to treat warnings as errors, then this will compile fine, but you won't get what you expect.
You're probably seeing that vector<int>::size_type is an unsigned int if the warning goes away.
You simply have a signed / unsigned mismatch between the type of i and the type of something in your statement:
for(int i = 0; i < something; i++)
So this has nothing to do with the for structure, but rather with the comparison.
bool b = i < something;
would give you the same warnings.
This can be the case if you use int i and compare it to a size_t variable somehow (which is what std::vector::size() gives you).
So, to fix it, simply change your for loop to using the same type for i and for something, such as:
for(size_t i = 0; i < something; i++)
if something is of type size_t.
Why would I use this
Because signed int and unsigned values like size_t have differing ranges, and you may not get your expected result if one contains a value that can not be represented by the other.
That said, if you think that code is too verbose, you don't have to use it.
Code like this:
for(vector<int>::size_type i= 0; i < myvector.size(); i++ )
{
int val = myvector[i];
Can also be written like this.
for ( int val : myvector )
Broadly, there are two kinds of integral types in C++: signed and unsigned. For each size of integer, there is a signed and an unsigned version. The difference is in their range: signed integers of n bits have a range from −2n − 1 to +2n − 1 − 1; unsigned integers, from 0 to 2n − 1.
When comparing signed integer types to unsigned, the signed value is converted to unsigned; negative values will wrap and be treated as large positive values. The upshot of this is that comparisons with < might not do what you expect, so many compilers will warn about such comparisons.
For example, 1u < -1 is true. u is a suffix that tells the compiler to treat the 1 as an unsigned int value.
With that, the meaning becomes clear: int is a signed type and vector<T>::size_type is an unsigned type. Since the result of vector<T>::size() is vector<T>::size_type, you want to use that or another unsigned type such as size_t to ensure that your comparisons have the behaviour you want.
Instead of using indices, you can also use iterators, which don’t have such conversion problems:
for (vector<int>::iterator i = v.begin(); i != v.end(); ++i)
cout << *i << '\n';
Which can be made more succinct with auto in C++11:
for (auto i = v.begin(); i != v.end(); ++i)
cout << *i << '\n';
If you’re just iterating over the whole container, use C++11 range-based for:
for (int i : v)
cout << i << '\n';
And if you want to modify the values, use a reference:
for (int& i : v)
++i;
something must be int, otherwise you get the warning. Or i must be unsigned int, depending on your needs.
Assuming a 32 bits integers, if signed any value above 0x7FFFFFFF (2,147,483,647 decimal) will be interpreted as negative, whereas it will be positive for an unsigned int.
So the compiler is issuing a warning telling you that comparison mail result in unexpected outcome.
32 bits integers range from −2,147,483,648 to 2,147,483,647.
32 bits unsigned integers range from 0 to 4,294,967,295