Why compiler not generating any warning ?

A

Ari Lukumies

Consider the following piece of code:

struct junk {
int i_val;
int i_val1;
char c_val;
};

int main(void)
{
struct junk str_junk;
int * i_ptr;
struct junk * pstr_junk;
pstr_junk = &str_junk;
i_ptr = (int *)pstr_junk + 1; /* shouldn't the compiler
give warning here ? */
Only, the typecasting to "char *" should be allowed which is
*guaranteed* to point to the lowest addressed byte of the structure.

Is this a troll? You explicitly told to cast the assigned address,
so why should you receive a warning? If you want to get warnings,
lose the casts. Typecasting to char* does not guarantee anything.
If you give it the address of one byte past the lowest byte of the
structure, it happily accepts that.

-atl-
 
K

Keith Thompson

1) One should aviod using casts while writing a program that is
supposed to be portable. casts are sign of bad design.

Let's be a bit more pedantic and restrict that to pointer casts.
Casting "normal" values is quite common, you may often see in C89
programs e.g.

char hw[ ] = "Hello world!";
printf( "%lu\n", ( unsigned long ) strlen( hw );

to convert the size_t type return value of strlen() to something you
can print with one of the available format specifier (in C99 you bet-
ter would drop the cast and use "%zu" in the format string instead) or

double d = 42.1234;
int i = ( int ) d;

to assign the integer part of 'd' to 'i', snipping off the fractional
part.

And while casting pointers is often a sign of some trouble in a program,
there exist a few cases where they it's actually required, mostly in
conjunction with arguments of variadic functions.
[...]

Most casts are unnecessary because, in most contexts, the value will
be implicitly converted to the required type.

Variadic functions are probably the major exception to this rule. For
example, printf with a "%p" specifier expects a void*; if you want to
pass it an int*, you have to explicitly convert it. (A non-variadic
equivalent would convert from int* to void* implicitly.) printf with
a "%lu" expects an unsigned long; if you want to pass it a foo_t, you
have to convert it. (For size_t, C99 provides "%zu", but it can't
cover everything.)

If you see a cast in a context other than a call to a variadic
function, it's likely to be either unnecessary (specifying a
conversion that would have been done implicitly without the cast), or
potentially dangerous (performing a non-trivial conversion when it
very likely would have been better to use the correct type in the
first place).

In your example:

double d = 42.1234;
int i = ( int ) d;

the cast is actually unnecessary; it could have been written as

double d = 42.1234;
int i = d;

It's tempting to use the cast anyway, since it makes the conversion
expicit and numeric conversions aren't as dangerous as pointer
conversions. On the other hand, if during maintenance the type of
i is changed from int to long, but you forget to change the cast:

double d = 42.1234;
long i = ( int ) d;

