473,490 Members | 2,472 Online
Bytes | Software Development & Data Engineering Community
Create Post

Home Posts Topics Members FAQ

type-punning?

Hi all

I have a program which, with inessential details removed,
looks like this:

--
#include <stdlib.h>
#include <stdio.h>

static int punme(void** dat,size_t newsize)
{
void *newdat = realloc(*dat,newsize);

if (! newdat) return 1;

*dat = newdat;

return 0;
}

int main (void)
{
char *dat = malloc(30);

int ret = punme((void**)&dat,40);

printf("punme returns %i\n",ret);

return 0;
}
--

My compiler (gcc -Wall -O3) tells me that "typepun.c:19:
warning: dereferencing type-punned pointer will break
strict-aliasing rules". The code works as expected, ie,
prints that punme returns 0.

Who is the idiot? Me or the compiler?

Many thanks!

Jim
Jun 27 '08 #1
14 2186
On May 6, 6:46 pm, j.j.fish...@gmail.com wrote:
Hi all

I have a program which, with inessential details removed,
looks like this:

--
#include <stdlib.h>
#include <stdio.h>

static int punme(void** dat,size_t newsize)
{
void *newdat = realloc(*dat,newsize);

if (! newdat) return 1;

*dat = newdat;

return 0;

}

int main (void)
{
char *dat = malloc(30);

int ret = punme((void**)&dat,40);

printf("punme returns %i\n",ret);

return 0;}

--

My compiler (gcc -Wall -O3) tells me that "typepun.c:19:
warning: dereferencing type-punned pointer will break
strict-aliasing rules". The code works as expected, ie,
prints that punme returns 0.

Who is the idiot? Me or the compiler?
You (cast) and assign a char ** to a void **. void ** is not like void
*.
Fixed your code:
static int punme(void* dat,size_t newsize) {
void **tmp = dat;
void *newdat = realloc(*tmp, newsize);
You also don't free the allocated memory.
Jun 27 '08 #2
On Tue, 06 May 2008 09:43:53 -0700, vippstar wrote:
On May 6, 6:46 pm, j.j.fish...@gmail.com wrote:
>#include <stdlib.h>
#include <stdio.h>

static int punme(void** dat,size_t newsize) {
void *newdat = realloc(*dat,newsize);

if (! newdat) return 1;

*dat = newdat;

return 0;

}

int main (void)
{
char *dat = malloc(30);

int ret = punme((void**)&dat,40);

printf("punme returns %i\n",ret);

return 0;}
Heh, this looks familiar. :)
>My compiler (gcc -Wall -O3) tells me that "typepun.c:19: warning:
dereferencing type-punned pointer will break strict-aliasing rules".
The code works as expected, ie, prints that punme returns 0.

