comaprison b/w signed and unsigned

C

c.lang.myself

I want to know how comparison b/w unsigned and signed quantity is done
i.e. how the conversions takes place..

Is following program behavior undefined...

#include<stdio.h>

#define TOTAL_ELEMENTS (sizeof(array) / sizeof(array[0]))
int array[] = {23,34,12,17,204,99,16};

int main()
{
int d;

for(d=-1;d <= (TOTAL_ELEMENTS-2);d++)
printf("%d\n",array[d+1]);

return 0;
}
 
T

Tomás Ó hÉilidhe

I want to know how comparison b/w unsigned and signed quantity is done
i.e. how the conversions takes place..


Before you can perform any kind of mathematical or bitwise operation
on an integer expression, the integer expression must undergo "integer
promotion". If the type of the expression is "int" or bigger, then no
integer promotion takes place. If the type of the expression is "int"
or smaller, then the expression gets promoted to either "signed int"
or "unsigned int".

Smaller than int: char, short
Bigger than or equal to int: int, long, long long

(The last one "long long" was introduced with the C Standard of 1999)

So let's say we have two objects as follows:

short unsigned i = 5;
long j = 15;

(If you don't specify the signedness, then the default is "signed".)

And let's say we have an expression as follows:

i + j

The first thing we need to do is promote both operands. "i" will get
promoted to either "signed int" or "unsigned int" depending on whether
USHRT_MAX is greater than INT_MAX.

if (USHRT_MAX > INT_MAX) it becomes signed int;
else it becomes unsigned int;

(It wants to become signed if possible)

On most systems, it'll become "signed int", but let's go against the
grain with our example and pretend it promotes to "unsigned int".
Because "j" is bigger than int, we don't do anything to it. So the
types we have so far are:

(unsigned int) + (signed long)

Next thing we have to do is make them the same size, and we do this by
making the smaller one bigger. Therefore, the "int" has to become
"long". If UINT_MAX is bigger than LONG_MAX, the "int" will become
"unsigned long", otherwise it will become "signed long". (Again, it
wants to become signed if possible). In our example, let's pretend
that it becomes "unsigned long". So our types so far are:

(unsigned long) + (signed long)

Last thing we do is match the signedness, and "unsigned" always takes
precedence, so the "signed long" becomes an "unsigned long" as
follows:

(unsigned long) + (unsigned long)

Now that the operands are of the same type, the operation can be
carried out.

So to summarise all that:
1) Promote both the operands.
2) Match the sizes. (Make the new smaller size "signed" if the range
is sufficient, otherwise make it "unsigned").
3) Match the signedness. (Unsigned takes precedence).

Hopefully I haven't made a mistake there but I'm not infallible so
wait a minute or two to see if anyone points out a mistake.


Is following program behavior undefined...

 #include<stdio.h>

  #define TOTAL_ELEMENTS (sizeof(array) / sizeof(array[0]))



