I recently activated gcc -Wsign-conversion option on a codebase and
received a lot of warnings. I started to fix them, usually expliciting casting. ...
After many years programming in C language, I'm always unsure if it
is safer to use signed int or unsigned int.
Of course there are situations where signed or unsigned is clearly
better. For example, if the values could assume negative values,
signed int is the only solution. If you are manipulating single bits
(&, |, ^, <<, >>), unsigned ints are your friends.
What about other situations? For example, what do you use for the "i"
loop variable?
I recently activated gcc -Wsign-conversion option on a codebase and
received a lot of warnings. I started to fix them, usually
expliciting casting. Is it the way or is it better to avoid the
warning from the beginning, choosing the right signed or unsigned
type?
On Mon, 20 Oct 2025 17:03:58 +0200
pozz <pozzugno@gmail.com> wrote:
After many years programming in C language, I'm always unsure if it
is safer to use signed int or unsigned int.
Of course there are situations where signed or unsigned is clearly
better. For example, if the values could assume negative values,
signed int is the only solution. If you are manipulating single bits
(&, |, ^, <<, >>), unsigned ints are your friends.
What about other situations? For example, what do you use for the "i"
loop variable?
I'd just point out that small negative numbers are FAR more common than numbers in range [2**31..2**32-1].
Now, make your own conclusion.
I recently activated gcc -Wsign-conversion option on a codebase and
received a lot of warnings. I started to fix them, usually
expliciting casting. Is it the way or is it better to avoid the
warning from the beginning, choosing the right signed or unsigned
type?
On Mon, 20 Oct 2025 17:03:58 +0200
pozz <pozzugno@gmail.com> wrote:
After many years programming in C language, I'm always unsure if it
is safer to use signed int or unsigned int.
Of course there are situations where signed or unsigned is clearly
better. For example, if the values could assume negative values,
signed int is the only solution. If you are manipulating single bits
(&, |, ^, <<, >>), unsigned ints are your friends.
What about other situations? For example, what do you use for the "i"
loop variable?
I'd just point out that small negative numbers are FAR more common than >numbers in range [2**31..2**32-1].
Now, make your own conclusion.
After many years programming in C language, I'm always unsure if it is
safer to use signed int or unsigned int.
Of course there are situations where signed or unsigned is clearly
better. For example, if the values could assume negative values, signed
int is the only solution. If you are manipulating single bits (&, |, ^,
<<, >>), unsigned ints are your friends.
What about other situations? For example, what do you use for the "i"
loop variable?
I recently activated gcc -Wsign-conversion option on a codebase and
received a lot of warnings. I started to fix them, usually expliciting casting. Is it the way or is it better to avoid the warning from the beginning, choosing the right signed or unsigned type?
On 20/10/2025 17:03, pozz wrote:
After many years programming in C language, I'm always unsure if it is
safer to use signed int or unsigned int.
Of course there are situations where signed or unsigned is clearly
better. For example, if the values could assume negative values, signed
int is the only solution. If you are manipulating single bits (&, |, ^,
<<, >>), unsigned ints are your friends.
What about other situations? For example, what do you use for the "i"
loop variable?
I recently activated gcc -Wsign-conversion option on a codebase and
received a lot of warnings. I started to fix them, usually expliciting
casting. Is it the way or is it better to avoid the warning from the
beginning, choosing the right signed or unsigned type?
Signed and unsigned types are equally safe. If you are sure you are
within the ranges you know will work for the types you use, your code is safe. If you are not sure, you are unsafe.
After many years programming in C language, I'm always unsure if it is
safer to use signed int or unsigned int.
Of course there are situations where signed or unsigned is clearly
better. For example, if the values could assume negative values,
signed int is the only solution. If you are manipulating single bits
(&, |, ^, <<, >>), unsigned ints are your friends.
What about other situations? For example, what do you use for the "i"
loop variable?
I recently activated gcc -Wsign-conversion option on a codebase and
received a lot of warnings. I started to fix them, usually expliciting casting. Is it the way or is it better to avoid the warning from the beginning, choosing the right signed or unsigned type?
On Mon, 20 Oct 2025 17:03:58 +0200
pozz <pozzugno@gmail.com> wrote:
After many years programming in C language, I'm always unsure if it
is safer to use signed int or unsigned int.
Of course there are situations where signed or unsigned is clearly
better. For example, if the values could assume negative values,
signed int is the only solution. If you are manipulating single bits
(&, |, ^, <<, >>), unsigned ints are your friends.
What about other situations? For example, what do you use for the "i"
loop variable?
I'd just point out that small negative numbers are FAR more common than numbers in range [2**31..2**32-1].
Now, make your own conclusion.
I recently activated gcc -Wsign-conversion option on a codebase and
received a lot of warnings. I started to fix them, usually
expliciting casting. Is it the way or is it better to avoid the
warning from the beginning, choosing the right signed or unsigned
type?
What about other situations? For example, what do you use for the "i"
loop variable?
I'd just point out that small negative numbers are FAR more common than numbers in range [2**31..2**32-1].
for (i = len(s);;)
{
...
} /*for*/
On Mon, 20 Oct 2025 19:43:37 +0300, Michael S wrote:
I'd just point out that small negative numbers are FAR more common than
numbers in range [2**31..2**32-1].
Perhaps you mean *large* negative numbers?
On Mon, 20 Oct 2025 19:43:37 +0300, Michael S wrote:
I'd just point out that small negative numbers are FAR more common than
numbers in range [2**31..2**32-1].
Perhaps you mean *large* negative numbers?
-1 is a larger number than -1000000.
The thing about unsigned types is that they have a discontinuity at
0, which is much easier to run into than signed int's discontinuties
at INT_MIN and INT_MAX. Subtraction in particular can easily yield mathematically incorrect results for unsigned types (unless your
problem domain actuall calls for modular arithmetic).
This is a nuanced topic where there isn't a one-type-fits-all answer,
but I gravitate toward signed; use of unsigned has to be justified in
some way.
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
[...]
The thing about unsigned types is that they have a discontinuity at
0, which is much easier to run into than signed int's discontinuties
at INT_MIN and INT_MAX. Subtraction in particular can easily yield
mathematically incorrect results for unsigned types (unless your
problem domain actuall calls for modular arithmetic).
One specific footgun enabled by unsigned types involves loops that count
down to zero. This :
for (int i = N; i >= 0; i --) {
// ...
}
is well behaved, but this :
for (size_t i = N; i >= 0; i --) {
// ...
}
Michael S <already5chosen@yahoo.com> writes:
[...]
One might also point out that negative loop indicies are rare, and
thus ones conclusion may be that generally speaking loop indexes should
be unsigned.
On 2025-10-21, Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
[...]
The thing about unsigned types is that they have a discontinuity at
0, which is much easier to run into than signed int's discontinuties
at INT_MIN and INT_MAX. Subtraction in particular can easily yield
mathematically incorrect results for unsigned types (unless your
problem domain actuall calls for modular arithmetic).
One specific footgun enabled by unsigned types involves loops that count
down to zero. This :
for (int i = N; i >= 0; i --) {
// ...
}
is well behaved, but this :
for (size_t i = N; i >= 0; i --) {
// ...
}
We just have to translate the signed "i >= 0" into unsigned.
One way is to just directly translate the two's complement semantics
is doing, pretending that the high bit of the value is a sign bit:
// if the two's-complement-like "sign bit" is zero ...
(SIZE_MAX & (SIZE_MAX >> 1) & i) == 0
In a downard counting loop, we can just stop when we wrap around
to the highest value, so we get to use most of the range:
for (size_t i = N; i != SIZE_MAX; --i) // or (size_t) -1
(Note: I like to write --i when it's downward, just as a style; it
comes from stacks: stack[i++] = push; pop = stack[--i].)
The troublesome case is when N needs to start at SIZE_MAX!
But that troublesome case exists when counting upward also,
signed or unsigned.
Signed:
// We must break the loop before undefined i++:
for (int i = 0; i <= INT_MAX; i++)
// Need a bottom-loop break on SIZE_MAX or else infinite loop:
for (size_t i = 0; i <= SIZE_MAX; i++)
This is where BartC will chime in with how languages benefit from
built-in idioms for ranged loops that solve these problems under the
hood. It's a valid argument.
After many years programming in C language, I'm always unsure if it is
safer to use signed int or unsigned int.
Of course there are situations where signed or unsigned is clearly
better. For example, if the values could assume negative values, signed
int is the only solution. If you are manipulating single bits (&, |, ^,
<<, >>), unsigned ints are your friends.
What about other situations? For example, what do you use for the "i"
loop variable?
I recently activated gcc -Wsign-conversion option on a codebase and
received a lot of warnings. I started to fix them, usually expliciting casting. Is it the way or is it better to avoid the warning from the beginning, choosing the right signed or unsigned type?
Michael S <already5chosen@yahoo.com> writes:
On Mon, 20 Oct 2025 17:03:58 +0200
pozz <pozzugno@gmail.com> wrote:
After many years programming in C language, I'm always unsure if it
is safer to use signed int or unsigned int.
Of course there are situations where signed or unsigned is clearly
better. For example, if the values could assume negative values,
signed int is the only solution. If you are manipulating single bits
(&, |, ^, <<, >>), unsigned ints are your friends.
What about other situations? For example, what do you use for the "i"
loop variable?
I'd just point out that small negative numbers are FAR more common than
numbers in range [2**31..2**32-1].
Now, make your own conclusion.
One might also point out that negative loop indicies are rare, and
thus ones conclusion may be that generally speaking loop indexes should
be unsigned.
On Mon, 20 Oct 2025 23:35:35 -0000 (UTC), I wrote:
for (i = len(s);;)
{
...
} /*for*/
Make that
for (i = len(s);;)
{
if (i == 0)
{
found = false;
break;
} /*if*/
--i;
if (matches(s[i]))
{
found = true;
break;
} /*if*/
} /*for*/
On 2025-10-20, David Brown <david.brown@hesbynett.no> wrote:
On 20/10/2025 17:03, pozz wrote:
After many years programming in C language, I'm always unsure if it is
safer to use signed int or unsigned int.
Of course there are situations where signed or unsigned is clearly
better. For example, if the values could assume negative values, signed
int is the only solution. If you are manipulating single bits (&, |, ^,
<<, >>), unsigned ints are your friends.
What about other situations? For example, what do you use for the "i"
loop variable?
I recently activated gcc -Wsign-conversion option on a codebase and
received a lot of warnings. I started to fix them, usually expliciting
casting. Is it the way or is it better to avoid the warning from the
beginning, choosing the right signed or unsigned type?
Signed and unsigned types are equally safe. If you are sure you are
within the ranges you know will work for the types you use, your code is
safe. If you are not sure, you are unsafe.
Safe generally means that the language somehow protects from harm, not
that you protect yourself.
Correct code operating on correct inputs, using unsafe constructs,
is still called unsafe code.
However using unsigned types due to them being safe is often poorly considered because if something goes wrong contrary to the programmer's intent, there likely will be undefined behavior somewhere.
E.g. an array underflow using an unsigned index will not produce
integer overlow undefined behavior, but the access will go out of
bounds, which is undefined behavior.
There are bugs which play out without any undefined behavior:
the program calculates something contrary to its requirements,
but stays within the confines of the defined language.
The odds that by using unsigned numbers you will get only that type of
bug are low, and even if so, it is not a big comfort.
Signed numbers behave more like mathematical integers, in cases
when there is no overflow.
If a, b and c are small, non-negative quantities, you might be tempted
to make them unsigned. But if you do so, then you can no longer make
this derivation of inequalities:
a + b > c
c > a - b
Under the unsigned types, we cannot add -b to both sides of the
inequality, preserving its truth value, even if all the operands
are tiny numbers that fit into a single decimal digit!
If b happens to be greater than a, we get a huge value on the right
side that is now larger than c, not smaller.
Gratuitous use of unsigned types impairs our ability to
algebra to simplify code, due to the "cliff" at zero.
This is a nuanced topic where there isn't a one-type-fits-all answer,
but I gravitate toward signed; use of unsigned has to be justified in
some way.
When sizes are being calculated and they come from functions or
operators that produce size_t, then that tends to dictate unsigned.
If the quantities are large and can possibly overflow, there are
situations in which unsigned makes that simpler.
For instance if a and b are unsigned such that a + b can semantically overflow (i.e. the result of the natural addition of a + b doesn't
fit into the type). It is simpler to detect: you can just do the
addition, and then test:
c = a + b;
when there is no overflow, it must be that (c >= a && c >= b)
so if either (c < a) or (c < b) is true, it overflowed.
This is significantly less verbose than a correct overflow test
for signed addition, which has to avoid doing the actual addition,
and has to be split into three cases: a and b have opposite
sign (always okay), a and b are both positive, and a and b are
both negative.
On 20/10/2025 22:09, Kaz Kylheku wrote:
On 2025-10-20, David Brown <david.brown@hesbynett.no> wrote:
On 20/10/2025 17:03, pozz wrote:
However using unsigned types due to them being safe is often poorly
considered because if something goes wrong contrary to the programmer's
intent, there likely will be undefined behavior somewhere.
Exactly. Unsigned types are not somehow "safer" than signed types, just because signed types have UB on overflow. Don't overflow your signed
types, then you have no UB. And if you overflow your unsigned types
without that being an intentional and understood part of your code, you
will at the very least get unexpected behaviour - a bug - and just like
UB, there are no limits to how bad that can get.
Am 21.10.2025 um 01:38 schrieb Lawrence D’Oliveiro:
for (i = len(s);;)
{
if (i == 0)
{
found = false;
break;
} /*if*/
--i;
if (matches(s[i]))
{
found = true;
break;
} /*if*/
} /*for*/
Your coding style doesn't match any common style ...
On 2025-10-21 06:42, David Brown wrote:
On 20/10/2025 22:09, Kaz Kylheku wrote:
On 2025-10-20, David Brown <david.brown@hesbynett.no> wrote:
On 20/10/2025 17:03, pozz wrote:
However using unsigned types due to them being safe is often poorly
considered because if something goes wrong contrary to the programmer's
intent, there likely will be undefined behavior somewhere.
Exactly. Unsigned types are not somehow "safer" than signed types, just
because signed types have UB on overflow. Don't overflow your signed
types, then you have no UB. And if you overflow your unsigned types
without that being an intentional and understood part of your code, you
will at the very least get unexpected behaviour - a bug - and just like
UB, there are no limits to how bad that can get.
No, there are limits on unexpected behavior: being unexpected, you might
not know what they are, but it is still the case that the behavior
starts out with having nothing more than an expression with an
unexpected but valid value. That's pretty bad, and your code might make
it worse, for example by promoting the unexpected value into undefined behavior. However, unless and until it actually does so, the behavior is somewhat more restricted than UB.
| Sysop: | DaiTengu |
|---|---|
| Location: | Appleton, WI |
| Users: | 1,075 |
| Nodes: | 10 (0 / 10) |
| Uptime: | 114:07:15 |
| Calls: | 13,799 |
| Calls today: | 1 |
| Files: | 186,990 |
| D/L today: |
5,531 files (1,594M bytes) |
| Messages: | 2,439,079 |