Who is the idiot? Me or the compiler?
You (cast) and assign a char ** to a void **. void ** is not like void
*.
Fixed your code:
>static int punme(void* dat,size_t newsize) { void **tmp = dat;
void *newdat = realloc(*tmp, newsize);
This doesn't fix the problem. This merely reorganises the code in a form
that the compiler may happen to not warn about.

The problem is that dat is defined as char *. You can't pretend it's
defined as a void *. The language doesn't let you. A fixed version looks
like

#include <stdlib.h>
#include <stdio.h>

static void *punme(void *dat, size_t newsize) {
return realloc(dat, newsize);
}

int main (void)
{
char *dat = malloc(30);
char *newdat = punme(dat, 40); /* or call realloc directly */
if (newdat != NULL) dat = newdat;
printf("punme would have returned %d\n", (newdat == NULL ? 1 : 0));
return 0;
}
Jun 27 '08 #3
j.*********@gmail.com wrote:
Hi all

I have a program which, with inessential details removed,
looks like this:

--
#include <stdlib.h>
#include <stdio.h>

static int punme(void** dat,size_t newsize)
{
void *newdat = realloc(*dat,newsize);

if (! newdat) return 1;

*dat = newdat;

return 0;
}

int main (void)
{
char *dat = malloc(30);

int ret = punme((void**)&dat,40);

printf("punme returns %i\n",ret);

return 0;
}
--

My compiler (gcc -Wall -O3) tells me that "typepun.c:19:
warning: dereferencing type-punned pointer will break
strict-aliasing rules". The code works as expected, ie,
prints that punme returns 0.

Who is the idiot? Me or the compiler?
...
You perform memory reinterpretation in your code. You have an lvalue
'dat' of type 'char*', which is reinterpreted as an lvalue of type
'void*' by using a conversion followed by a dereference '*(void**)
&dat'. In general, accessing the lvalue obtained by such
reinterpretation causes undefined behavior in C, unless the types are
"similar enough". In strict-aliasing mode (implied by -O3) GCC assumes
that such reinterpretations are not performed in the code. This is why
it issues the warning.

--
Best regards,
Andrey Tarasevich
Jun 27 '08 #4
Hi
This doesn't fix the problem. This merely reorganises the code in a form
that the compiler may happen to not warn about.

The problem is that dat is defined as char *. You can't pretend it's
defined as a void *. The language doesn't let you.
are you saying that

---
int foo(void* bar)
{
char *st = (char*)bar;
:
}

:
:

char *bar;
:

foo((void*)bar);
---

is not possible in C?

Are we using the same language?
A fixed version looks
like

#include <stdlib.h>
#include <stdio.h>

static void *punme(void *dat, size_t newsize) {
return realloc(dat, newsize);

}

int main (void)
{
char *dat = malloc(30);
char *newdat = punme(dat, 40); /* or call realloc directly */
if (newdat != NULL) dat = newdat;
printf("punme would have returned %d\n", (newdat == NULL ? 1 : 0));
return 0;

}
Part of the "inessential detail" omitted is that
I use the return value of punme() for other purposes,
I specifically want to modify dat (char*) which I pass
to punme() by reference.

Is there really no way at all to modify a generic
pointer by passing a reference to it to a function?
Are C programmers condemned to return structs
(one member of which is the void* modified) whenever
we want to do generic programming??

Thanks!
Jun 27 '08 #5
j.*********@gmail.com wrote:
are you saying that

---
int foo(void* bar)
{
char *st = (char*)bar;
:
}

:
:

char *bar;
:

foo((void*)bar);
---

is not possible in C?
This is not even remotely the same thing as your original code. This
version uses _conversion_. Your original version performed memory
reinterpretation.

Compare the following two examples:

Let's assume that sizeof(float) is the same as sizeof(int)

float f = 1.0;
int i;

Example 1. Conversion

i = (int) f;
/* Here we can expect i to be equal to 1 */
assert(i == 1);

Example 2. Memory reinterpretation

i = *(int*) &f;
/* Can we say anything about the value of i here? NO! */

The difference between these tow examples is exactly the difference
between your two code samples.
Is there really no way at all to modify a generic
pointer by passing a reference to it to a function?
No.

--
Best regards,
Andrey Tarasevich
Jun 27 '08 #6
On Tue, 06 May 2008 11:17:38 -0700, j.j.fishbat wrote:
Is there really no way at all to modify a generic pointer by passing a
reference to it to a function?
No, there's really no way to do that, any more than it's possible to
modify a generic integer (char, short, int, long) by passing a reference
to it to a function. It's obvious that that won't work on most systems
with integers, because different integer types are represented
differently. When you consider that C allows different pointer types to be
represented differently too (even though most implementations don't do
so), I hope you can see why what you're asking for is not possible.
Jun 27 '08 #7
j.*********@gmail.com wrote:
[...]
The problem is that dat is defined as char *. You can't pretend it's
defined as a void *. The language doesn't let you.

are you saying that

---
int foo(void* bar)
{
[...]
:
char *bar;
:
foo((void*)bar);
---

is not possible in C?
That's not the same thing. In your original post, you had:

static int punme(void **dat,size_t newsize)
...
char *dat = malloc(30);
int ret = punme((void **)&dat,40);

Here, you are telling punme that is gets passed a pointer to "void *"
but you are passing a pointer to "char *". Note "pointer to" in both
of those pieces.

[...]
Is there really no way at all to modify a generic
pointer by passing a reference to it to a function?
Are C programmers condemned to return structs
(one member of which is the void* modified) whenever
we want to do generic programming??
But you are not modifying a "generic pointer". Or, at least, you
are not _passing_ a "generic pointer".

Consider the fact that a "void *" and a "char *" need not be stored
the same way. Yes, a function can take a "void *" and you can pass
it a "char *", but that is because the value will be converted to a
"void *" before being passed. However, what you are trying to do is
pass a pointer to "char *", while a pointer to "void *" is expected.
No conversion of your original "char *" will take place. This is
the same as if you had:

void foo(double *pt)
...
float f;
foo((double *)&f);

In your case, the program "works" because, in all likelihood, the
representation of "void *" and "char *" are the same.

--
+-------------------------+--------------------+-----------------------+
| Kenneth J. Brody | www.hvcomputer.com | #include |
| kenbrody/at\spamcop.net | www.fptech.com | <std_disclaimer.h|
+-------------------------+--------------------+-----------------------+
Don't e-mail me at: <mailto:Th*************@gmail.com>

Jun 27 '08 #8
On Tue, 06 May 2008 14:34:57 -0400, Kenneth Brody wrote:
Consider the fact that a "void *" and a "char *" need not be stored the
same way.
Actually, void * and char * are special in that they must be stored the
same way. It may be better to pretend they too might be stored
differently, as you still aren't allowed to access one as the other anyway
(except in special circumstances), and this guarantee does not extend to
other pointer types, but I'm mentioning it for completeness.
Jun 27 '08 #9

Hi all
Consider the fact that a "void *" and a "char *" need not be stored
the same way.
OK, I am the idiot then. I had thought, for the past 10 years
C programming, that a pointer to char was the same as a
pointer to int was the same as a pointer to foobar_t, the only
difference being what it pointed to. And that this fact enables
one to cast to void* to enable generic programming.

I see I'll have to rewrite some code.

Thank you again to all who replied!

Jim
Jun 27 '08 #10
Andrey Tarasevich <an**************@hotmail.comwrote:
>
You perform memory reinterpretation in your code. You have an lvalue
'dat' of type 'char*', which is reinterpreted as an lvalue of type
'void*' by using a conversion followed by a dereference '*(void**)
&dat'. In general, accessing the lvalue obtained by such
reinterpretation causes undefined behavior in C, unless the types are
"similar enough".
In particular, (char *) and (void *) are required to have the same
representation and alignment requirements, so reinterpretation is almost
(but not quite) guaranteed to work in that case. The same is not true
of other pointer types, however, and reinterpretation involving other
pointer types *does* fail on some implementations.

-- Larry Jones

Apparently I was misinformed. -- Calvin
Jun 27 '08 #11
j.*********@gmail.com wrote:
>
Hi all
Consider the fact that a "void *" and a "char *" need not be stored
the same way.

OK, I am the idiot then. I had thought, for the past 10 years
C programming, that a pointer to char was the same as a
pointer to int was the same as a pointer to foobar_t, the only
difference being what it pointed to. And that this fact enables
one to cast to void* to enable generic programming.

I see I'll have to rewrite some code.

Thank you again to all who replied!
Well, you have probably been programming on a platform on which all
of the data pointer types are represented the same way. (And, I
must confess, so have I.) However, C does not guarantee such a
thing, and I believe others have posted here examples of systems
in which they are, in fact, represented differently.

Yes, you can convert from any pointer to "void *" and back again.
But, you can't, for example, convert from "int *" to "void *" and
then to "long *", and dereference that pointer without invoking
UB. (In fact, the converting to "long *" step itself may be UB.)

Note, too, that Mr. van D?k (sorry about the mangling of the name,
but my newsreader is obviously not UTF-8 compliant) says that the
standard does say that "void *" and "char *" are special cases
which must be represented the same way. If that is true, then
your original example will "work" as-is, but only because you
are converting "char **" to "void **". It's not guaranteed to
work with, for example, "int **" to "void **", in a "portable"
manner. Again, it will probably "work" on your systems, because
they are likely to be storing "void *" and "int *" in the same
representation.

--
+-------------------------+--------------------+-----------------------+
| Kenneth J. Brody | www.hvcomputer.com | #include |
| kenbrody/at\spamcop.net | www.fptech.com | <std_disclaimer.h|
+-------------------------+--------------------+-----------------------+
Don't e-mail me at: <mailto:Th*************@gmail.com>

Jun 27 '08 #12
On Tue, 06 May 2008 16:26:19 -0400, Kenneth Brody wrote:
Note, too, that Mr. van D?k (sorry about the mangling of the name, but
my newsreader is obviously not UTF-8 compliant)
<OTIt could be that your system font simply doesn't contain the
character, but the newsreader still is UTF-8 compliant. :) </OT>
says that the standard
does say that "void *" and "char *" are special cases which must be
represented the same way. If that is true,
6.2.5p27:
"A pointer to void shall have the same representation and alignment
requirements as a pointer to a character type."
then your original example
will "work" as-is, but only because you are converting "char **" to
"void **".
No, and I had tried to make that explicit. I wrote "you still aren't
allowed to access one as the other anyway (except in special
circumstances)". Those special circumstances would be using memcpy or an
union to copy between distinct pointer types. Using *(char **) &p when p
is defined as void *p, or vice versa, is not one of those special
circumstances, and when you do that, the behaviour is still undefined,
just like the behaviour is undefined if you use *(int **) &p on systems
where int * has the same representation and alignment requirements as
void *.
Jun 27 '08 #13
j.*********@gmail.com writes:
Is there really no way at all to modify a generic
pointer by passing a reference to it to a function?
Two people have said "no" here and that is because they have, quote
reasonably, taken this phrase in the context of your previous example.
However, I think you can do what you ask, provided you stick to the
narrowest interpretation of the quote above. (If not, I will get shot
down by the experts and we will both be wiser.)

What you can't do is leave the safety guarantees provided by the void
* type. So, you /can/ do this (example only, I don't recommend it!):

int my_realloc(void **ptr, size_t sz)
{
void *newp = realloc(*ptr, sz);
if (newp == NULL) {
free(*ptr);
return 0;
}
*ptr = newp;
return 1;
}
later...

char *sp = malloc(20);
void *tmp;
...
tmp = sp;
if (!my_realloc(&tmp, 30))
printf("Failed!\n");
else sp = tmp;
...

and you can do the same even for the types that do not explicitly use
the same representation: int *, or struct pointers, etc. The
assignments to a void * object make it work in a portable way.

This may not be close enough to what you want to be of any use, but I
hope it clarifies some things.

--
Ben.
Jun 27 '08 #14
Harald van D?k wrote:
On Tue, 06 May 2008 16:26:19 -0400, Kenneth Brody wrote:
Note, too, that Mr. van D?k (sorry about the mangling of the name,
but my newsreader is obviously not UTF-8 compliant)

<OTIt could be that your system font simply doesn't contain the
character, but the newsreader still is UTF-8 compliant. :) </OT>
It's a char set problem. Here's the header from his message:

Content-Type: text/plain; charset=us-ascii


Brian
Jun 27 '08 #15

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

Similar topics

21
4487
by: Batista, Facundo | last post by:
Here I send it. Suggestions and all kinds of recomendations are more than welcomed. If it all goes ok, it'll be a PEP when I finish writing/modifying the code. Thank you. .. Facundo
6
2672
by: S.Tobias | last post by:
I'm trying to understand how structure type completion works. # A structure or union type of unknown # content (as described in 6.7.2.3) is an incomplete type. It # is ...
0
1763
by: Chris Fink | last post by:
When I am consuming a webservice, an object has an undefined value (inq3Type.Call3Data). I do not completely understand why this is happening and apologize for the vague question. My assumption...
1
8683
by: Rob Griffiths | last post by:
Can anyone explain to me the difference between an element type and a component type? In the java literature, arrays are said to have component types, whereas collections from the Collections...
669
25375
by: Xah Lee | last post by:
in March, i posted a essay “What is Expressiveness in a Computer Language”, archived at: http://xahlee.org/perl-python/what_is_expresiveness.html I was informed then that there is a academic...
3
2813
by: john | last post by:
Hi to All To demonstrate: public class MyBaseGenericClass<T> { } public class MyGenericClass1<T: MyBaseGenericClass<T> {
7
7794
by: Sky | last post by:
I have been looking for a more powerful version of GetType(string) that will find the Type no matter what, and will work even if only supplied "{TypeName}", not the full "{TypeName},{AssemblyName}"...
9
3844
by: weirdwoolly | last post by:
Hopefully someone will be able to help. I have written a stored procedure in C++ called from a Java test harness to validate the graphic data types in C++ and their use. I have declared the...
5
3153
by: JH | last post by:
Hi I found that a type/class are both a subclass and a instance of base type "object". It conflicts to my understanding that: 1.) a type/class object is created from class statement 2.) a...
3
17085
by: amanjsingh | last post by:
Hi, I am trying to implement Java Web Service using Apache Axis2 and Eclipse as a tool. I have created the basic code and deployed the service using various eclipse plugin but when I try to invoke...
0
7112
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
7146
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
7183
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
0
7356
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...
0
5448
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development projectplanning, coding, testing,...
0
4573
by: conductexam | last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and...
0
3084
by: TSSRALBI | last post by:
Hello I'm a network technician in training and I need your help. I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs. The...
1
628
muto222
by: muto222 | last post by:
How can i add a mobile payment intergratation into php mysql website.
0
277
bsmnconsultancy
by: bsmnconsultancy | last post by:
In today's digital era, a well-designed website is crucial for businesses looking to succeed. Whether you're a small business owner or a large corporation in Toronto, having a strong online presence...

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.