This is 7.


  int array[] = {23,34,12,17,204,99,16};

  int main()
  {
      int d;

      for(d=-1;d <= (TOTAL_ELEMENTS-2);d++)
          printf("%d\n",array[d+1]);



To make this easier to read, I'm going to add 1 to d, and remove it
from d in the body of the loop:

for (d = 0; d <= (TOTAL_ELEMENTS-1); d++)
printf("%d\n",array[d]);

Seems fine to me.
 
T

Tomás Ó hÉilidhe

if (USHRT_MAX > INT_MAX) it becomes signed int;
                   else it becomes unsigned int;


This is wrong. Here's the revision:

if (USHRT_MAX > INT_MAX) it becomes unsigned int;
else it becomes signed int;

(It wants to become signed if possible)
 
V

vippstar

I want to know how comparison b/w unsigned and signed quantity is done
i.e. how the conversions takes place..

Before you can perform any kind of mathematical or bitwise operation
on an integer expression, the integer expression must undergo "integer
promotion". If the type of the expression is "int" or bigger, then no
integer promotion takes place. If the type of the expression is "int"
or smaller, then the expression gets promoted to either "signed int"
or "unsigned int".
Is following program behavior undefined...
#include<stdio.h>

#define TOTAL_ELEMENTS (sizeof(array) / sizeof(array[0]))

This is 7.
int array[] = {23,34,12,17,204,99,16};
int main()
{
int d;
for(d=-1;d <= (TOTAL_ELEMENTS-2);d++)
printf("%d\n",array[d+1]);

To make this easier to read, I'm going to add 1 to d, and remove it
from d in the body of the loop:

for (d = 0; d <= (TOTAL_ELEMENTS-1); d++)
printf("%d\n",array[d]);

Seems fine to me.


You're an idiot and a troll. By adding 1 to d, you change the behavior
of the program, because of the integer promotion (for which you wrote
so much before)
 
V

vippstar

I want to know how comparison b/w unsigned and signed quantity is done
i.e. how the conversions takes place..

Well, have you tried searching a comp.lang.c archive for answers?
<http://groups.google.com/group/comp.lang.c/msg/bc70b38a3431febf>
Is following program behavior undefined...

No, it's defined. However, it is not what I think you expected to
happend. (the value of each element of 'array' to be printed)
#include<stdio.h>

Fine, usually prefered to add a space between '#include' and the
header name.
#define TOTAL_ELEMENTS (sizeof(array) / sizeof(array[0]))

Well, it makes more sense to define the macro as:

#define TOTAL_ELEMENTS(x) (sizeof (x) / sizeof (x)[0])

so you can use it for any array, not just the one named 'array'.
int array[] = {23,34,12,17,204,99,16};

int main()

I prefer to be explicit:

int main(void)

or

int main(int argc, char **argv)
{
int d;

for(d=-1;d <= (TOTAL_ELEMENTS-2);d++)
printf("%d\n",array[d+1]);

I suppose that here you expected the values of the elements of array
to be printed.
However, the condition d <= (TOTAL_ELEMENTS - 2) is met before any
iteration.

d is int, has the negative value -1.
(TOTAL_ELEMENTS - 2) is size_t, has the positive value 5.
Therefore, the expression d <= (TOTAL_ELEMENTS - 2) is equal to

-1 <= (size_t)5

Because of integer promotions, -1 is promoted to (size_t).
(size_t)-1 is equal to SIZE_MAX, which is guaranteed to be at least
32767 in C90 (notice, however, that SIZE_MAX is a C99 macro) and 65535
in C99, so it's certainly larger than 5.
 
T

Tomás Ó hÉilidhe

for (d = 0; d <= (TOTAL_ELEMENTS-1); d++)
    printf("%d\n",array[d]);

Seems fine to me.

By adding 1 to d, you change the behavior
of the program, because of the integer promotion



Correct, I was wrong.

The result of the "sizeof" operator is an expression of the type
"size_t". size_t is an unsigned integer type, it might be unsigned
long, it might be unsigned long long. Let's pretend it's unsigned long
for now:

In the comparison field of the for loop, you have:

(signed int) < (unsigned long)

First thing that happens is the "int" becomes "signed long".

(signed long) < (unsigned long)

Next thing that happens is the "signed long" becomes "unsigned long":

(unsigned long) < (unsigned long)

When the signed long becomes an unsigned long, a small negative value
will become a large positive value. For instance, -1 might become
something like 4294967295. When the loop first runs, this large
positive value will be compared against 7, and since it's a bigger
than 7, the loop body will never execute.
 
B

Barry Schwarz

I want to know how comparison b/w unsigned and signed quantity is done
i.e. how the conversions takes place..

You need to read section 6.3.1.8 very carefully.
Is following program behavior undefined...

I don't think so but whether you get any output is determined by how
many iterations of the loop occur and that is implementation
dependent.
#include<stdio.h>

#define TOTAL_ELEMENTS (sizeof(array) / sizeof(array[0]))
int array[] = {23,34,12,17,204,99,16};

int main()
{
int d;

for(d=-1;d <= (TOTAL_ELEMENTS-2);d++)

Expression TOTAL_ELEMENTS is of type size_t and the only thing we know
is that it is unsigned. Its rank could be greater than, equal to, or
less than the rank of int. Expression 2 has type signed int. The
type of the combination of these expressions
TOTAL_ELEMENTS-2
is determined by their relative ranks:
If rank(size_t) >= rank(int), then the combination has type
size_t.
Else, the combination has type signed int. (Since TOTAL_ELEMENTS
will evaluate to 7 here, the following is not a factor. However, if
it evaluated to a value that could not fit in a signed int, you would
get an implementation-defined value or an implementation_defined
signal would be raised.)

Object d is of type signed int.

If the combination has type size_t, then when evaluating
d <= ( (TOTAL_ELEMENTS-2)
the current value of d will be converted to size_t which is unsigned
so the result will be (size_t)(-1). This is the same value as
SIZE_MAX which (being greater than 7) will cause the comparison to
evaluate as false. Your loop will never execute (or if you prefer
will execute zero iterations) and you will get no output.
printf("%d\n",array[d+1]);

When the combination as type signed int, the loop executes "normally"
and your subscripts range from 0 to TOTAL_ELEMENTS-1 which appears
valid to me.
 
T

Tomás Ó hÉilidhe

INT_MAX being greater than (size_t)-1 seems unusual,
but there's no rules against it.



My initial reaction to this was that this must be an oversight in the
Standard... however it made sense to me when I considered 8-Bit
microcontrollers. I've worked with 8-Bit PIC microcontrollers that
have 8-Bit registers and a very small instruction set, and I used the
PIC C compiler. With this setup, if you do something like the
following:

unsigned i = 0; /* Standard says must be at least 16-Bit */

i += SomeExternalInputWhichCouldBeBiggerThan256();

then it results in machine code that uses bitwise operations to
manipulate two contiguous 8-Bit chunks of memory as if they were one
single 16-Bit chunk. If the compiler can see that the value will never
go above 256, it uses a single 8-Bit chunk, but it follows the "as if"
principle 100%. I played around with this a lot and was quite
surprised when I viewed the assembler. Here's an example of where it'd
use a single 8-Bit chunk:

unsigned i;

for (i = 0; i != 7; ++i)
DoWhatever();

If you were to stick in some code that does "sizeof i", then you'd
have a 16-Bit chunk.

In wanting to keep my code portable and fast for both microcontrollers
and PC's, I adopted the use of stuff like "uint_fast8_t" instead of
"unsigned" in all of my coding.

For a microcontroller that has less than 256 bytes of data memory,
it's conceivable that "size_t" would be a typedef of "char unsigned".

I suppose if you want "size_t" to remain as an unsigned type after
integer promotion, then the only option is to cast it to "uintmax_t".
If there's no uintmax_t, then I don't think there's a portable
solution because (if I'm not mistaken) size_t could be a typedef for a
compiler-add-on-type that's bigger than "long long unsigned".

But at the end of it all, I still do think it's an oversight that
size_t was not said to be at least "int unsigned" because the C
Standard doesn't go out of its way to be kind to microcontrollers (and
microcontrollers are the only ones that would benefit from having a
smaller size_t). Quite the contrary, Standard-C-compliant compilers
for 8-Bit microcontrollers go out of their way to make their machine
code efficient while at the same time abiding by the Standard.
 
V

vippstar

My initial reaction to this was that this must be an oversight in the
Standard...however it made sense to me when I considered 8-Bit
microcontrollers. I've worked with 8-Bit PIC microcontrollers that
have 8-Bit registers and a very small instruction set, and I used the
PIC C compiler. With this setup, if you do something like the
following:

unsigned i = 0; /* Standard says must be at least 16-Bit */

i += SomeExternalInputWhichCouldBeBiggerThan256();

Please don't do this. Do not describe what the function does in its
name.

i += f(); /* f returns some value bigger than 256 */
then it results in machine code that uses bitwise operations to
manipulate two contiguous 8-Bit chunks of memory as if they were one
single 16-Bit chunk. If the compiler can see that the value will never
go above 256, it uses a single 8-Bit chunk, but it follows the "as if"
principle 100%. I played around with this a lot and was quite
surprised when I viewed the assembler.

And? Where are you getting at?
Here's an example of where it'd use a single 8-Bit chunk:

unsigned i;

for (i = 0; i != 7; ++i)
DoWhatever();

If you were to stick in some code that does "sizeof i", then you'd
have a 16-Bit chunk.

I don't see how sizeof i is relevant to that hypothetical optimization
trick.
I think, with that hypothetical optimization trick, that it'd be 16-
bit if you were to use a value bigger than 255.
In wanting to keep my code portable and fast for both microcontrollers
and PC's, I adopted the use of stuff like "uint_fast8_t" instead of
"unsigned" in all of my coding.

uint_fast8_t is at least 8 bits. unsigned is at least 16 bits. I don't
think they are interchangeable.
For a microcontroller that has less than 256 bytes of data memory,
it's conceivable that "size_t" would be a typedef of "char unsigned".

unsigned char, not char unsigned. There's absolutely no reason to use
char unsigned other than to troll newsgroups.
I suppose if you want "size_t" to remain as an unsigned type after
integer promotion, then the only option is to cast it to "uintmax_t".
If there's no uintmax_t, then I don't think there's a portable
solution because (if I'm not mistaken) size_t could be a typedef for a
compiler-add-on-type that's bigger than "long long unsigned".

If there's no uintmax_t, there's no hosted C99 implementation.
It either leaves you with a freestanding implementation or C89/C90, in
which case unsigned long long (not long long unsigned - don't be
stupid) doesn't exist and unsigned long is guaranteed to be the
largest integer type.

#if __STDC_VERSION__ >= 199901L
# if __STDC_HOSTED__
# include <stdint.h>
typedef uintmax_t maxint_t
# else
# error Not supported
# endif /* __STDC_HOSTED__ */
#else
typedef unsigned long maxint_t
#endif /* __STDC_VERSION__ >= 199901L */

then use (maxint_t)sizeof obj or (T).
But at the end of it all, I still do think it's an oversight that
size_t was not said to be at least "int unsigned" because the C

It's not. If the implementation wants it to be, it can be. If the
implementation doesn't want it to be, it won't be.
At the end of it all, it's all about having sane implementors.
Standard doesn't go out of its way to be kind to microcontrollers (and
microcontrollers are the only ones that would benefit from having a
smaller size_t). Quite the contrary, Standard-C-compliant compilers
for 8-Bit microcontrollers go out of their way to make their machine
code efficient while at the same time abiding by the Standard.

Microcontrollers probably don't need hosted implementations.
 
H

Hallvard B Furuseth

pete said:
Unless, INT_MAX is greater than ((size_t)-1),

Greater or equal. I.e. if the range of the unsigned type
fits in the signed type.
in which case the ((size_t)5) expression,
gets converted to (int) 5,
which -1 happens to be less than.

INT_MAX being greater than (size_t)-1 seems unusual,
but there's no rules against it.

True. E.g. 32-bit signed int, 31-bit unsigned int (where the
"sign bit" = 0, a padding bit), and typedef unsigned int size_t;
 
B

Barry Schwarz

Greater or equal. I.e. if the range of the unsigned type
fits in the signed type.


True. E.g. 32-bit signed int, 31-bit unsigned int (where the
"sign bit" = 0, a padding bit), and typedef unsigned int size_t;

No need to be that complicated. On a system where short is 16 bits
and int is 32 bits, the definition
typedef unsigned short size_t;
satisfies the standard and produces the "unusual" situation.
 
C

Chris Dollin

Please don't do this. Do not describe what the function does in its
name.

(a) This is an illustrative example. Why not use a name which says
something important about the function result?

(b) In any case, why not? Choosing good names for things is a critical
part of writing programs.

If what you meant was, don't name a function for how it's /implemented/,
name it for its reason for being, then yes, I agree. If you meant, don't
name functions with essays, then I agree. If you meant, watch out because
not /all/ the characters of an external name may be preserved, then I agree
that caution may be indicated. But if you meant, give functions meaningless
names (with meaningful comments), then I don't.

--
"We did not have time to find out everything /A Clash of Cymbals/
we wanted to know." - James Blish

Hewlett-Packard Limited Cain Road, Bracknell, registered no:
registered office: Berks RG12 1HN 690597 England
 
P

Phil Carmody

Please don't do this. Do not describe what the function does in its
name.

Someone tell the standards committee that they need to rename about
50% of their functions.

Personally I quite like having the function name tell you what
the function does. It makes code review easier, certainly.
I don't care about how the function's implemented, but that's
a different matter entirely.

Phil
 
V

vippstar

(a) This is an illustrative example. Why not use a name which says
something important about the function result?

It's hard to read. I think i += f(); /* may return value > 255 */ or
something like that.
(Also, did you notice that the value must be bigger than 255, not 256
as the name said?)
(b) In any case, why not? Choosing good names for things is a critical
part of writing programs.

Notice that 'SomeExternalInputWhichCouldBeBiggerThan256' is 42
characters long.
If what you meant was, don't name a function for how it's /implemented/,
name it for its reason for being, then yes, I agree. If you meant, don't
name functions with essays, then I agree. If you meant, watch out because
not /all/ the characters of an external name may be preserved, then I agree
that caution may be indicated. But if you meant, give functions meaningless
names (with meaningful comments), then I don't.

Anything but the last. A meaningless name is fine for illustrative
purposes, as long as the function is explained, I think.
 
V

vippstar

Someone tell the standards committee that they need to rename about
50% of their functions.

They don't need to, and they shouldn't. But if they could, I think
they would, if not rename, certainly redesign. There's no acceptable
naming convention in the standard, simply because the ANSI C standard
was a ratification of things that were in common use back in the day
in C. Of course that is not possible because it wouldn't be backwards
compatible.

Take an example fgets:

int fgets(char *s, int size, FILE *stream);

int size? Why int? It should be size_t. atoi, gets, et cetera should
be completely removed. I can't find an example of a bad name that
should be changed in standard C, possibly because I'm too used to the
functions.

Personally I quite like having the function name tell you what
the function does. It makes code review easier, certainly.
I don't care about how the function's implemented, but that's
a different matter entirely.

Briefly, like strcpy I think it's acceptable.
StringCopy wouldn't be though, nor LIB_StringCopy (since it would
propably be prefixed with something)
 
C

Chris Dollin

It's hard to read. I think i += f(); /* may return value > 255 */ or
something like that.

I think that's at least as hard to read.
(Also, did you notice that the value must be bigger than 255, not 256
as the name said?)

Not particularly. Do you think that piece of non-noticing particularly
important in the context of my comment? Which do you think more likely
to survive the editing process -- the function name or the associated
comments -- if this were real code and not a usenet example?
Notice that 'SomeExternalInputWhichCouldBeBiggerThan256' is 42
characters long.

A very apt choice of length. I commend the originator.
 
H

Hallvard B Furuseth

Barry said:
No need to be that complicated. On a system where short is 16 bits
and int is 32 bits, the definition
typedef unsigned short size_t;
satisfies the standard and produces the "unusual" situation.

Seems even less likely though. Such a size_t gets promoted to
int, and I suspect that a size_t which is signed even if it's just
after integer promotions is going to cause trouble. So I'd expect
int to be 16 bits too on such a host, to avoid such trouble.

OTOH if you have hardware which implements unsigned arithmetic
like signed with sign bit = padding 0, then the C implementation's
integer types will likely reflect that, trouble or no trouble.
 
V

vippstar

fgets returns (char *).

Whoops, you're right. I know it returns char *, I wrote int though...
fgetc returns int.

I don't think that's relevant because I'm not confused about fgets
return type, it was a "programming typo".
(like when you want to write copy and you write cpy instead)
The old stdio functions, such as printf and fgetc:
1 are older than size_t is,

I do know why fgets' second parameter is int. I even mentioned it in
my post:

Even though I was talking about the naming convention, I do state that
the ANSI C standard was (among other things), a ratification of things
that were in common use, such as fgets.

I'm not asking the reason the person who thought of fgets decided it
should take int, nor the reason ANSI C adopted it, because both
questions have straightforward answers. I'm just saying that I think
the C standard library is not as well designed (ignoring some of the
goals of C, ie backwards compatible) as it could be.
2 return EOF, which is a negative integer.

Also not related.
 

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

Similar Threads


Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,065
Latest member
OrderGreenAcreCBD

Latest Threads

Top