Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.c > #91442 > unrolled thread
| Started by | Tim Rentsch <txr@alumni.caltech.edu> |
|---|---|
| First post | 2016-07-31 16:36 -0700 |
| Last post | 2016-08-01 07:27 -0700 |
| Articles | 3 — 2 participants |
Back to article view | Back to comp.lang.c
This discussion starts older than the indexed window; earlier articles aren't shown. The article labeled Started by
below is the oldest one visible, not the original post.
Re: unsigned int Tim Rentsch <txr@alumni.caltech.edu> - 2016-07-31 16:36 -0700
Re: unsigned int Ike Naar <ike@iceland.freeshell.org> - 2016-08-01 06:01 +0000
Re: unsigned int Tim Rentsch <txr@alumni.caltech.edu> - 2016-08-01 07:27 -0700
| From | Tim Rentsch <txr@alumni.caltech.edu> |
|---|---|
| Date | 2016-07-31 16:36 -0700 |
| Subject | Re: unsigned int |
| Message-ID | <kfnshupl6se.fsf@x-alumni2.alumni.caltech.edu> |
ram@zedat.fu-berlin.de (Stefan Ram) writes:
> I am now trying to write a new lesson for my German-language
> C course. This time about >>unsigned<< (say, >>unsigned int<<,
> for a start).
>
> So, what are the crucial facts the readers need to know?
You've gotten some good answers in other responses. Let me
see if I can add to those without duplicating too much. I
haven't tried to do any filtering, please select those you
consider important. (This ended up being longer than I was
expecting, kind of a compendium of comments on unsigned
types.)
Generally I think comments about unsigned types should be divided
into factual statements of behavior and advice about various
usage patterns. I may not make the distinction explicitly in
some of these comments but I recommend doing so for the class.
Normally unsigned int has a range at least as big as the entire
range of int (meaning unsigned int has as many distinct values as
int does). However, the Standard allows unsigned int to have
only as many distinct values as the non-negative int values,
although these days it's unlikely to run across a system where
that is true.
The rules of integer literals are somewhat baroque, and have
changed between C90 and C99/C11. I recommend using a U or u
suffix for any integer literal meant to be an unsigned type, even
those that would be unsigned without it.
Related to that, for literals the suffix letters can be in either
order, but for printf() specifications it has to be "lu" or "llu",
not "ul" or "ull".
Different printf() specifications expect different signedness of
the argument expressions: %d and %i expect a signed type, and %u,
%o, %x, and %X expect an unsigned type. It's okay to pass a
signed type where an unsigned type is expected, or vice versa,
if the value is one that is common to the two ranges. (Note:
other people have different opinions on this. I believe my
comment reflects the view of WG14 on the matter, but others
may believe otherwise.) Also it's usually okay to pass a value
of the wrong signedness in the sense that the results may be
surprising but they are extremely unlikely to be catastrophic.
The most obvious difference between signed types and unsigned
types (not counting that only signed types can be negative) is
that operating on and converting to unsigned types is always
defined behavior, whereas operating on or converting to signed
types is sometimes undefined or implementation-defined behavior.
The rules for type promotion are not symmetric, and sometimes
give surprising results. It may be worth explaining the
difference between the so-called "value preserving" rule
and the "unsigned preserving" rule, to give an appreciation
for why the language is as it is today. (And anyone using
a pre-ANSI compiler, not that I think it likely, might
appreciate being warned about such a possibility.)
A good way to convert from int-or-smaller to unsigned int is to
add 0u. Assigning a value of -1 to an unsigned type gives the
maximum value in that type. Note that -1 always works, but -1u
works only if the destination type is no larger than unsigned
int.
Generically there are two common danger modes in using unsigned
types: falling through the floor at 0, and inter-operating with
a (not larger) signed type that has a negative value.
To say that in a positive sense, operations involving an unsigned
type are always okay if both operands and the arithmetic result
(ie, before the modulo operation) are all in the non-negative
range of the types involved. If they aren't, the results may
be surprising.
Situations where unsigned types work well are counting that never
decreases, or array indexing that is always relative to the
absolute base of the array. It can be good to use an unsigned
index in a for() loop whose final value is calculated using
sizeof, since size_t is an unsigned type. Danger areas are
counting down or using subtraction. Usually it is better to
recast subtraction in terms of addition, eg
while( i+1 < limit ) ... // do not use 'i < limit-1'
to protect against the case where 'limit' may be zero. Similarly
it is good to know the pattern
for( i = limit; i-- > 0; ) ...
similarly to how pointer manipulation is done. The same sorts of
concerns that arise for pointers into an array come up with
unsigned index values, but with one key difference: pointer
subtraction produces a signed result, not an unsigned result.
Situations where unsigned types are usually better than signed
types are those involving bitwise operators: <<, >>, &, ^, |.
To avoid problems with type promotion, the unsigned type(s) used
should be unsigned int or larger.
One further comment, perhaps not important but related. In the
C preprocessor, all arithmetic is done as if all the integer
types have the widest possible width. So what is true in
regular code might be false when used as a preprocessor
expression. For example,
if( 999999999999999 > -1u )
may behave differently than
#if 999999999999999 > -1u
because the ranges of types may be different in the two
contexts.
[toc] | [next] | [standalone]
| From | Ike Naar <ike@iceland.freeshell.org> |
|---|---|
| Date | 2016-08-01 06:01 +0000 |
| Message-ID | <slrn3vfsnptpcs.ssv.ike@iceland.freeshell.org> |
| In reply to | #91442 |
On 2016-07-31, Tim Rentsch <txr@alumni.caltech.edu> wrote: > The most obvious difference between signed types and unsigned > types (not counting that only signed types can be negative) is > that operating on and converting to unsigned types is always > defined behavior, whereas operating on or converting to signed > types is sometimes undefined or implementation-defined behavior. Nit: a shift operation on unsigned types has undefined behavour if the value of the right operand is greater than or equal to the width of the promoted left operand (6.5.7p3).
[toc] | [prev] | [next] | [standalone]
| From | Tim Rentsch <txr@alumni.caltech.edu> |
|---|---|
| Date | 2016-08-01 07:27 -0700 |
| Message-ID | <kfn37molg2w.fsf@x-alumni2.alumni.caltech.edu> |
| In reply to | #91455 |
Ike Naar <ike@iceland.freeshell.org> writes: > On 2016-07-31, Tim Rentsch <txr@alumni.caltech.edu> wrote: >> The most obvious difference between signed types and unsigned >> types (not counting that only signed types can be negative) is >> that operating on and converting to unsigned types is always >> defined behavior, whereas operating on or converting to signed >> types is sometimes undefined or implementation-defined behavior. > > Nit: a shift operation on unsigned types has undefined behavour if > the value of the right operand is greater than or equal to the > width of the promoted left operand (6.5.7p3). Yes, and that is worth pointing out. I subconsciously put it in a different category, because the abstract operation (or maybe I should say "abstract operation") isn't defined for such shift values. But it is undefined behavior, as are divide by zero and remainder by zero. Is that a complete list? Off the top of my head I can't think of any others. Converting from floating point to an unsigned type can (IIANM) give undefined behavior, but I don't think of that as an operation on an unsigned type.
[toc] | [prev] | [standalone]
Back to top | Article view | comp.lang.c
csiph-web