size_t compare with zero

L

labros07

Hello to all !
----------------

#include <stdio.h>

int main()
{
/*size_t i;*/
int i;

for(i=10 ;i>=0; i--){

printf("%d\n", i);


}

return 0;
}


In the above code if I make 'i' to be of type size_t, when i = -1 the
comparison i>=0 is true and loop executes
even when i<0. Does anyone knows why ?

I use:
gcc (GCC) 4.2.4 (Ubuntu 4.2.4-1ubuntu3)

Thanks,
 
L

labros07

Hello to all !
----------------

#include <stdio.h>

int main()
{
/*size_t i;*/
int i;

for(i=10 ;i>=0; i--){

printf("%d\n", i);

}

return 0;

}

In the above code if I make 'i' to be of type size_t, when i = -1 the
comparison i>=0 is true and loop executes
even when i<0. Does anyone knows why ?

I use:
gcc (GCC) 4.2.4 (Ubuntu 4.2.4-1ubuntu3)

Thanks,

I think I found it. Bacause size_t is unsigned int, it never happens
i=-1. The lowest value is i=0. When i-- overflow happens and i takes a
big unsigned int value. SO we have a infinite loop. This is clear when
we print i with modifier %u printf
 
B

Boon

labros07 said:
if I make 'i' to be of type size_t
when i = -1 the comparison i >= 0 is true

Yes. "size_t is the unsigned integral type of the result of
the sizeof operator." Thus ((size_t)-1) is greater than 0.

In fact, it is the largest number that can be represented in size_t.

Regards.
 
J

James Kuyper

Hello to all !
----------------

#include <stdio.h>

int main()
{
/*size_t i;*/
int i;

for(i=10 ;i>=0; i--){

printf("%d\n", i);


}

return 0;
}


In the above code if I make 'i' to be of type size_t, when i = -1 the
comparison i>=0 is true and loop executes
even when i<0. Does anyone knows why ?

The type size_t is an unsigned type. Therefore, an object of type size_t
can never store a negative value. If you think you have stored a
negative value in it, you've misunderstood how conversion from a signed
type to an unsigned type is performed.

Just before the loop, after you think you've set 'i' to -1, print out
the value of i using the following statement if you're using C90:

printf("%lu\n", (unsigned long)i);

Or this one if you're using C99:

printf("%zu\n", i);

If you are surprised by the value that you see printed out, re-read the
portion of your text book describing conversion from signed types to
unsigned types.
 
J

Jens Thoms Toerring

#include <stdio.h>
int main()
{
/*size_t i;*/
int i;
for(i=10 ;i>=0; i--){
printf("%d\n", i);
}
return 0;
}
In the above code if I make 'i' to be of type size_t, when i = -1 the
comparison i>=0 is true and loop executes
even when i<0. Does anyone knows why ?

size_t is an unsigned integer type (of not further specified
size, so it could be an 'unsigned int', an 'unsigned long' or
also something else - take that into account when you use '%d'
as the format specifier). And when an unsigned integer is 0
and you subtract something from it it "wraps around", resul-
ting in a positive value (if you subtract 1 you get the lar-
gest value that can be stored in a variable of that type).

Regards. Jens
 
B

Ben Bacarisse

#include <stdio.h>

int main()
{
/*size_t i;*/
int i;
for(i=10 ;i>=0; i--){
printf("%d\n", i);
}
return 0;
}


In the above code if I make 'i' to be of type size_t, when i = -1 the
comparison i>=0 is true and loop executes
even when i<0. Does anyone knows why ?

I thought this would be a FAQ, but I could not find the reference.
size_t is an unsigned type. Values of unsigned types can never be <0
so i < 0 is never true not matter what you put in i.

size_t i = -1;

set i to the largest value a size_t can hold. All good C books should
explain why this is, or at least the underlying rule that make it so.
 
K

Keith Thompson

