473,326 Members | 2,013 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,326 software developers and data experts.

compiler bug or misuse of a cast?

Hi,

In the following code fragment, gcc seems to ignore the initial value
assigned to pData. (compiled -fvolatile -O2 with gcc)

void test(void)
{
void *pData = (void*)0x3400;

pData = (void*)(*((unsigned char**)&pData) + 4);
memcpy(pData,(void*)0x1000,10);
return;
}

The generated code simply reads whatever garbage is at [sp], adds 4 to
that value, stores it back to [sp], and then calls memcpy.

I've found that, if I declare pData as

void * volatile pData = (void*)0x3400;

then the generated code behaves as I would expect -- that is, the value
actually passed to memcpy is 0x3404 because the initializer is not
optimized away.

I also found that, if I write this fragment this way..

void test(void)
{
void *pData = (void*)0x3400;
unsigned char *pChar;

pchar = pData;
pData = pChar + 4;
memcpy(pData,(void*)0x1000,10);
return;
}

then the generated code works as expected, passing 0x3404 to memcpy.

I know that the casts create temporary unnamed values, but I wouldn't
expect that to cause the complier to ignore the initializer.

It seems as though gcc doesn't consider the read access (&pData) when
deciding whether to optimize the initializer out of existence.

Does this make sense? Is a compiler allowed to ignore the code sequence
when read accesses are buried in a cast expression?

TIA,

GV

Nov 14 '05 #1
9 1431
On 27 Dec 2004 05:28:33 -0800, "gvarndell" <gv*******@hotmail.com>
wrote in comp.lang.c:
Hi,

In the following code fragment, gcc seems to ignore the initial value
assigned to pData. (compiled -fvolatile -O2 with gcc)
You're going to have to take this up with the compiler maintainers,
somewhere in the gnu group hierarchy.
void test(void)
{
void *pData = (void*)0x3400;
Initializing or assigning an integer value to a pointer type, with a
suitable cast, is legal but implementation-defined behavior.
pData = (void*)(*((unsigned char**)&pData) + 4);
Now you are attempting to use the value in pData as a valid pointer,
regardless of type. This is where the code crosses the line into
undefined behavior. The C standard places no requirements on the
result of this operation. Whether or not this code produces the
results you want is not a language issue.
memcpy(pData,(void*)0x1000,10);
return;
}

The generated code simply reads whatever garbage is at [sp], adds 4 to
that value, stores it back to [sp], and then calls memcpy.

I've found that, if I declare pData as

void * volatile pData = (void*)0x3400;

then the generated code behaves as I would expect -- that is, the value
actually passed to memcpy is 0x3404 because the initializer is not
optimized away.

I also found that, if I write this fragment this way..

void test(void)
{
void *pData = (void*)0x3400;
unsigned char *pChar;

pchar = pData;
pData = pChar + 4;
memcpy(pData,(void*)0x1000,10);
return;
}

then the generated code works as expected, passing 0x3404 to memcpy.

I know that the casts create temporary unnamed values, but I wouldn't
expect that to cause the complier to ignore the initializer.

It seems as though gcc doesn't consider the read access (&pData) when
deciding whether to optimize the initializer out of existence.

Does this make sense? Is a compiler allowed to ignore the code sequence
when read accesses are buried in a cast expression?
Regardless of whether or not the compiler generates code that operates
as the compiler coders intended, you won't get any support for your
position here. Once your program produces undefined behavior, neither
ISO C nor this group take a position one way or the other.

Of course, your expression is unnecessarily convoluted.
pData = (void*)(*((unsigned char**)&pData) + 4);'


The first thing I would do is eliminate the unnecessary cast to
pointer to void. Then I'd eliminate & operator and the extra level of
indirection. I would write this expression, if I had to write it at
all, as:

pData = (unsigned char *)pData + 4;

....and let it go at that.

But you need to take this up with the GNU people.

--
Jack Klein
Home: http://JK-Technology.Com
FAQs for
comp.lang.c http://www.eskimo.com/~scs/C-faq/top.html
comp.lang.c++ http://www.parashift.com/c++-faq-lite/
alt.comp.lang.learn.c-c++
http://www.contrib.andrew.cmu.edu/~a...FAQ-acllc.html
Nov 14 '05 #2
gvarndell <gv*******@hotmail.com> wrote:

