Why compiler not generating any warning ?

J

junky_fellow

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.
So, what is the way to cacth these type of bugs ? Is there
any other tool that may find out all undefined, unportable
behaviour ?
I tried *lint* as well but it also didn't warned for this.

Only, the typecasting to "char *" should be allowed which is
*guaranteed* to point to the lowest addressed byte of the structure.
 
J

Jens.Toerring

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.

You will also get undefined behaviour when you write past the end of
an array, if you use a pointer to already deallocated memory etc. ect.
but the compiler won't warn about that.
But on compilation I don't get any warning message.

Well, you tell the compiler "convert this pointer to a pointer of
a different type". So the compiler does, and since you explicitely
told it to do that why should it doubt the wisdom of your commands?
If you cast you basically say "I know what I am doing, don't tell
me I shouldn't, if I would like you to warn me I wouldn't use that
[expletive censored] cast". And afterwards you tell it "assign to
a pointer from a pointer of the same type" which is 100% legal and
not dangerous at all.
So, what is the way to cacth these type of bugs ? Is there
any other tool that may find out all undefined, unportable
behaviour ?

The best tool is probably between your ears;-) View all casts as
being suspicious until you're satisifed that they are ok. Finding
all instances of undefined behaviour would be impossible, it might
only happen at runtime - the compiler can't typically know in ad-
vance if e.g. in an access of an array element the index is going
to be within the bounds of the array. And undefined behaviour is
not something evil in itself, it's just something the C standard
does not define. And in most programs you actually rely on unde-
fined behaviour to work out fine, e.g. when you call a function
in a third-party library - while a strictly conforming program may
use only those features of the language and library specified in
the C standard you very often need more than just that.
Only, the typecasting to "char *" should be allowed which is
*guaranteed* to point to the lowest addressed byte of the structure.