I think I found it. Bacause size_t is unsigned int, it never happens
i=-1. The lowest value is i=0. When i-- overflow happens and i takes a
big unsigned int value. SO we have a infinite loop. This is clear when
we print i with modifier %u printf

size_t may well be unsigned int on your implementation. In general,
it's *some* unsigned integer type. Printing a size_t value with "%u"
is not portable. There are two good ways to print size_t values. In
C90:

size_t s = /* ... */;
printf("s = %lu\n", (unsigned long)s);

In C99:

size_t s = /* ... */;
printf("s = %zu\n", s);

The latter will work only if your runtime library's implementation of
printf supports the 'z' modifier; not all do.
 
A

Aura Networks

Hello,

Try this one, it will work :


int main()
{
size_t i;

for(i = 10;(signed) i >= 0; i--)
{
printf("i: %d \r\n", i);
}

return 0;
}

Only the thing is changing the for loop expression to 'signed'.

Thanks & Regards,
Aura Networks
 
R

Richard Bos

Aura Networks said:
Try this one, it will work :

It _may_ work. (In fact, it quite likely will, but the language doesn't
require it to.)
int main()
{
size_t i;

for(i = 10;(signed) i >= 0; i--)

Casting an unsigned value to a signed type in which that value cannot be
represented, has (essentially) undefined behaviour*. _Usually_ the
values will wrap around just as they do for unsigned types, but if you
want correct code, don't rely on that.

Richard

* Specifically, it converts to an implementation-defined value _or_
raises an implementation-defined signal; this latter option has
behaviour which is undefined by omission.
 
Z

zaimoni

I thought this would be a FAQ, but I could not find the reference.
size_t is an unsigned type.  

Both the definition of the type of the sizeof operator (which is
typedef'd as size_t), and the definition of stddef.h, require this.

definition of type of sizeof operator: C90 3.3.3.4p4; C99 6.5.3.4p4
definition of stddef.h: C90 4.1.5, C99 7.17
Values of unsigned types can never be <0
so i < 0 is never true not matter what you put in i.

  size_t i = -1;

set i to the largest value a size_t can hold.

This is only true for normal machines (two's complement representation
for signed integers). C90 overtly only requires a positional binary
numeration system (C90 3.1.2.5); C99 is more explicit and mentions
one's complement and sign-magnitude integers (C99 6.2.6.2p2). In any
case, decrementing any unsigned integer type is required to wrap
around to the largest value that integer type can hold.

On platforms lacking a working stdint.h (containing the macro
SIZE_MAX), the following should allow any reasonable optimizer to get
it right at compile-time:

size_t i = (size_t)0 ^ (size_t)0;
 
K

Keith Thompson

This is only true for normal machines (two's complement representation
for signed integers). C90 overtly only requires a positional binary
numeration system (C90 3.1.2.5); C99 is more explicit and mentions
one's complement and sign-magnitude integers (C99 6.2.6.2p2). In any
case, decrementing any unsigned integer type is required to wrap
around to the largest value that integer type can hold.

size_t i = -1; is guaranteed to set i to the largest value of type
size_t regardless of the underlying representation.

-1 is an expression of type int with the obvious value. C99 6.3.1.3
specifies the semantics of converting between integer types:

When a value with integer type is converted to another integer
type other than _Bool, if the value can be represented by the
new type, it is unchanged.

Otherwise, if the new type is unsigned, the value is converted
by repeatedly adding or subtracting one more than the maximum
value that can be represented in the new type until the value
is in the range of the new type.49)

Otherwise, the new type is signed and the value cannot be
represented in it; either the result is implementation-defined
or an implementation-defined signal is raised.
On platforms lacking a working stdint.h (containing the macro
SIZE_MAX), the following should allow any reasonable optimizer to get
it right at compile-time:

size_t i = (size_t)0 ^ (size_t)0;

If the conversion from int to size_t weren't guaranteed to do the
right think, you could write:

size_t i = -(size_t)1;

so the negation occurs in type size_t rather than in type int.
 
A

Andrey Tarasevich

Aura said:
Try this one, it will work :


int main()
{
size_t i;

for(i = 10;(signed) i >= 0; i--)
{
printf("i: %d \r\n", i);
}

return 0;
}

Only the thing is changing the for loop expression to 'signed'.

A completely unacceptable workaround.

Firstly, 'signed' is a synonym for 'int', which might easily have a
completely different size than that platform's 'size_t' meaning that the
above case might easily result in overflow. Even if size of 'int' is the
same as size of 'size_t', the positive range of 'int' might not be (and
will not be) sufficient to represent the positive range of 'size_t'.

Secondly, the typical coding idiom for implementing downward-counting
unsigned cycles looks as follows

size_t i;

for(i = 10 + 1; i > 0; )
{
--i;

/* Cycle body */
printf("i: %d \r\n", i);
}

or, as some people prefer

for(i = 10 + 1; i-- > 0; )
{
/* Cycle body */
printf("i: %d \r\n", i);
}

This is how one does it. But never by casting to a signed type.
 
B

Ben Bacarisse

Both the definition of the type of the sizeof operator (which is
typedef'd as size_t), and the definition of stddef.h, require this.

definition of type of sizeof operator: C90 3.3.3.4p4; C99 6.5.3.4p4
definition of stddef.h: C90 4.1.5, C99 7.17

Yes, I know. I meant I could not find this question in the FAQ, not
that I could not find a reference for size_t or sizeof!
This is only true for normal machines (two's complement representation
for signed integers). C90 overtly only requires a positional binary
numeration system (C90 3.1.2.5); C99 is more explicit and mentions
one's complement and sign-magnitude integers (C99 6.2.6.2p2).

It is true for all (conforming) implementations. 6.3.1.3 p2 defines
the result in terms of value not representation.
In any
case, decrementing any unsigned integer type is required to wrap
around to the largest value that integer type can hold.

I think this means you agree with me. It is a slightly round-about way
of saying what 6.3.1.3 p2 says about size_t i = -1;

<snip>
 
L

lawrence.jones

This is only true for normal machines (two's complement representation
for signed integers).

No, it's not. The conversion rules guarantee it regardless of the
representation.
 
A

Andrey Tarasevich

This is only true for normal machines (two's complement representation
for signed integers).

No, it is true for any conforming C implementation in general,
regardless of signed representation.
C90 overtly only requires a positional binary
numeration system (C90 3.1.2.5); C99 is more explicit and mentions
one's complement and sign-magnitude integers (C99 6.2.6.2p2).

No, there's no significant difference between C90 and C99 in this
regard. Both use "positional binary numeration system" and both mention
"one's complement" and "sign-magnitude " as possible signed
representations along with "two's complement".
In any
case, decrementing any unsigned integer type is required to wrap
around to the largest value that integer type can hold.

.... as well as assigning '-1' to an object of unsigned type, as shown above.
On platforms lacking a working stdint.h (containing the macro
SIZE_MAX), the following should allow any reasonable optimizer to get
it right at compile-time:

size_t i = (size_t)0 ^ (size_t)0;

Huh? What exactly are you expecting to get as the result if '(size_t)0 ^
(size_t)0' expression?
 
K

Keith Thompson

Andrey Tarasevich said:
No, it is true for any conforming C implementation in general,
regardless of signed representation.


No, there's no significant difference between C90 and C99 in this
regard. Both use "positional binary numeration system" and both
mention "one's complement" and "sign-magnitude " as possible signed
representations along with "two's complement".

No, there are significant differences. C99 allows only one of those
three representations for signed integers. C90 doesn't mention one's
complement or sign-magnitude at all, and mentions two's complement
only in an example and a couple of footnotes.

C90 does require a pure binary representation, and says that a given
non-negative value that's within the range of both a signed type and
the corresponding unsigned type must have the same representation in
both; the latter does eliminate some possibilities, such as a biased
representation. But I'm sure one could construct a signed
representation that's neither sign-and-magnitude, two's-complement,
nor ones'-complement, that would satisfy the C90 requirements; by
definition, it would not satisfy the C99 requirements. I doubt that
any real-world implementation has ever used such a representation, but
by adding the restriction C99 makes it possible for programs to make
assumptions that they couldn't necessarily make under C90.
 
K

Keith Thompson

On platforms lacking a working stdint.h (containing the macro
SIZE_MAX), the following should allow any reasonable optimizer to get
it right at compile-time:

size_t i = (size_t)0 ^ (size_t)0;

Um, that initializes i to 0.

Perhaps you meant:

size_t i = ~(size_t)0;

But that could have problems on systems where size_t is narrower than
int, since the operand of "~" is subject to the integer promotions.
But I doubt that any such systems actually exist.

If the size_t is not promoted, the result is guaranteed to be
SIZE_MAX. C99 6.5.3.3p4:

If the promoted type is an unsigned type, the expression ~E
is equivalent to the maximum value representable in that type
minus E.
 
Z

zaimoni

(e-mail address removed) writes:

[...]
On platforms lacking a working stdint.h (containing the macro
SIZE_MAX), the following should allow any reasonable optimizer to get
it right at compile-time:
size_t i = (size_t)0 ^ (size_t)0;

Um, that initializes i to 0.

Unfortunately, yes :(
Perhaps you meant:

    size_t i = ~(size_t)0;

But that could have problems on systems where size_t is narrower than
int, since the operand of "~" is subject to the integer promotions.
But I doubt that any such systems actually exist.

Yes, I was explicitly trying to avoid integer promotion triggering a
trap on one's complement trapping machines. In retrospect, this was a
vain goal.
 
C

CBFalconer

Aura said:
Try this one, it will work :

int main() {
size_t i;

for (i = 10; (signed) i >= 0; i--) {
printf("i: %d \r\n", i);
}
return 0;
}

No it doesn't. When the loop end is reached with i == 0, it is
decremented, and becomes the largest possible value of a size_t.
This is normally larger than INT_MAX, so the conversion to signed
integer will overflow. At this point you have undefined behaviour,
and the program can do anything it wishes.

If it works on your hardware you have uncovered a failure to detect
integer overflow.
 
R

Richard Bos

Keith Thompson said:
No, there are significant differences. C99 allows only one of those
three representations for signed integers. C90 doesn't mention one's
complement or sign-magnitude at all, and mentions two's complement
only in an example and a couple of footnotes.

C90 does require a pure binary representation, and says that a given
non-negative value that's within the range of both a signed type and
the corresponding unsigned type must have the same representation in
both; the latter does eliminate some possibilities, such as a biased
representation. But I'm sure one could construct a signed
representation that's neither sign-and-magnitude, two's-complement,
nor ones'-complement, that would satisfy the C90 requirements; by
definition, it would not satisfy the C99 requirements. I doubt that
any real-world implementation has ever used such a representation, but
by adding the restriction C99 makes it possible for programs to make
assumptions that they couldn't necessarily make under C90.

I once came up with a representation like that, for this very issue. It
combines characteristics of sign-magnitude and two's complement; in
fact, one might say that it is to sign-magnitude as two's complement is
to ones'. The smallest numbers, positive and negative, run like this for
four-bit numbers:

3 0011
2 0010
1 0001
0 0000
-1 1000
-2 1001
-3 1010
-4 1011

My flabber would be deeply ghasted should a machine using this scheme
ever have been made, but it _is_ a pure binary scheme that is not
allowed by C99.
I don't think that any reasonable program that works with all allowed
C99 representations would not work with this scheme; but neither do I
think that its official absence is going to discombobulate anyone. Even
so, given that the requirement of a "pure binary" system for integers
was already in C89, I don't see why this additional requirement on
negative values was added.

Richard
 

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

Forum statistics

Threads
473,770
Messages
2,569,584
Members
45,075
Latest member
MakersCBDBloodSupport

Latest Threads

Top