types and conversions

M

Mark

I'm somewaht confused with what type to choose and possible conversion it
may have to undergo. Let me explain with a real example:

extern int read_reg(int unit, unsigned int addr, uint16_t *val);
....

#define REG_PORT 0x50
/* ports enumerate from 1 to 25 */
extern int port_link_get(int unit, unsigned short port, int *link);

int port_link_get(int unit, unsigned short port, int *link)
{
int rv;
unsigned short addr;
uint16_t val;

addr = REG_PORT + ((port - 1) / 3);
rv = read_reg(unit, addr, &val);
....
return rv;
}

As far as I know, only in the absence of a prototype, function arguments
unfergo default promotions. We have prototypes, so no promotion/conversion
apply to arguments, am I tight?

Furthemore, the second argument of 'port_link_get' is of type 'unsigned
short', it participates in evaluating 'addr'. Will the operands of 'addr =
...' undergo arithmetic conversion?

And finally 'addr' is passed to 'read_reg', where it should be converted
(promoted?) to uint32_t. If so, it'd probably more effective to have:

extern int port_link_get(int unit, unsigned int port, int *link);

Are my reasonings right?
So, if I'm on an embedded system with memory constrints, it would not be
possible to save a few bytes by declaring 'port' argument as unsigned short
or even unsigned char?

What is the common sense to decide on data type, so for both effective use
and portable?
 
E

Eric Sosman

I'm somewaht confused with what type to choose and possible conversion
it may have to undergo. Let me explain with a real example:

extern int read_reg(int unit, unsigned int addr, uint16_t *val);
...

#define REG_PORT 0x50
/* ports enumerate from 1 to 25 */
extern int port_link_get(int unit, unsigned short port, int *link);

int port_link_get(int unit, unsigned short port, int *link)
{
int rv;
unsigned short addr;
uint16_t val;

addr = REG_PORT + ((port - 1) / 3);
rv = read_reg(unit, addr, &val);
....
return rv;
}

As far as I know, only in the absence of a prototype, function arguments
unfergo default promotions. We have prototypes, so no
promotion/conversion apply to arguments, am I tight?

Based on the loose wording and spelling errors, I'd say it's
at least plausible that you might be tight. ;-)

As for the rest,

- The default argument promotions apply when there is no prototype,
and to arguments in the "..." part of a variable-argument function
(which must have a prototype).

- An argument matching a prototyped parameter does not undergo the
default argument promotions, but it *is* converted (if necessary)
to the type specified by the prototype.

- In your example, one such non-trivial conversion does in fact
occur: The second argument expression in the call to read_reg()
is of type `unsigned short', and this expression is converted
to `unsigned int' as directed by the prototype.
Furthemore, the second argument of 'port_link_get' is of type 'unsigned
short', it participates in evaluating 'addr'. Will the operands of 'addr
= ..' undergo arithmetic conversion?

It's not the same thing as the conversions that apply to function
arguments, but yes: Conversions are at work here.

- The parameter `port' is an `unsigned short', and the "integer
promotions" convert it to some flavor of `int'. It might be
to `int' or to `unsigned int' depending on the widths of `int'
and `short' in a particular machine.

- On machines where `unsigned short' promotes to `int', the
right-hand side now consists entirely of `int' operands and
the arithmetic is carried out according to `int' rules, giving
an `int' result.

- On machines where `unsigned short' promotes to `unsigned int',
the right-hand side now consists of a mixture of `unsigned int'
and plain `int' operands. The latter are all promoted to
`unsigned int', the arithmetic is carried out according to
`unsigned int' rules, and the result is an `unsigned int'.