In this case maybe yes. But there can be situations where you
know what's going to happen on the platform you're working on
(because it's well-documented) and where you have to explicitely
invoke undefined behaviour in order to get things done.

Regards, Jens
 
J

jacob navia

pete said:
Learn more C.
What is the point of this reply?

He is exactly trying to learn more C and asks in this
discussion group a question.

Of course *you* never asked any questions when you were learning C.

Why being rude to newcomers?

If you do not feel like answering or you think the question is silly
just do not reply.

jakob
 
P

pete

jacob said:
What is the point of this reply?

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.
 
E

Ed Vogel

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.

From the switches used, I'll guess you're using the HP C compiler
on Tru64. If that's the case, you can get the compiler to
emit a diagnostic for this case.

Using -check enables most messages, but not all. To enable
all messages use the -msg_enable all option. To get the
specific message for this case, use -msg_enable ansialiascast.

Ed Vogel
HP/Compaq/DEC C/C++ Engineering
 
R

Robert Gamble

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.

You can safely convert a pointer to a structure to a pointer to the first
element of the structure, no undefined behavior. What is dangerous is the
pointer arithmetic you perform after the conversion. You might expect
i_ptr to point to the second member in the structure but since there may
be padding between the two integers, dereferencing the pointer would
invoke UB.
But on compilation I don't get any warning message.

I don't see anything that requires such a message.
So, what is the way to cacth these type of bugs ? Is there any other
tool that may find out all undefined, unportable behaviour ?

Nothing to add to pete and Jens responses.
I tried *lint* as well but it also didn't warned for this.
Okay.

Only, the typecasting to "char *" should be allowed which is
*guaranteed* to point to the lowest addressed byte of the structure.

Nope. In addition to this and the case I described above, a pointer to a
structure may safely be converted to a pointer to any other structure and
back again.

Robert Gamble
 
J

Jonathan Bartlett

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 ? */

Why? You casted pstr_junk to an integer pointer. Since you are doing
an explicit cast, the compiler assumes that you know what you are doing.
Then you told the integer pointer to advance one, which is a perfectly
valid thing for an integer pointer to do (though, because it's from the
cast, the results are undefined, but since you did an explicit cast, the
compiler won't complain). Then you assigned your new integer pointer to
i_ptr, which is of type int *.

What did you expect the compiler to complain about?

Jon
 
M

Mark McIntyre

On 3 Jun 2005 04:03:17 -0700, in comp.lang.c ,
However, the statement
i_ptr = (int *)pstr_junk + 1; seems to be incorrect to me.

it /is/ incorrect. But you lied to the compiler, by putting in the
cast. This says "shut up, I know what I'm doing" to the compiler.
Hence no warning.
So, what is the way to cacth these type of bugs ?

Avoid casts, which are almost never needed in C, unless you're trying
to do naughty things. Or programme better...
I tried *lint* as well but it also didn't warned for this.

It can't since you already lied and told us that you knew what you
were doing....
Only, the typecasting to "char *" should be allowed which is
*guaranteed* to point to the lowest addressed byte of the structure.

You need to understand the difference between a conversion and a cast.
 
M

Mark McIntyre

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.

To be fair though, you could have explained *why* the compiler didn't
grumble, and why using casts is a bad idea.
 
C

CBFalconer

.... snip ...
i_ptr = (int *)pstr_junk + 1; /* shouldn't the compiler
give warning here ? */

Why? You used a cast, which said specifically "I know what I am
doing and don't harass me about it". You can always add 1 to a
pointer, but the result may not be useful.

Casts are usually a sign of an error.
 
J

Jens.Toerring

Why? You used a cast, which said specifically "I know what I am
doing and don't harass me about it". You can always add 1 to a
pointer, but the result may not be useful.
Casts are usually a sign of an error.

May I qualify this a bit? I would say that using casts in programs
that are supposed to be portable are, if not a sign of an error,
often a sign of either bad design or a sign of misunderstandings.
On the other hand, in programs - and there are a lot in the wild,
and I have to admit that I am guilty of writing my share of them
- where you deliberately invoke undefined behaviour to get things
done (e.g. in device drivers and other programs for e.g. directly
accessing hardware), casts may be the only way to go and, when used
while being aware of what they do they are not necessarily evil.
Since 'junky_fellow' seems to be on a quest of finding out where
what makes sense and what's a stupid idea I think he has some right
to get the real gray-scale picture and not just the black-and-white
view of the world;-)
Regards, Jens
 
B

Barry Schwarz

Why? You casted pstr_junk to an integer pointer. Since you are doing
an explicit cast, the compiler assumes that you know what you are doing.
Then you told the integer pointer to advance one, which is a perfectly
valid thing for an integer pointer to do (though, because it's from the
cast, the results are undefined, but since you did an explicit cast, the

Why do you think this is undefined. After the cast, the intermediate
value must point to str_junk.i_val (which you omitted from your quoted
material). Incrementing this value by one points to the byte
immediately after this int (and still well within the struct) which is
perfectly legal as long as it is not dereferenced.
compiler won't complain). Then you assigned your new integer pointer to
i_ptr, which is of type int *.

What did you expect the compiler to complain about?

Jon



<<Remove the del for email>>
 
J

junky_fellow

I thank you all for your answers. From the various replies in this
thread what I concluded is that:

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

2) Sometimes while writing a piece of code that has to
directly access h/w (device drivers, embedded s/w) casts may be
the only option. In such cases, one must be aware of the consequences
of typecasting. Also, in such cases code review is the best
tool to find out such bugs. Compiler may not be issuing the
warning for all such *incompatible* casts.

Still I have one question. I might sound irritating but its better to
ask. Please bear with me. As most of you said that the compiler
didn't generate any warning because it was explicitly told to do
so. But there are other cases as well where explicit cast is done,
still the complier gives the warning.
eg.
int main(void) {
char * c_ptr;
int * i_ptr;
(int *)i_ptr = (char *) c_ptr; /* warning for this line */
return(0);
}

compiling this code always generates warning.
 
K

Keith Thompson

I thank you all for your answers. From the various replies in this
thread what I concluded is that:

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

2) Sometimes while writing a piece of code that has to
directly access h/w (device drivers, embedded s/w) casts may be
the only option. In such cases, one must be aware of the consequences
of typecasting. Also, in such cases code review is the best
tool to find out such bugs. Compiler may not be issuing the
warning for all such *incompatible* casts.

