return -1 using size_t???

R

Robert Hayes

I have read that size_t can hold an unsigned int. I have been playing
with using size_t as a return value and I am not sure I should. I
wrote the following code to demonstrate the issue:

#include <stdio.h>
#include <stdlib.h>

static size_t test(size_t test2) {
size_t a;
a = test2;
return a;
}

int main(int argc, char **argv) {
size_t b,c;
b = -1;
c = test(b);
if (c == -1)
printf("here\n");
else
printf("there\n");

printf("c = %zu\n",c);
return 0;
}

When b is -1 I see:
here
c = 18446744073709551615

When b is 3 I see:
there
c = 3

While I realize using an unsigned int to store a negative number is
not proper, why does it sort of work? By sort of work I mean the
comparison works but the printf doesn't. However if I change the
printf("c = %zu\n",c); to printf("c = %d\n",c) then when b is -1 I see
here and c = -1.

So should I use a size_t to hold -1? If not why does the comparison
work? I am using Ubuntu Lucid.

Thanks,
 
J

Jeffrey R

I have read that size_t can hold an unsigned int. I have been playing
with using size_t as a return value and I am not sure I should. I wrote
the following code to demonstrate the issue:

#include <stdio.h>
#include <stdlib.h>

static size_t test(size_t test2) {
size_t a;
a = test2;
return a;
}

int main(int argc, char **argv) {
size_t b,c;
b = -1;
c = test(b);
if (c == -1)
printf("here\n");
else
printf("there\n");

printf("c = %zu\n",c);
return 0;
}

When b is -1 I see:
here
c = 18446744073709551615

When b is 3 I see:
there
c = 3

While I realize using an unsigned int to store a negative number is not
proper, why does it sort of work? By sort of work I mean the comparison
works but the printf doesn't. However if I change the printf("c =
%zu\n",c); to printf("c = %d\n",c) then when b is -1 I see here and c =
-1.

So should I use a size_t to hold -1? If not why does the comparison
work? I am using Ubuntu Lucid.

Thanks,

Assigning -1 into a size_t is an Undefined Behavior. Anything can happen,
be glad it didn't trash your hard disk!
 
S

Shao Miller

I have read that size_t can hold an unsigned int. I have been playing
with using size_t as a return value and I am not sure I should. I
wrote the following code to demonstrate the issue:

#include<stdio.h>
#include<stdlib.h>

static size_t test(size_t test2) {
size_t a;
a = test2;
return a;
}

int main(int argc, char **argv) {
size_t b,c;
b = -1;
c = test(b);
if (c == -1)
printf("here\n");
else
printf("there\n");

printf("c = %zu\n",c);
return 0;
}

When b is -1 I see:
here
c = 18446744073709551615

When b is 3 I see:
there
c = 3

While I realize using an unsigned int to store a negative number is
not proper, why does it sort of work? By sort of work I mean the
comparison works but the printf doesn't. However if I change the
printf("c = %zu\n",c); to printf("c = %d\n",c) then when b is -1 I see
here and c = -1.

So should I use a size_t to hold -1? If not why does the comparison
work? I am using Ubuntu Lucid.

When the value '-1' is converted to a 'size_t' on your platform, it'll
yield the value '18446744073709551615', which is the maximum value
representable with 64 value bits.

In your comparison before "here," the '-1' is promoted to a 'size_t'
value for the comparison. So your comparison is actually:

if (c == 18446744073709551615)
printf("here\n");
 
E

Eric Sosman

I have read that size_t can hold an unsigned int. I have been playing
with using size_t as a return value and I am not sure I should. I
wrote the following code to demonstrate the issue:

#include<stdio.h>
#include<stdlib.h>

static size_t test(size_t test2) {
size_t a;
a = test2;
return a;
}