- Finally, the result (whether `int' or `unsigned int') is converted
to `unsigned short', and `addr' receives this converted value.
And finally 'addr' is passed to 'read_reg', where it should be converted
(promoted?) to uint32_t.

No: to `unsigned int'.
If so, it'd probably more effective to have:

extern int port_link_get(int unit, unsigned int port, int *link);

Are my reasonings right?

I'm not sure what you mean by "more effective." "More predictable,"
maybe, since you'd avoid the "What does `unsigned short' promote to?"
question, essentially by forcing the expression to use `unsigned int'
arithmetic willy-nilly. This could make a difference, for example,
with a `port' value of zero:

- In `int' arithmetic, the sub-expression `(port - 1) / 3' would
evaluate as `-1 / 3' and produce zero

- In `unsigned int' arithmetic, `(port - 1) / 3' would evaluate
as `UINT_MAX / 3' and produce something like 21845 or 1431655765
(these are common outcomes, other values are possible)
So, if I'm on an embedded system with memory constrints, it would not be
possible to save a few bytes by declaring 'port' argument as unsigned
short or even unsigned char?

Maybe. On some machines it may take extra instructions to handle
smaller-than-int values (sign-extending, masking, ...), so it's possible
that space saved by shrinking the data might be lost in bloating code.
On the other hand, some extremely low-end processors (8-bit, 4-bit, ...)
might need multiple-precision arithmetic even for `int' calculations,
so using a minimally-sized datum could be a win. It's entirely system-
dependent.
What is the common sense to decide on data type, so for both effective
use and portable?

Common sense is "Stick with `(unsigned) int' until and unless you
have a reason to abandon it." The reason (if there is one) might be
portable ("I need Really Big numbers") or non-portable ("My chosen CPU
does `short' arithmetic faster/smaller/cheaper than `int', and the
difference is large enough to worry about").
 
C

Chad

     I'm not sure what you mean by "more effective."  "More predictable,"
maybe, since you'd avoid the "What does `unsigned short' promote to?"
question, essentially by forcing the expression to use `unsigned int'
arithmetic willy-nilly.  This could make a difference, for example,
with a `port' value of zero:

     - In `int' arithmetic, the sub-expression `(port - 1) / 3' would
       evaluate as `-1 / 3' and produce zero

     - In `unsigned int' arithmetic, `(port - 1) / 3' would evaluate
       as `UINT_MAX / 3' and produce something like 21845 or 1431655765
       (these are common outcomes, other values are possible)

How do you figure that in 'int' arithmetic case, the sub-expression
would evaluate as '-1/3', but evaluate as 'UINT_MAX/3' in the
'unsigned int' case?
 
E

Eric Sosman

How do you figure that in 'int' arithmetic case, the sub-expression
would evaluate as '-1/3', but evaluate as 'UINT_MAX/3' in the
'unsigned int' case?

What is the value of `0u - 1'?
 
E

Eric Sosman

Why wouldn't it be 'INT_MAX / 3' ?

What is the type of `port' (hint: see the O.P.'s code)?

What type does `port' promote to (hint: see the assumption at
the beginning of the quoted paragraph)?

What is the type of `1'?

What happens when the `-' operator has operands of different types?

What are the "usual arithmetic conversions?"

What are the "integer promotions?"

What color are the pages of your C textbook or reference?

Have you seen the pages with your own eyes, or are you guessing?
 
B

Barry Schwarz

How do you figure that in 'int' arithmetic case, the sub-expression
would evaluate as '-1/3', but evaluate as 'UINT_MAX/3' in the
'unsigned int' case?

If port is signed int and has the value 0, what is the result (type
and value) of evaluating the parenthetical expression?

If port is unsigned int and has the value 0, what is the result (type
and value) of evaluating the parenthetical expression?
 
C

Chad

If port is signed int and has the value 0, what is the result (type
and value) of evaluating the parenthetical expression?

If port is unsigned int and has the value 0, what is the result (type
and value) of evaluating the parenthetical expression?

I'm still not 'getting it' like what I should. Anyways, let me attempt
to reason this out. '1' would of of type int. But port is of type
'unsigned short'. This means port would get promoted to type 'int'. If
port is a 'signed int' and has a value of '0', then the sub-expression
becomes (0u - 1) / 3 or -1/3.

Here is the part that I'm confused about. If port is of type 'unsigned
int' and has a value of '0', then shouldn't the sub-expression become
(0u - 1) / 3 instead of ((UINT_MAX + 1) - 1) / 3 ?
 
K

Keith Thompson

Chad said:
Here is the part that I'm confused about. If port is of type 'unsigned
int' and has a value of '0', then shouldn't the sub-expression become
(0u - 1) / 3 instead of ((UINT_MAX + 1) - 1) / 3 ?

I haven't followed this entire discussion; I'm commenting only on the
quoted paragraph (actually just the last line).

Consider the subexpression (0u - 1). 0u is of type unsigned int;
1 is of type int. The "usual arithmetic conversions" are applied
to the operands of the "-" operator; these conversions are explained
in detail in C99 6.3.1.8, but in this case it just means that the
right operand is converted from int to unsigned int.

So now we have (0u - 1u). The *mathematical* result of this subtraction
is -1, but -1 cannot be represented directly as an unsigned int value,
so the result is "reduced modulo the number that is one greater than the
largest value that can be represented by the resulting type" (C99
6.2.5p9).

