Home > Archive > C > August 2004 > cast-as-lvalue
You are viewing an archived Text-only version of the thread.
To view this thread in it's original format and/or if you want to reply to
this thread please [click here]
|
|
| Michael Baehr 2004-08-23, 8:55 pm |
| I recently upgraded my Arch Linux system to GCC 3.4, and found out that a
previously accepted behavior (cast-as-lvalue) is now marked as deprecated,
and will cease to function in GCC 3.5. This has caused several builds to
break, most notably elfutils.
Presumably this behavior is not part of the C standard and it is thus
being excised like many other nonstandard GCC extensions. Regardless, my
question is not about the specifics or politics of GCC or any compiler.
Much of the code I'm trying to fix involves things like the following:
*((some_type *) ptr)++;
The desired behavior in this case is to increment the pointer as if it
were a (some_type *). In some cases, the pointer is typed as void, in
other cases it has a specific type, but regardless the compiler needs to
treat it as a different type when it is incrementing it.
Now I personally think cast-as-lvalue is a very logical way to attack this
problem, but apparently enough don't that it's been deprecated. My
question, then, is how to get the same exact behavior, but within the C
spec?
My current instinct is to do something as follows.
Assume the current code is:
foo *ptr;
*((bar *) ptr)++;
I would do the following:
foo *ptr;
bar *temp = (bar *)ptr;
*temp++;
ptr = (foo *)temp;
Is there any better way to do this? This seems like quite a bit of extra
code to avoid what (at least to me) looks like the logical way of
expressing this behavior.
-- Mike
| |
| Arthur J. O'Dwyer 2004-08-23, 8:55 pm |
|
On Mon, 23 Aug 2004, Michael Baehr wrote:
>
> The desired behavior in this case is to increment the pointer as if it
> were a (some_type *). In some cases, the pointer is typed as void, in
> other cases it has a specific type, but regardless the compiler needs to
> treat it as a different type when it is incrementing it.
[not valid C code]
> foo *ptr;
> *((bar *) ptr)++;
[valid C code]
> foo *ptr;
> bar *temp = (bar *)ptr;
> *temp++;
> ptr = (foo *)temp;
This works, but you might as well compress it into one line and
lose the temporary:
foo *ptr;
ptr = (foo *)((bar *)ptr + 1);
Note that if 'foo' is 'void', you can drop the redundant cast, too:
void *ptr;
ptr = (bar *)ptr + 1;
HTH,
-Arthur
| |
| Jarno A Wuolijoki 2004-08-23, 8:55 pm |
| On Mon, 23 Aug 2004, Michael Baehr wrote:
> I recently upgraded my Arch Linux system to GCC 3.4, and found out that a
> previously accepted behavior (cast-as-lvalue) is now marked as deprecated,
> and will cease to function in GCC 3.5. This has caused several builds to
> break, most notably elfutils.
>
> Presumably this behavior is not part of the C standard and it is thus
> being excised like many other nonstandard GCC extensions. Regardless, my
> question is not about the specifics or politics of GCC or any compiler.
>
> Much of the code I'm trying to fix involves things like the following:
>
> *((some_type *) ptr)++;
This isn't cast-as-lvalue but a normal cast. Standard C, though the
result is often undefined.
AFAIK, the equivalent cast-as-lvalue would be:
((some_type)*ptr)++;
| |
| Jarno A Wuolijoki 2004-08-23, 8:55 pm |
| On Tue, 24 Aug 2004, Jarno A Wuolijoki wrote:
> On Mon, 23 Aug 2004, Michael Baehr wrote:
>
>
> This isn't cast-as-lvalue but a normal cast. Standard C, though the
> result is often undefined.
I re-engage my brain and take my word back:
*((some_type *) ptr)++; != (*((some_type *) ptr))++;
Sigh. Why is it that I always hit send before catching these details..
| |
| Richard Tobin 2004-08-23, 8:55 pm |
| In article <pan.2004.08.23.22.21.51.353227@spamblocked.com>,
Michael Baehr <usemike@spamblocked.com> wrote:
>*((some_type *) ptr)++;
As you note, this is not standard C (it was present in early C89
drafts, but removed). On machines where pointers to all types look
the same, declaring a union of pointer types is likely to work:
union {some_type *st; other_type *ot;} foo;
*foo.st++;
-- Richard
| |
| Michael Baehr 2004-08-23, 8:55 pm |
| On Tue, 24 Aug 2004 00:51:57 +0300, Jarno A Wuolijoki wrote:
> I re-engage my brain and take my word back:
> *((some_type *) ptr)++; != (*((some_type *) ptr))++;
>
> Sigh. Why is it that I always hit send before catching these details..
I think it's one of those unwritten laws of USENET.
Cheers ;)
-- Mike
| |
| SM Ryan 2004-08-24, 4:34 pm |
| # Much of the code I'm trying to fix involves things like the following:
#
# *((some_type *) ptr)++;
#
# The desired behavior in this case is to increment the pointer as if it
# were a (some_type *). In some cases, the pointer is typed as void, in
# other cases it has a specific type, but regardless the compiler needs to
# treat it as a different type when it is incrementing it.
You can get a lvalue by judicious use of & and *.
lvalue ref char s
ref ref char (&s)
ref ref int (int**)(&s)
lvalue ref int *((int**)(&s))
(cd /tmp
rm a.out
cat >x.c <<':eof'
#include <stdio.h>
#include <stdlib.h>
#define lvaluecast(type,lvalue) (*((type*)((void*)(&(lvalue)))))
int main(int N,char **P) {
char *s = malloc(27),*s0; int v;
strcpy(s,"abcdefghijklmnopqrstuvwxyz");
s0 = s;
v = *lvaluecast(int*,s)++;
printf("s0=%p s=%p %d v=%x\n",s0,s,s-s0,v);
return 0;
}
:eof
cc x.c
a.out)
s0=0x300140 s=0x300144 4 v=61626364
--
SM Ryan http://www.rawbw.com/~wyrmwif/
Raining down sulphur is like an endurance trial, man. Genocide is the
most exhausting activity one can engage in. Next to soccer.
| |
| Arthur J. O'Dwyer 2004-08-24, 4:34 pm |
|
On Tue, 24 Aug 2004, SM Ryan wrote:
[Someone else wrote, I assume:]
> # Much of the code I'm trying to fix involves things like the following:
> #
> # *((some_type *) ptr)++;
> #
> # The desired behavior in this case is to increment the pointer as if it
> # were a (some_type *). In some cases, the pointer is typed as void, in
> # other cases it has a specific type, but regardless the compiler needs to
> # treat it as a different type when it is incrementing it.
SM Ryan: Please don't use # as a "quoting" mechanism, because it's
annoying, it's hard to read and it breaks many line-wrapping programs.
(Also, please endeavor not to tell untruths to newbies, but I'll give
you the benefit of the doubt this time. See below.)
> You can get a lvalue by judicious use of & and *.
>
> lvalue ref char s
> ref ref char (&s)
> ref ref int (int**)(&s)
> lvalue ref int *((int**)(&s))
Undefined behavior. You can tell it's suspect without even knowing
the Standard, because I could drop one line in your "derivation" and come
up with the same answer where it's more obviously incorrect.
lvalue ref char s
ref ref int (int**)s
lvalue ref int *(int**)s
> (cd /tmp
> rm a.out
> cat >x.c <<':eof'
> #include <stdio.h>
> #include <stdlib.h>
>
> #define lvaluecast(type,lvalue) (*((type*)((void*)(&(lvalue)))))
Overly complicated
#define lvaluecast(type,lvalue) (*(type*)&(lvalue))
and insufficiently general
lvaluecast(void(*)(), fnptr) = bar;
and flat-out wrong.
assert(sizeof(char) != sizeof(int));
lvaluecast(int, mychar[0]) = 255;
printf("%d\n", mychar[1]);
> int main(int N,char **P) {
> char *s = malloc(27),*s0; int v;
> strcpy(s,"abcdefghijklmnopqrstuvwxyz");
> s0 = s;
> v = *lvaluecast(int*,s)++;
Here is the crash. If sizeof(char*)!=sizeof(int*), you are
overwriting memory you don't own. If the layouts of the two
types are different, you are causing serious Bad Stuff. No
matter what, this code is dangerous.
> printf("s0=%p s=%p %d v=%x\n",s0,s,s-s0,v);
And just for the record:
printf("s0=%p s=%p %d v=%x\n",(void*)s0,(void*)s,s-s0,(unsigned)v);
although some people think the first two casts aren't needed when 's0'
and 's' are pointers to 'char'. I put them in anyway.
> return 0;
> }
-Arthur
| |
| Richard Tobin 2004-08-26, 8:55 pm |
| In article <pan.2004.08.23.22.21.51.353227@spamblocked.com>,
Michael Baehr <usemike@spamblocked.com> wrote:
>*((some_type *) ptr)++;
As you note, this is not standard C (it was present in early C89
drafts, but removed). On machines where pointers to all types look
the same, declaring a union of pointer types is likely to work:
union {some_type *st; other_type *ot;} foo;
*foo.st++;
-- Richard
| |
| Arthur J. O'Dwyer 2004-08-27, 3:55 pm |
|
On Tue, 24 Aug 2004, SM Ryan wrote:
[Someone else wrote, I assume:]
> # Much of the code I'm trying to fix involves things like the following:
> #
> # *((some_type *) ptr)++;
> #
> # The desired behavior in this case is to increment the pointer as if it
> # were a (some_type *). In some cases, the pointer is typed as void, in
> # other cases it has a specific type, but regardless the compiler needs to
> # treat it as a different type when it is incrementing it.
SM Ryan: Please don't use # as a "quoting" mechanism, because it's
annoying, it's hard to read and it breaks many line-wrapping programs.
(Also, please endeavor not to tell untruths to newbies, but I'll give
you the benefit of the doubt this time. See below.)
> You can get a lvalue by judicious use of & and *.
>
> lvalue ref char s
> ref ref char (&s)
> ref ref int (int**)(&s)
> lvalue ref int *((int**)(&s))
Undefined behavior. You can tell it's suspect without even knowing
the Standard, because I could drop one line in your "derivation" and come
up with the same answer where it's more obviously incorrect.
lvalue ref char s
ref ref int (int**)s
lvalue ref int *(int**)s
> (cd /tmp
> rm a.out
> cat >x.c <<':eof'
> #include <stdio.h>
> #include <stdlib.h>
>
> #define lvaluecast(type,lvalue) (*((type*)((void*)(&(lvalue)))))
Overly complicated
#define lvaluecast(type,lvalue) (*(type*)&(lvalue))
and insufficiently general
lvaluecast(void(*)(), fnptr) = bar;
and flat-out wrong.
assert(sizeof(char) != sizeof(int));
lvaluecast(int, mychar[0]) = 255;
printf("%d\n", mychar[1]);
> int main(int N,char **P) {
> char *s = malloc(27),*s0; int v;
> strcpy(s,"abcdefghijklmnopqrstuvwxyz");
> s0 = s;
> v = *lvaluecast(int*,s)++;
Here is the crash. If sizeof(char*)!=sizeof(int*), you are
overwriting memory you don't own. If the layouts of the two
types are different, you are causing serious Bad Stuff. No
matter what, this code is dangerous.
> printf("s0=%p s=%p %d v=%x\n",s0,s,s-s0,v);
And just for the record:
printf("s0=%p s=%p %d v=%x\n",(void*)s0,(void*)s,s-s0,(unsigned)v);
although some people think the first two casts aren't needed when 's0'
and 's' are pointers to 'char'. I put them in anyway.
> return 0;
> }
-Arthur
|
|
|
|
|