That sounds about right.
Still I have one question. I might sound irritating but its better to
ask. Please bear with me. As most of you said that the compiler
didn't generate any warning because it was explicitly told to do
so. But there are other cases as well where explicit cast is done,
still the complier gives the warning.
eg.
int main(void) {
char * c_ptr;
int * i_ptr;
(int *)i_ptr = (char *) c_ptr; /* warning for this line */
return(0);
}

compiling this code always generates warning.

It might be helpful to mention what the warning says, but ...

Both the casts are no-ops; they convert a value of a given type to
that same type. If you're getting a warning about incompatible
pointer types, it has nothing to do with the casts. You'd get the
same warning without them.

But the most important point is that the assignment is illegal (though
some compilers may allow it as an extension). A cast is not an
lvalue, and cannot appear on the left side of an assignment, any more
than you can assign

x + 1 = 3;

If you invoke your compiler in conforming mode, it should reject the
assignment.
 
J

Jens.Toerring

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. Take the following
(admittedly rather contrived) example program:

#include <stdio.h>
#include <stdarg.h>

double add_to( double stuff, ... )
{
va_list ap;
double *dp;

va_start( ap, stuff );
while ( ( dp = va_arg( ap, double * ) ) != NULL )
stuff += *dp;
va_end( ap );

return stuff;
}

int main( void )
{
double d0 = 1.0,
d1 = 2.0;

printf( "%f\n", add_to( 0, &d0, ( double * ) NULL ) );
printf( "%f\n", add_to( 0, &d0, &d1, ( double * ) NULL ) );
return 0;
}

The add_to() function takes a double argument plus an unspecified num-
ber of double pointers and then adds all the values the pointers are
pointing to to the first argument, returning the result. The end of
the argument list is marked by a NULL double pointer.

You may have noticed that I wrote "NULL double pointer" and not just
"NULL pointer" in the last sentence. That's because a "NULL double
pointer" could have a different bit representation than e.g. "NULL
void pointer", and it's simple values in a certain bit representation
tha get passed to functions. When you have a function foo() which is
declared as

void foo( double *dp );

and you call it like this

foo( NULL );

then the compiler knows that foo() expects a double pointer and will
convert NULL to a value with the correct bit representation for a
"NULL double pointer" and not the bit representation for e.g. a "NULL
void pointer" if the representations differ (that's another reason
why having correct function declarations can be so important).

But there's a snag with the add_to() function - the compiler doesn't
know about the types of the arguments following the first one. So
when calling add_to() the compiler can't help you by automatically
converting pointers if necessary and it's then your obligation to
make sure pointers of the correct type get passed to the function.
And that's why in the call

add_to( 0, &d0, ( double * ) NULL )

the NULL pointer actually must be cast - without the cast, the function
would be called with a value which is the bit representation of a "NULL
void pointer" and not necessarily the required representation of a "NULL
double pointer". So this is a place where a pointer cast isn't a mistake
but required. (Of course, one may argue that a design of that kind is
bad, but sometimes there may be no better solution.)
2) Sometimes while writing a piece of code that has to
directly access h/w (device drivers, embedded s/w) casts may be
the only option. In such cases, one must be aware of the consequences
of typecasting. Also, in such cases code review is the best
tool to find out such bugs. Compiler may not be issuing the
warning for all such *incompatible* casts.

The compiler probably won't issue a warning for whatever cast (except
for casts of function pointers to object pointers) since a cast tells
it that you know better - why would you use the cast otherwise?
Still I have one question. I might sound irritating but its better to
ask. Please bear with me. As most of you said that the compiler
didn't generate any warning because it was explicitly told to do
so. But there are other cases as well where explicit cast is done,
still the complier gives the warning.
eg.
int main(void) {
char * c_ptr;
int * i_ptr;
(int *)i_ptr = (char *) c_ptr; /* warning for this line */
return(0);
}

compiling this code always generates warning.

Something must be wrong with your compiler since this shouldn't just
generate a warning but an error message. You can't use a cast like
that on the left hand side of the assignment, it's like writing

int x;
-x = 3;