you could be introducing a subtle bug that may be very difficult to
track down. (It's obvious in this example, but would be less so if
the assignments are far from the declarations.) That's another
drawback of the cast operator: the syntax requires you to specify the
target type by name, but sometimes what you really want is the type of
the target, whatever that happens to be. (A typeof operator might be
useful here -- not that I'm suggesting adding it to the language.)

One case where a numeric cast is necessary is when you want to control
when the conversion takes place. For example:

int x = 1;
int y = 3;
double z = (double)x/y;

Without the cast, z would be assigned the value 0.0.

By using a cast, you're promising the compiler that you know what
you're doing. You'd better be right.
 
S

shmartonak

It's tempting to use the cast anyway, since it makes the conversion
expicit and numeric conversions aren't as dangerous as pointer
conversions. On the other hand, if during maintenance the type of
i is changed from int to long, but you forget to change the cast:

Can one then say that if there's any possibility that a type
might need to be changed for any reason, changing program requirements or
porting to a different platform, then a typedef should be used?

typedef int MyIntegerType /* change to 'long long' on a Whizbang-X100 */

...

MyIntegerType i;
double d = 12.345;

...

i = (MyIntegerType)d;

--
 
K

Kenneth Brody

Consider the following piece of code:

struct junk {
int i_val;
int i_val1;
char c_val;
};

int main(void)
{
struct junk str_junk;
int * i_ptr;
struct junk * pstr_junk;
pstr_junk = &str_junk;
i_ptr = (int *)pstr_junk + 1; /* shouldn't the compiler
give warning here ? */
}

On compiling this program: cc -c99 -check -portable test.c
I don't get any warning.
However, the statement
i_ptr = (int *)pstr_junk + 1; seems to be incorrect to me.

Here, pstr_junk is a pointer to structure and it is being typecasted to
"int *" which may cause undefined behaviour.
But on compilation I don't get any warning message.
[...]

Because you have explicitly cast pstr_junk to (int *), you have basically
told the compiler "be quiet, I know what I'm doing".

If you had used an implicit cast:

i_ptr = pstr_junk;
i_ptr++;

then the compiler probably would be generating some nasty messages along
the lines of "are you sure you want to do this?"

On the other hand, is it undefined behavior when the first two members
of the struct are ints?

On the other other hand, it looks like what the code is trying to do is:

i_ptr = &(pstr_junk->i_val1);

which is perfectly legal and much clearer as to the intent.

--
+-------------------------+--------------------+-----------------------------+
| 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 Schwarz

Consider the following piece of code:

struct junk {
int i_val;
int i_val1;
char c_val;
};

int main(void)
{
struct junk str_junk;
int * i_ptr;
struct junk * pstr_junk;
pstr_junk = &str_junk;
i_ptr = (int *)pstr_junk + 1; /* shouldn't the compiler
give warning here ? */
}

On compiling this program: cc -c99 -check -portable test.c
I don't get any warning.
However, the statement
i_ptr = (int *)pstr_junk + 1; seems to be incorrect to me.

Here, pstr_junk is a pointer to structure and it is being typecasted to
"int *" which may cause undefined behaviour.
But on compilation I don't get any warning message.
[...]

Because you have explicitly cast pstr_junk to (int *), you have basically
told the compiler "be quiet, I know what I'm doing".

If you had used an implicit cast:

i_ptr = pstr_junk;
i_ptr++;

then the compiler probably would be generating some nasty messages along
the lines of "are you sure you want to do this?"

On the other hand, is it undefined behavior when the first two members
of the struct are ints?

On the other other hand, it looks like what the code is trying to do is:

i_ptr = &(pstr_junk->i_val1);

which is perfectly legal and much clearer as to the intent.

But not necessarily the same result. There is no guarantee that
i_val1 immediately follows i_val since the compiler is free to insert
any padding it wants at this point.


<<Remove the del for email>>
 
K

Keith Thompson

Kenneth Brody said:
If you had used an implicit cast:

i_ptr = pstr_junk;
i_ptr++;

then the compiler probably would be generating some nasty messages along
the lines of "are you sure you want to do this?"

Correction: if you had used an implicit *conversion*. There's no such
thing as an implicit cast.

A cast is a source construct; the cast operator is a type name
enclosed in parentheses, preceding the operand. The operation that a
cast specifies is a conversion. Conversions can be explicit or
implicit (an explicit conversion is indicated by a cast operator).
 
M

Malcolm

pete said:
The only way that you catch undefined behavior
that your compiler warnings miss, is by knowing enough C.
That's pretty much the whole point of reading this newsgroup.
If you look at what junky_fellow is doing, he seems to be playing games with
the C type system, maybe to work out what the underlying representation is
like. So why not tell him that the actual bit pattern and structure layout
varies from platform to platform, and that this is a surefire way to get
into trouble?

We've all got to learn, and at least he making some effort and not just
posting homework questions.
 
C

CBFalconer

Keith said:
.... snip ...

One case where a numeric cast is necessary is when you want to
control when the conversion takes place. For example:

int x = 1;
int y = 3;
double z = (double)x/y;

Without the cast, z would be assigned the value 0.0.

Which cast can also be avoided by:

int x = 1;
int y = 3;
double z = x;

z /= y;
 
P

pete

Malcolm said:
If you look at what junky_fellow is doing,
he seems to be playing games with
the C type system, maybe to work out what the underlying
representation is like.
So why not tell him that the actual bit pattern and structure layout
varies from platform to platform,
and that this is a surefire way to get into trouble?

I don't see what you see in junky_fellow's code.
I saw nothing that had to do with bit patterns.
It looked like portable C99 code to me.
We've all got to learn, and at least he making
some effort and not just posting homework questions.

I see no bugs here:

struct junk {
int i_val;
int i_val1;
char c_val;
};

int main(void)
{
struct junk str_junk;
int * i_ptr;
struct junk * pstr_junk;
pstr_junk = &str_junk;
i_ptr = (int *)pstr_junk + 1; /* shouldn't the compiler
give warning here ? */
}
 
L

Lawrence Kirby

Which cast can also be avoided by:

int x = 1;
int y = 3;
double z = x;

z /= y;

This is an example where the cast isn't particulatly bad, in fact I prefer
the cast form. It is clear and to the point. It is pointer casts that are
dangerous, casts between arithmetic types are usually benign - all such
conversions are allowed implcitly anyway. The only thing that avoiding the
cast protects you against in this example would be x being a pointer. That
could be a legitimate concern in some cases (e.g. catching (double *)*p
mistyped as (double *)p ) but it isn't usually a big deal.

So just because you can avoid casts doesn't mean that you automatically
should. You could avoid all of the casts in

printf("%.*lx %p\n", (int)prec, (unsigned long)size, (void *)ptr);

by assigning the values to variables of the appropriate type beforehand,
but sometimes casts are a good alternative, even for portable code.

Lawrence
 

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,020
Latest member
GenesisGai

Latest Threads

Top