In the following code fragment, gcc seems to ignore the initial value
assigned to pData. (compiled -fvolatile -O2 with gcc)
That's because you're lying to the compiler and it's getting its
revenge.
void test(void)
{
void *pData = (void*)0x3400;

pData = (void*)(*((unsigned char**)&pData) + 4);


Here's the lie -- you're telling the compiler to pretend that pData is
an unsigned char * rather than a void *. Instead of pretending, you
should just do a normal conversion:

pData = (unsigned char *)pData + 4;

Note that casting to void * is unnecessary (and, many would argue,
undesirable, unless you're actually writing C++ rather than C, in which
case it's required and your question is off-topic here).

-Larry Jones

I don't need to improve! Everyone ELSE does! -- Calvin
Nov 14 '05 #3
Thanks to you, and to Jack, for replying. The replies were helpful, but
don't get to the crux of my question -- which is my fault for framing
the question as a gcc question and supplying a convoluted example. So
I'd like to try again.

int test(void)
{
int a = 17;

a += 17;
return a;
}

If this function returned anything other than 34, the compiler would be
clearly broken.

int test(void)
{
int a = 17;

*((int *)&a) += 17;
return a;
}

Because of the fleeting nature of the values that result from casts,
I'm not so sure that any conforming C compiler would be to be blame if
this function returned 17 instead of the expected value of 34.

Wouldn't it be okay for any compiler to generate code that did this?
1. allocate storage for the integer variable 'a'
2. read an integer value from the address of 'a'
3. store whatever is read into a temporary
4. add 17 to the temporary and store the result in a temporary
5. store 17 into 'a' and return 'a'
I hope this clarifies my real question.

TIA
GV

Nov 14 '05 #4
gvarndell wrote:
Thanks to you, and to Jack, for replying. The replies were helpful, but
don't get to the crux of my question -- which is my fault for framing
the question as a gcc question and supplying a convoluted example. So
I'd like to try again.

int test(void)
{
int a = 17;

a += 17;
return a;
}

If this function returned anything other than 34, the compiler would be
clearly broken.

int test(void)
{
int a = 17;

*((int *)&a) += 17;
&a has type int *, and points to an int object with the value 17.
The cast is from int * to int *, so it's a nop. And the & and *
cancel. So the expression resolves, effectively, to

a += 17;

Because of the fleeting nature of the values that result from casts,
I'm not so sure that any conforming C compiler would be to be blame if
this function returned 17 instead of the expected value of 34.
It would in fact be blameworthy.

Wouldn't it be okay for any compiler to generate code that did this?
1. allocate storage for the integer variable 'a'
Yes.
2. read an integer value from the address of 'a'
Yes.
3. store whatever is read into a temporary
Yes.
4. add 17 to the temporary and store the result in a temporary
Yes.
5. store 17 into 'a' and return 'a'


No. The function must return the value 34.

Nov 14 '05 #5

"gvarndell" <gv*******@hotmail.com> wrote in message news:11**********************@z14g2000cwz.googlegr oups.com...
Hi,

In the following code fragment, gcc seems to ignore the initial value
assigned to pData. (compiled -fvolatile -O2 with gcc)

void test(void)
{
void *pData = (void*)0x3400;

pData = (void*)(*((unsigned char**)&pData) + 4);
memcpy(pData,(void*)0x1000,10);
return;
}

The generated code simply reads whatever garbage is at [sp], adds 4 to
that value, stores it back to [sp], and then calls memcpy. The garbage at [sp] is pData.
pData is a local variant which is stored in the stack. And [sp] is the register
pointed to the stack. The compiler may also use [bp]/[ebp] to access it,
which is read-writeable.
I've found that, if I declare pData as

void * volatile pData = (void*)0x3400;

then the generated code behaves as I would expect -- that is, the value
actually passed to memcpy is 0x3404 because the initializer is not
optimized away.

I also found that, if I write this fragment this way..

void test(void)
{
void *pData = (void*)0x3400;
unsigned char *pChar;

pchar = pData;
pData = pChar + 4;
memcpy(pData,(void*)0x1000,10);
return;
}

then the generated code works as expected, passing 0x3404 to memcpy.

I know that the casts create temporary unnamed values, but I wouldn't
expect that to cause the complier to ignore the initializer.

It seems as though gcc doesn't consider the read access (&pData) when
deciding whether to optimize the initializer out of existence.

Does this make sense? Is a compiler allowed to ignore the code sequence
when read accesses are buried in a cast expression?

TIA,

GV

Nov 14 '05 #6
gvarndell <gv*******@hotmail.com> wrote:

int test(void)
{
int a = 17;

*((int *)&a) += 17;
return a;
}

Because of the fleeting nature of the values that result from casts,
I'm not so sure that any conforming C compiler would be to be blame if
this function returned 17 instead of the expected value of 34.


I am -- that code is perfectly well defined and any compiler that
doesn't return 34 is broken. Your original code contained type punning
that is *not* well defined and thus compilers are free to do whatever
they like with it.

-Larry Jones

Things are never quite as scary when you've got a best friend. -- Calvin
Nov 14 '05 #7

la************@ugs.com wrote:
gvarndell <gv*******@hotmail.com> wrote:

int test(void)
{
int a = 17;

*((int *)&a) += 17;
return a;
}

Because of the fleeting nature of the values that result from casts, I'm not so sure that any conforming C compiler would be to be blame if this function returned 17 instead of the expected value of 34.
I am -- that code is perfectly well defined and any compiler that
doesn't return 34 is broken. Your original code contained type

punning that is *not* well defined and thus compilers are free to do whatever
they like with it.

-Larry Jones


Thank you once again. This makes sense to me.
One final question on this, if I may.

Since this code

void test(void)
{
void *pData=(void*)0x3400;

(*(unsigned char**)&pData) += 4;
memcpy((unsigned char *)pData, (unsigned char *)0x1000,10);
return;
}

apparently causes undefined behavior, is a compiler required to at
least issue a warning about it?
I tried -pedantic with gcc and it doesn't make a peep about the code.
TIA,
GV

Nov 14 '05 #8
On 29 Dec 2004 09:29:28 -0800
"gvarndell" <gv*******@hotmail.com> wrote:

<snip>
Thank you once again. This makes sense to me.
One final question on this, if I may.

Since this code

void test(void)
{
void *pData=(void*)0x3400;

(*(unsigned char**)&pData) += 4;
memcpy((unsigned char *)pData, (unsigned char *)0x1000,10);
return;
}

apparently causes undefined behavior, is a compiler required to at
least issue a warning about it?
I tried -pedantic with gcc and it doesn't make a peep about the code.


No. A compiler is not required to issue a warning for undefined
behaviour. One reason for this is that there are instances of undefined
behaviour that cannot be detected at compile time.

<OT>
gcc can give you warnings about some instances of undefined behaviour,
so it is well worth turning up the warnings. Something like "-ansi
-pedantic -Wall -O" might be useful. Others have different opinions
about the best set of options.
<OT>

However, always make sure you know *why* any given warning has been
produced before changing your code to remove it and never assume a
clean compile means correct code.
--
Flash Gordon
Living in interesting times.
Although my email address says spam, it is real and I read it.
Nov 14 '05 #9
In article <11*********************@z14g2000cwz.googlegroups. com>,
"gvarndell" <gv*******@hotmail.com> wrote:
la************@ugs.com wrote:
gvarndell <gv*******@hotmail.com> wrote:

int test(void)
{
int a = 17;

*((int *)&a) += 17;
return a;
}

Because of the fleeting nature of the values that result from casts, I'm not so sure that any conforming C compiler would be to be blame if this function returned 17 instead of the expected value of 34.


I am -- that code is perfectly well defined and any compiler that
doesn't return 34 is broken. Your original code contained type

punning
that is *not* well defined and thus compilers are free to do whatever
they like with it.

-Larry Jones


Thank you once again. This makes sense to me.
One final question on this, if I may.

Since this code

void test(void)
{
void *pData=(void*)0x3400;

(*(unsigned char**)&pData) += 4;
memcpy((unsigned char *)pData, (unsigned char *)0x1000,10);
return;
}

apparently causes undefined behavior, is a compiler required to at
least issue a warning about it?
I tried -pedantic with gcc and it doesn't make a peep about the code.


"Warnings" are not part of the C language. Good compilers give warnings
in situations where you have written code that is legitimate C code but
looks to the compiler (which is written by experienced programmers) as
if you had unintentionally done something that you didn't actually want
to do.

The memcpy call most certainly doesn't look unintentionally. Whoever
wrote that code most certainly intenteded to write that code. Therefore,
no warning.
Nov 14 '05 #10

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

4
by: Jonathan Fielder | last post by:
Hi, My program (below) casts a double (which is in range for a float) to a float. As far as I know this should give me the nearest representable float, which will loose some precision. I...
1
by: Sacha Faust | last post by:
I have an abstract class, RuleResponse, and then create a new class base on it, RuleResponseSequence, and override the ++ operater. If I try to cast a RuleResponse as a RuleResponseSequence and...
29
by: junky_fellow | last post by:
Consider the following piece of code: struct junk { int i_val; int i_val1; char c_val; }; int main(void) {
6
by: Rajesh.S | last post by:
some more info... >-----Original Message----- >I built a VC++.Net project as a dll and >included it as a reference in a c# project. >When I call a c++ function from the csharp >project I get...
30
by: Philippe Bertrand | last post by:
Is this a bug in the C# compiler or CLR runtime? enum MyEnum { ZERO = 0, ONE = 1, TWO = 2 } class Foo { public Foo(string,object) { ... } public Foo(string,MyEnum) { ... } } Foo f = new...
16
by: cyber citizen | last post by:
Hi Folks, We are encountering the following code issue on compiler susch as "xlc","gcc" but "icc" passes it successfully. Sample code: int main(void) { typedef unsigned char oratext;...
8
by: Charles Sullivan | last post by:
I have a program written in C under Linux (gcc) which a user has ported to run under AT&T SysV R4. He sent me a copy of his makelog which displays a large number of compiler warnings similar to...
0
by: erik.erikson | last post by:
I am getting a compiler error that I can't well explain or even understand the origin of (though I boiled it down close...). Below is a bare-bones example. What I am doing is defining the...
45
by: alertjean | last post by:
Or may be I am stubborn or dumb ... of not putting in a * in the typecast. This is code I am worrying about long long b=1; int *address ; address=(int)&b; printf ("%x %x...
0
by: DolphinDB | last post by:
Tired of spending countless mintues downsampling your data? Look no further! In this article, you’ll learn how to efficiently downsample 6.48 billion high-frequency records to 61 million...
0
by: ryjfgjl | last post by:
ExcelToDatabase: batch import excel into database automatically...
0
isladogs
by: isladogs | last post by:
The next Access Europe meeting will be on Wednesday 6 Mar 2024 starting at 18:00 UK time (6PM UTC) and finishing at about 19:15 (7.15PM). In this month's session, we are pleased to welcome back...
0
by: jfyes | last post by:
As a hardware engineer, after seeing that CEIWEI recently released a new tool for Modbus RTU Over TCP/UDP filtering and monitoring, I actively went to its official website to take a look. It turned...
0
by: ArrayDB | last post by:
The error message I've encountered is; ERROR:root:Error generating model response: exception: access violation writing 0x0000000000005140, which seems to be indicative of an access violation...
1
by: Defcon1945 | last post by:
I'm trying to learn Python using Pycharm but import shutil doesn't work
1
by: Shællîpôpï 09 | last post by:
If u are using a keypad phone, how do u turn on JavaScript, to access features like WhatsApp, Facebook, Instagram....
0
by: af34tf | last post by:
Hi Guys, I have a domain whose name is BytesLimited.com, and I want to sell it. Does anyone know about platforms that allow me to list my domain in auction for free. Thank you
0
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 3 Apr 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome former...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.