So (0u - 1) yields the value UINT_MAX of type unsigned int.

Now we have UINT_MAX / 3. Again, the usual arithmetic conversions
are applied, and 3 is converted from int to unsigned int; then
the division yields a result of type unsigned int with a value
that depends on the value of UINT_MAX (if UINT_MAX is 2**32-1,
the result is 1431655765).
 
C

Chad

[...]
Here is the part that I'm confused about. If port is of type 'unsigned
int' and has a value of '0', then shouldn't the sub-expression become
(0u - 1) / 3 instead of ((UINT_MAX + 1) - 1) / 3 ?

I haven't followed this entire discussion; I'm commenting only on the
quoted paragraph (actually just the last line).

Consider the subexpression (0u - 1).  0u is of type unsigned int;
1 is of type int.  The "usual arithmetic conversions" are applied
to the operands of the "-" operator; these conversions are explained
in detail in C99 6.3.1.8, but in this case it just means that the
right operand is converted from int to unsigned int.

So now we have (0u - 1u).  The *mathematical* result of this subtraction
is -1, but -1 cannot be represented directly as an unsigned int value,
so the result is "reduced modulo the number that is one greater than the
largest value that can be represented by the resulting type" (C99
6.2.5p9).

So (0u - 1) yields the value UINT_MAX of type unsigned int.

Now we have UINT_MAX / 3.  Again, the usual arithmetic conversions
are applied, and 3 is converted from int to unsigned int; then
the division yields a result of type unsigned int with a value
that depends on the value of UINT_MAX (if UINT_MAX is 2**32-1,
the result is 1431655765).

Okay, to my defense, I wasn't aware that the result of (0u - 1u) was
"reduced modulo the number that is one greater than the largest value
that can be represented by the resulting type". This probably due to
the fact that I don't have the C99 standard, and hence, haven't read
it.
 
R

Richard Bos

Okay, to my defense, I wasn't aware that the result of (0u - 1u) was
"reduced modulo the number that is one greater than the largest value
that can be represented by the resulting type". This probably due to
the fact that I don't have the C99 standard, and hence, haven't read
it.

It's also in C90, as well as (logically) in K&R 2.

Richard
 
F

Francis Moreau

     Based on the loose wording and spelling errors, I'd say it's
at least plausible that you might be tight.  ;-)

     As for the rest,

     - The default argument promotions apply when there is no prototype,
       and to arguments in the "..." part of a variable-argument function
       (which must have a prototype).

     - An argument matching a prototyped parameter does not undergo the
       default argument promotions, but it *is* converted (if necessary)
       to the type specified by the prototype.

     - In your example, one such non-trivial conversion does in fact
       occur: The second argument expression in the call to read_reg()
       is of type `unsigned short', and this expression is converted
       to `unsigned int' as directed by the prototype.

Sorry but I don't understand the last part: the prototype of
'read_reg()' specifies that the type of the second parameter is
'unsigned short'. So why is the second argument converted to 'unsigned
int' ?

Thanks
 
I

Ian Collins

Sorry but I don't understand the last part: the prototype of
'read_reg()' specifies that the type of the second parameter is
'unsigned short'. So why is the second argument converted to 'unsigned
int' ?

The OP had:

unsigned short addr;

rv = read_reg(unit, addr,&val)

Where the prototype was:

extern int read_reg(int unit, unsigned int addr, uint16_t *val);

Hence addr is promoted from "unsigned short" to "unsigned int".
 
F

Francis Moreau

The OP had:

unsigned short addr;

rv = read_reg(unit, addr,&val)

Where the prototype was:

extern int read_reg(int unit, unsigned int addr, uint16_t *val);

Hence addr is promoted from "unsigned short" to "unsigned int".

Damn I mis read the prototypes.

Sorry for the noise.
 

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,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top