On the left hand side of an assignment you always have to have an ex-
pression that, when evaluated, must designate an object. And like "-x"
is not an existing object, so also "(int *) i_ptr" does not designate
an existing object (the result of the cast is a pointer value, not an
object). It's not like e.g.

*( (int *) i_ptr ) = 3;

where the computed value of the expression designates an object (a
memory location) a value can be assigned to (at least if 'i_ptr'
points to some memory you have permission to write to and that the
alignment requirements are satisfied).

Let's hope I got that right, otherwise someone more knowledgeable will
chime in.
Regards, Jens
 
A

Alan J. McFarlane

of typecasting. Also, in such cases code review is the best
tool to find out such bugs. Compiler may not be issuing the
warning for all such *incompatible* casts.
[/QUOTE]
I've taken to creating an include file Cast.h with the following
#ifndef NO_CAST
# define CAST(x) (x)
#else
# define CAST(x)
#endif

Then I create casts as e.g.
i_ptr = CAST(int*) c_ptr;

Firstly, one can then simply find all (such) casts in your code by
searching for CAST(, which is AFAIK impossible when the cast operator is
just a type in brackets.

Also simply with #define NO_CAST, all the casts are hidden, and thus the
compiler highlights all places where casting is required. Then I can
quickly step through all the warnings to see if the casts I have in
place are valid.

I'd welcome feedback from the pro's on whether this is useful, not, or
just amazingly stupid. :)

Since casting a integer and a pointer is so different, I've also
wonderer whether to have two 'operators', INT_CAST() (or
INTEGRAL_CAST()?) and PTR_CAST(), to allow each form to be
found/undefined separately...
 
E

Ed Vogel

Something must be wrong with your compiler since this shouldn't just
generate a warning but an error message.

The Standard only requires a diagnostic message. The Standard
makes no distinction between "error" or "warning" or any other
type of message.

Again...assuming the compiler being used is the Compaq C compiler
on Tru64, the compiler will emit two diagnostics for this program:

(int *)i_ptr = (char *) c_ptr; /* warning for this line */
1
(1) Info: In this statement, the result of the cast "(int ...)i_ptr" is used
as
an lvalue. (lvaluecast)

(1) Warning: In this statement, the referenced type of the pointer value
"(char
....)c_ptr" is "char", which is not compatible with "
int". (ptrmismatch)

Ed Vogel
HP/Compaq/DEC C/C++ Engineering
 
J

Joe Wright

Keith said:
That sounds about right.




It might be helpful to mention what the warning says, but ...
I get:
jf.c:4: warning: assignment from incompatible pointer type

If you really want to do the assignment:
i_ptr = (int*)c_ptr;
should work.
Both the casts are no-ops; they convert a value of a given type to
that same type. If you're getting a warning about incompatible
pointer types, it has nothing to do with the casts. You'd get the
same warning without them.

But the most important point is that the assignment is illegal (though
some compilers may allow it as an extension). A cast is not an
lvalue, and cannot appear on the left side of an assignment, any more
than you can assign

x + 1 = 3;

If you invoke your compiler in conforming mode, it should reject the
assignment.

Yes. It must reject it. In an assignment, the lvalue is the object, not
the value it holds. For example:

int x = 1;
int y = 2;

if (x == 1)

treats the value of x.

x = y;

disregards the value of x. In this context x is an object, not a value.

(int)x = y;

This must fail because the cast creates a value where an object is required.
 
J

Jens.Toerring

The Standard only requires a diagnostic message. The Standard
makes no distinction between "error" or "warning" or any other
type of message.
Again...assuming the compiler being used is the Compaq C compiler
on Tru64, the compiler will emit two diagnostics for this program:
(int *)i_ptr = (char *) c_ptr; /* warning for this line */
1
(1) Info: In this statement, the result of the cast "(int ...)i_ptr" is used
as
an lvalue. (lvaluecast)
(1) Warning: In this statement, the referenced type of the pointer value
"(char
...)c_ptr" is "char", which is not compatible with "
int". (ptrmismatch)

Ok, thanks;-)
Regard, Jens
 

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,767
Messages
2,569,572
Members
45,045
Latest member
DRCM

Latest Threads

Top