int main(int argc, char **argv) {
size_t b,c;
b = -1;

As you note, `b' is an unsigned integer. Arithmetic on unsigned
integer is modular ("clock arithmetic"), so adding 1 to the type's
maximum value wraps around to zero. Also, subtracting 1 from the
types minimum value (zero) wraps around to the maximum value. And
that's what happens here: Converting -1 to an unsigned type produces
the type's maximum value. In your case, it appears that size_t is
a 64-bit unsigned type, so -1 becomes 0xFFFFFFFFFFFFFFFF, the very
large value you've printed in decimal form.
c = test(b);
if (c == -1)

This compares the size_t value `c' to the (signed) int value -1.
When you compare dissimilar types, C converts the values to a common
type and compares the converted values. There's a bunch of rules about
what converts to what in which circumstances; in this case, the int
value is converted to size_t and compared with `c'. Converting -1
to size_t works here just as it did above, and yields that very large
number -- which is, of course, equal to the very large number in `c'.
printf("here\n");
else
printf("there\n");

printf("c = %zu\n",c);
return 0;
}

When b is -1 I see:
here
c = 18446744073709551615

When b is 3 I see:
there
c = 3

While I realize using an unsigned int to store a negative number is
not proper, why does it sort of work? By sort of work I mean the
comparison works but the printf doesn't.

Once you take the conversions into account, I think you'll agree
that both constructs "work."
However if I change the
printf("c = %zu\n",c); to printf("c = %d\n",c) then when b is -1 I see
here and c = -1.

Happenstance. The "%d" conversion specifier must match a (signed)
int argument, but you're feeding it a size_t. Since the argument is
not of the expected type, you get the dreaded Undefined Behavior: C no
longer guarantees anything about what your program will do. (By the
way, gcc will warn you about mismatches of this kind if you crank up
its warning levels a bit: "-Wall -W -std=c99" is a reasonable start,
and you can get it to be even more picky if you wish.)

What (probably) happened is that "%d" looked at (probably) one
half or the other of the 64-bit size_t value, saw that the half had
all bits set, and interpreted that as the int value minus one. What
happened to the other half remains shrouded in mystery: Maybe it
just sat in a CPU register and was ignored, maybe it hung around in
a stack slot ready to confuse the next conversion, maybe it donated
your bank balance to the Greek bailout fund.
So should I use a size_t to hold -1? If not why does the comparison
work? I am using Ubuntu Lucid.

size_t cannot hold -1, nor any other negative value. It can,
however, hold the "wrapped around" value that results from converting
-1 to size_t; this value is large and positive. You can, if you
like, impute some special meaning to that large value, just as you
might treat zero as special, or -42 (in a signed type). Just don't
expect `(size_t)-1' to be negative, because it ain't.

size_t is also unable to hold extremely large positive values:
There is a maximum possible value for a size_t. If you try to go
above the maximum, the too-large number wraps around and becomes a
small non-negative value.
 
E

Eric Sosman

Assigning -1 into a size_t is an Undefined Behavior. Anything can happen,
be glad it didn't trash your hard disk!

Nonsense. The behavior is implementation-defined, not undefined.
(The implementation-defined aspect is the maximum value of the size_t
type; it can be as small as 65535 or as large as, as, well, as large
as Very Large Indeed.)

The conversion will not trash a disk, nor cause butterscotch
pudding to ooze from your keyboard, nor make demons fly out of your
nose. It will yield the largest value a size_t can hold, period.
 
R

Robert Hayes

     As you note, `b' is an unsigned integer.  Arithmetic on unsigned
integer is modular ("clock arithmetic"), so adding 1 to the type's
maximum value wraps around to zero.  Also, subtracting 1 from the
types minimum value (zero) wraps around to the maximum value.  And
that's what happens here: Converting -1 to an unsigned type produces
the type's maximum value.  In your case, it appears that size_t is
a 64-bit unsigned type, so -1 becomes 0xFFFFFFFFFFFFFFFF, the very
large value you've printed in decimal form.


     This compares the size_t value `c' to the (signed) int value -1.
When you compare dissimilar types, C converts the values to a common
type and compares the converted values.  There's a bunch of rules about
what converts to what in which circumstances; in this case, the int
value is converted to size_t and compared with `c'.  Converting -1
to size_t works here just as it did above, and yields that very large
number -- which is, of course, equal to the very large number in `c'.





     Once you take the conversions into account, I think you'll agree
that both constructs "work."


     Happenstance.  The "%d" conversion specifier must match a (signed)
int argument, but you're feeding it a size_t.  Since the argument is
not of the expected type, you get the dreaded Undefined Behavior: C no
longer guarantees anything about what your program will do.  (By the
way, gcc will warn you about mismatches of this kind if you crank up
its warning levels a bit: "-Wall -W -std=c99" is a reasonable start,
and you can get it to be even more picky if you wish.)

     What (probably) happened is that "%d" looked at (probably) one
half or the other of the 64-bit size_t value, saw that the half had
all bits set, and interpreted that as the int value minus one.  What
happened to the other half remains shrouded in mystery: Maybe it
just sat in a CPU register and was ignored, maybe it hung around in
a stack slot ready to confuse the next conversion, maybe it donated
your bank balance to the Greek bailout fund.


     size_t cannot hold -1, nor any other negative value.  It can,
however, hold the "wrapped around" value that results from converting
-1 to size_t; this value is large and positive.  You can, if you
like, impute some special meaning to that large value, just as you
might treat zero as special, or -42 (in a signed type).  Just don't
expect `(size_t)-1' to be negative, because it ain't.

     size_t is also unable to hold extremely large positive values:
There is a maximum possible value for a size_t.  If you try to go
above the maximum, the too-large number wraps around and becomes a
small non-negative value.


Thank you for the clear explanation. I will not use a size_t to hold
a negative number.
 
M

Malcolm McLean

Thank you for the clear explanation.  I will not use a size_t to hold
a negative number.- Hide quoted text -
There's an example of a size_t holding a negative number on the thread
about stripping right spaces.
 
E

Eric Sosman

Robert Hayes said:
While I realize using an unsigned int to store a negative number is
not proper, why does it sort of work? By sort of work I mean the

On a twos complement CPU, [...]

On no C implementation anywhere does size_t use two's complement.
Nor ones' complement, nor signed magnitude.
Addition and subtraction will yield the same bit patterns with either
interpretation. If the most significant bits are equal, comparison will be the
same.

C's comparison and equality operators use values, not bit patterns.
Here's (slightly non-portable) proof:

#include <string.h>
#include <math.h>
#include <assert.h>
...
float f = NAN;
float g = NAN;
assert(memcmp(&f, &g, sizeof f) == 0); // identical bits
assert(f != g); // unequal values

Here's another proof (I think this one's portable):

struct {
unsigned int p : 1;
unsigned int q : 15;
} s;
s.p = 1;
s.q = 1;
assert(s.p == s.q); // different bits, equal values
 
E

Eric Sosman

Eric Sosman said:
In
article<8c1eef8c-2ba4-441e-aa86-36486b56a261@n12g2000yqb.googlegroups.com>,

While I realize using an unsigned int to store a negative number is
not proper, why does it sort of work? By sort of work I mean the

On a twos complement CPU, [...]

And you snipped that all references were to integers

The question was about *unsigned* integers. *Unsigned* integers
in C never use complement represention.
The bit pattern of s.p would be 0...1 and s.q would be 0...000000000000001
widenned to the number of bits in an unsigned int.

The bit pattern of s.p is `1', period. The bit pattern of
s.q is `000000000000001', period. These bit patterns necessarily
disagree, since they're not even the same size.

I've made two points about your post: First, talking about
representation schemes for negative numbers is silly when the
type in question is unsigned. Second, suggesting that comparison
relies on bit patterns without regard to types is not merely
silly, but wrong. (I could also have taken issue with the
misstatement about "most significant bits," but enough's enough.)
 
J

Jeffrey R

Thank you for the clear explanation. I will not use a size_t to hold a
negative number.

This explanation may seem clear but it is also WRONG!

It is assuming that the CPU is 2-s compliment.

In many common cases eg on x86, behavior will be as Eric Sossman
describes. However, this is not guaranteed and not portable. On other
platforms it could trigger an overflow signal from the ALU or lead to a
Trap Representation. In fact, Undefined Behavior means that anything
could happen! Including (in theory) reformatting your hard disk.

A size_t is an UN-SIGNED type, trying to store a negative number in it is
a programmer error generating an undefined behavior.
 
B

Barry Schwarz

I have read that size_t can hold an unsigned int. I have been playing

size_t is defined as an unsigned integer type but it need not be
unsigned int. I expect on most systems it is unsigned long as
recommended in 7.17-4 of n1256.
 
J

Jeroen Mostert

A size_t is an UN-SIGNED type, trying to store a negative number in it is
a programmer error generating an undefined behavior.

(size_t) -1 is well-defined on any C implementation, per section 6.3.1.3p2
of the standard. It's equal to the maximum value a size_t can hold. This
does not depend on the underlying representation.

There are operations on signed values that invoke undefined behavior (in
fact, integer overflow is the poster child for undefined behavior), but
converting signed values to unsigned values isn't one of them.
 
W

Willem

Devil with the China Blue Dress wrote:
) No, the question was about casting an signed number (-1) to an unsigned type.
) That this can be done and is closed under addition is a property of twos
) complement integers.

