cast vs. suffixes

R

Ralf

If you want to explicitly state the type of an integer constant, you
basically have two options. Either you use an explicit cast, as in:
(unsigned int) 1234
or you use the UL suffixes, as in:
1234UL
Are there -- in general -- any semantic differences in using casts
versus suffixes?
--Ralf
 
V

Vladimir S. Oka

Ralf said:
If you want to explicitly state the type of an integer constant, you
basically have two options. Either you use an explicit cast, as in:
(unsigned int) 1234

This, at compiler's choice, can be left to be evaluated at
run-time. I'd still expect a good optimiser to do it at
compile-time. So, potentially, you get nasty bugs if you mistype
your constants.
or you use the UL suffixes, as in:
1234UL

This is always done at compile time. Here, mistyped constants
are caught while still in vitro.
Are there -- in general -- any semantic differences in using casts
versus suffixes?
--Ralf

There may also be other implications which escape me at the moment.

Cheers

Vladimir
 
E

Eric Sosman

Ralf wrote On 01/20/06 12:52,:
If you want to explicitly state the type of an integer constant, you
basically have two options. Either you use an explicit cast, as in:
(unsigned int) 1234
or you use the UL suffixes, as in:
1234UL
Are there -- in general -- any semantic differences in using casts
versus suffixes?

Yes. `(unsigned int)1234' is not a constant: it is
an expression consisting of the operator `(unsigned int)'
applied to the constant operand `1234'. The type of the
expression's result is `unsigned int' and the value is
one thousand two hundred thirty-four.

`1234UL' is a constant. Its type is `unsigned long'
and its value is one thousand two hundred thirty-four.
(Note that the type of this constant is not the same as
the type of the earlier expression.)

One place this makes an easily-observable difference
is in the preprocessor. Since the preprocessor operates
at an early stage of compilation before types "exist,"
it cannot evaluate cast operators -- since casts are all
about type conversions and there aren't any types yet,
there's really nothing the preprocessor can do with them.

Consider the following code:

#include <stdio.h>

int main(void) {
#if 1234UL > 0
puts ("#1: as expected");
#else
puts ("#1: unbelievable!");
#endif

#if (unsigned int)1234 > 0
puts ("#2: as expected");
#else
puts ("#2: unbelievable!");
#endif

return 0;
}

