Paavo Helde <
ees...@osa.pri.ee> writes:
> 10.05.2022 17:18 Tim Rentsch kirjutas:
>
>> Paavo Helde <
ees...@osa.pri.ee> writes:
>>
>>> So, in order to not lose these useful warnings in the avalanche of
>>> non-useful ones, one should use matching types in such simple code,
>>> it does not cost anything.
>>
>> That seems plainly false. Any change that incurs some programming
>> effort and disturbs what code is written obviously costs something.
>> A claim could be made that the cost is worth the benefit, but
>> clearly the cost is greater than zero.
>
> One can argue that it's using of mismatching types in a comparison
> which means non-zero programming effort.
That contrast is different than what I was talking about, but let
me respond to your comments here.
> If the types used in a comparison are of different types and
> especially of different signedness, the programmer needs to know the
> promotion rules which might depend on the operand sizes, which in
> turn might depend on the platform/implementation.
This is an overstatement. Comparing two signed types, or two
unsigned types, is always safe, regardless of the widths of the
types involved. It's only when one type is signed and the other
type is unsigned, and the unsigned type is "larger" than int, that
there is even just possibly a problem; for example, comparing a
signed type against an unsigned char, or unsigned short, or
unsigned bit-field whose width is less than the width of unsigned
int, will never give a result different than what the unconverted
values would indicate.
> The programmer needs to think through what happens when the signed
> operand becomes negative. Is it possible and what happens then?
These questions do need to be considered but that doesn't mean
they are difficult necessarily. If we see a code fragment like
int a[] = { ... };
for( Index i = 0; i < sizeof a / sizeof a[0]; i++ ){ ... }
it's immediately obvious that the value in 'i' is non-negative, no
matter whether the type Index is signed or unsigned. Almost no
mental effort is needed.
> Even more to the point, when a maintenance programmer comes around
> and sees the code, they have to think it through again, every time.
> Why are incompatible types used in comparison? Was it an oversight
> and is it possible it might cause problems?
This observation doesn't change the argument. Any factors needing
consideration for the original developer still need consideration
for maintenance work. The same kind of response applies.
> I will see that as a significant programming effort. YMMV.
No one is saying that there is no programming effort involved.
Whether the effort is _significant_ in each case is a different
question. Furthermore, the comments you make are looking at only
one side of the equation. It isn't always practicable to choose
the two types involved so that they must be the same; for example,
one type could be dictated by the type of a third party function
interface, and the other type could be dictated by language
semantics (subtracting one pointer from another gives a signed
type, but sizeof gives an unsigned type). Even when it is feasible
to give the two types the same signedness, doing that may entail
other tradeoffs that have their own costs. For example, if a
parameter 'n' has an unsigned type, we know that it has a
non-negative value. Changing the type of 'n' to be a signed type
would mean either more code in the function to accommodate a
possible negative value of 'n', or more mental effort during
development or maintenance to consider whether such code is needed,
etc (and quite possibly both). A proposed rule that comparands
should /always/ have the same signedness tacitly assumes that this
factor always outweighs all other factors. That assumption simply
isn't right: sometimes it does, but sometimes it doesn't.