No, that this can be done is a result of the wording of the C standard.
Case in point: it also works on ones complement machines.
Twos complement is just a red herring in this case.


SaSW, Willem
--
Disclaimer: I am in no way responsible for any of the statements
made in the above text. For all I know I might be
drugged or something..
No I'm not paranoid. You all think I'm paranoid, don't you !
#EOT
 
B

Ben Bacarisse

Devil with the China Blue Dress said:
There is nothing quite as wonderful as a ones complement machine where
-0==0 and -0<0.

A C implementation would, I think, have to hide that from the
programmer. In C, the value of a negative zero is zero and will
therefore not compare less than zero.
 
J

Jens Gustedt

Am 02/11/2012 11:31 PM, schrieb Devil with the China Blue Dress:
No, the question was about casting an signed number (-1) to an unsigned type.
That this can be done and is closed under addition is a property of twos
complement integers.

No, the representation of the -1 has nothing do with the result of
converting it into any unsigned integer type. As has been already been
repeated several times upthread, C strictly defines what has to be done
when converting a negative *value* to an unsigned integer type. And in
particular it uses the value and not the representation.

For all conforming C implementations the result of converting -1 to
size_t is SIZE_MAX.

Jens
 
B

Ben Bacarisse

Shao Miller said:
On 2/11/2012 14:21, Robert Hayes wrote:
In your comparison before "here," the '-1' is promoted to a 'size_t'
value for the comparison.

Just a niggle: it's not technically a promotion. The integer promotions
preserve value including sign. What's happening here is the "usual
arithmetic conversions" where a signed int is converted to an unsigned
integer type.

<snip>
 
B

Ben Bacarisse

Robert Hayes said:
On Feb 11, 2:07 pm, Eric Sosman <[email protected]> wrote:
Thank you for the clear explanation. I will not use a size_t to hold
a negative number.

Don't worry you can't! But I think I get what you mean. However, I
don't think that's the take away message here. It's not uncommon to use
(size_t)-1 as a special signifier. Sometimes size_t is the natural
return type and yet you want to use an in-band error indicator.
(size_t)-1 is good for the job. In fact, C defines several standard
functions that do this. A couple of them even signal two different
errors using (size_t)-1 and (size_t)-2.
 
K

Keith Thompson

Malcolm McLean said:
There's an example of a size_t holding a negative number on the thread
about stripping right spaces.

No, there isn't.

I don't recall the details from that thread. The point is that size_t
simply cannot hold a negative number.

You can convert a negative number (of a signed type) to size_t, and
store the result of the conversion in a size_t object. You *probably*
can then convert the stored value back to the signed type, but the
result is implementation-defined.
 
K

Keith Thompson

Barry Schwarz said:
size_t is defined as an unsigned integer type but it need not be
unsigned int. I expect on most systems it is unsigned long as
recommended in 7.17-4 of n1256.

That paragraph doesn't recommend using unsigned long; it recommends
*not* using a type with conversion rank greater than that of
unsigned long unless it's necessary. An implementation defining
size_t as either unsigned int or unsigned long would satisfy the
recommendation.
 
M

Malcolm McLean

No, there isn't.

I don't recall the details from that thread.  The point is that size_t
simply cannot hold a negative number.

Here it is
[ from Ben ]
size_t i = strlen(s);
while (i-- > 0 && isspace(s))
/* do nothing */;
s[i + 1] = '\0';

If the string is all spaces, or empty, I'd say that i goes to -1.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,482
Members
44,900
Latest member
Nell636132

Latest Threads

Top