Without compiling and running it, can you predict the
output? (Warning: I've asked a trick question.)
 
K

Kenneth Brody

Ralf said:
If you want to explicitly state the type of an integer constant, you
basically have two options. Either you use an explicit cast, as in:
(unsigned int) 1234
or you use the UL suffixes, as in:
1234UL
Are there -- in general -- any semantic differences in using casts
versus suffixes?

What happens on a system with 16-bit integers with:

(long)65537

versus

65537L

--
+-------------------------+--------------------+-----------------------------+
| Kenneth J. Brody | www.hvcomputer.com | |
| kenbrody/at\spamcop.net | www.fptech.com | #include <std_disclaimer.h> |
+-------------------------+--------------------+-----------------------------+
Don't e-mail me at: <mailto:[email protected]>
 
B

Barry

Eric Sosman said:
Ralf wrote On 01/20/06 12:52,:

Yes. `(unsigned int)1234' is not a constant: it is
an expression consisting of the operator `(unsigned int)'
applied to the constant operand `1234'. The type of the
expression's result is `unsigned int' and the value is
one thousand two hundred thirty-four.

`1234UL' is a constant. Its type is `unsigned long'
and its value is one thousand two hundred thirty-four.
(Note that the type of this constant is not the same as
the type of the earlier expression.)

One place this makes an easily-observable difference
is in the preprocessor. Since the preprocessor operates
at an early stage of compilation before types "exist,"
it cannot evaluate cast operators -- since casts are all
about type conversions and there aren't any types yet,
there's really nothing the preprocessor can do with them.

Consider the following code:

#include <stdio.h>

int main(void) {
#if 1234UL > 0
puts ("#1: as expected");
#else
puts ("#1: unbelievable!");
#endif

#if (unsigned int)1234 > 0
puts ("#2: as expected");
#else
puts ("#2: unbelievable!");
#endif

return 0;
}

Without compiling and running it, can you predict the
output? (Warning: I've asked a trick question.)

It produces the "expected" result, of course :). I will try it
momentarily.
 
R

Ralf

OK, I guess that #if (unsigned int)1234 > 0 doesn't 'preprocess' that
is, it will yield an error.
Actually, I wanted to write (unsigned long) instead of (unsigned int),
but my fingers were quicker than my brain.
My wrong example actually makes quite a difference, even to the
compiler because of the 'usual arithmetic conversion' rules.
But let's assume that I had written (unsigned long)1234. Would there be
any semantic difference from the
*compiler's* point of view?
(You made a good point about the preprocesser; I would have never
thought about that)
 
N

N Mulangi

Eric said:
Ralf wrote On 01/20/06 12:52,:



Yes. `(unsigned int)1234' is not a constant: it is
an expression consisting of the operator `(unsigned int)'
applied to the constant operand `1234'. The type of the
expression's result is `unsigned int' and the value is
one thousand two hundred thirty-four.

`1234UL' is a constant. Its type is `unsigned long'
and its value is one thousand two hundred thirty-four.
(Note that the type of this constant is not the same as
the type of the earlier expression.)

One place this makes an easily-observable difference
is in the preprocessor. Since the preprocessor operates
at an early stage of compilation before types "exist,"
it cannot evaluate cast operators -- since casts are all
about type conversions and there aren't any types yet,
there's really nothing the preprocessor can do with them.

Consider the following code:

#include <stdio.h>

int main(void) {
#if 1234UL > 0
puts ("#1: as expected");
#else
puts ("#1: unbelievable!");
#endif

#if (unsigned int)1234 > 0
puts ("#2: as expected");
#else
puts ("#2: unbelievable!");
#endif

return 0;
}

Without compiling and running it, can you predict the
output? (Warning: I've asked a trick question.)

"# (unsigned int)1234 ..." is a compile error

"missing binary operator before token "int"

So basically, your observation is correct; the preprocessor has no
problems with the first set of #if/#else and borks on the second because
the types ("unsigned int" or whatever) doesn't yet exist.
 
K

Keith Thompson

Kenneth Brody said:
What happens on a system with 16-bit integers with:

(long)65537

versus

65537L

I presume you mean 16-bit ints. (The term "integer" includes all the
signed and unsigned integral types, from char to long long; "int" is
just one of those types.)

So, assuming int is 16 bits, type int will most likely have a range of
-32768 .. +32767, and unsigned int will most likely have a range of
0 .. 65535. (Padding bits and representations other than 2's-complement
can cause variations, but we'll ignore that.) Type long is guaranteed
to have a range of at least -2147483647 .. +2147483647.

The type of a decimal integer constant (like 65537) is the first of
the following in which is value can be represented:

int
long int
long long int (C99)

So if int is 16 bits, 65537 is already of type long, and the cast does
nothing. If int is 32 bits, 65537 is of type int, and the cast
converts it from int to long.

On any implementation, both (long)65537 and 65537L are of type long
and have the value 65537. The difference, as discussed previously, is
that 65537L is a constant and (long)65537 is not.
 
E

Eric Sosman

Ralf wrote On 01/20/06 14:08,:
OK, I guess that #if (unsigned int)1234 > 0 doesn't 'preprocess' that
is, it will yield an error.
Actually, I wanted to write (unsigned long) instead of (unsigned int),
but my fingers were quicker than my brain.
My wrong example actually makes quite a difference, even to the
compiler because of the 'usual arithmetic conversion' rules.
But let's assume that I had written (unsigned long)1234. Would there be
any semantic difference from the
*compiler's* point of view?
(You made a good point about the preprocesser; I would have never
thought about that)

Both `(unsigned long)1234' and `1234UL' are
expressions, the first consisting of an operator
and an `int' operator and the second consisting
only of an `unsigned long' constant. They'll have
the same type and the same value, and I can't think
of any circumstance where they'd behave differently
(once past the preprocessor).
 
E

Eric Sosman

N Mulangi wrote On 01/20/06 14:25,:
Eric Sosman wrote:
[...]
Consider the following code:

#include <stdio.h>

int main(void) {
#if 1234UL > 0
puts ("#1: as expected");
#else
puts ("#1: unbelievable!");
#endif

#if (unsigned int)1234 > 0
puts ("#2: as expected");
#else
puts ("#2: unbelievable!");
#endif

return 0;
}

Without compiling and running it, can you predict the
output? (Warning: I've asked a trick question.)


"# (unsigned int)1234 ..." is a compile error

"missing binary operator before token "int"

So basically, your observation is correct; the preprocessor has no
problems with the first set of #if/#else and borks on the second because
the types ("unsigned int" or whatever) doesn't yet exist.

Right. Even worse, the keywords `unsigned' and `int'
don't yet exist. As far as the preprocessor is concerned,
they're just identifiers that don't happen to be macros
and therefore aren't subject to macro substitutions. The
rules for #if specify that any identifiers remaining after
macro expansion (other than `defined') are evaluated as
zeroes, so the directive becomes the equivalent of

#if ( 0 0 ) 1234 > 0

.... and that's not a properly-formed expression.

By the way, I'm not suggesting that anybody in his
right mind would actually write the #if directives I showed
in my sample. It's still a concern, though, because of
things like

void f(void) {
#if MAX_WIDGETS > 100
/* large buffer: use dynamic memory */
struct widget *widgets =
malloc(MAX_WIDGETS * sizeof *widgets);
if (widgets == NULL) die();
#else
/* safe to use small auto buffer */
struct widget widgets[MAX_WIDGETS];
#endif

/* ... work with the array of widgets ... */

#if MAX_WIDGETS > 100
free (widgets);
#endif
}

This sort of thing is fine and dandy if MAX_WIDGETS
is #define'd as 42 or 1024u, but will cause exactly the
problem you spotted if defined as `(unsigned)99'.
 
E

Emmanuel Delahaye

Eric Sosman a écrit :
#if (unsigned int)1234 > 0
puts ("#2: as expected");
#else
puts ("#2: unbelievable!");
#endif
ITYM
if ((unsigned int)1234 > 0)
puts ("#2: as expected");
else
puts ("#2: unbelievable!");
 
K

Keith Thompson

Emmanuel Delahaye said:
Kenneth Brody a écrit :

compiler warning 'too big for the type' or the like...

No, if int can't hold the value 65537, then the literal is of type
long.
 

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,755
Messages
2,569,536
Members
45,013
Latest member
KatriceSwa

Latest Threads

Top