In the good old days, any self-respecting C compiler knew what you
meant when you did arithmetic on a void * pointer, i.e. that the units
were bytes. Then someone decided that we would all be more productive
somehow if the compilers couldn't figure out obvious things like that
on their own. So now you can't do arithmetic on void * pointers.
Ok, fine. So I adjusted, I would do this:
void *buf;
int n;
(char *)buf += n;
But now I'm getting nastygrams like "warning: target of assignment not
really an lvalue; this will be a hard error in the future". Once again
someone, probably the same person, decided that compilers shouldn't
have to burden this great responsibility of understanding obvious
things like that. (And why a cast of a pointer to another pointer type
is no longer a pointer, I have no idea.)
So now what am I supposed to do? It starts to get just downright silly. E.g.
void *buf;
int n;
char *cmon;
cmon = (char *)buf;
cmon += n;
buf = (void *)cmon;
Is there a better way to work around this, at least until the *next*
dumbing down of the language?
Mark
Is void a data type?
Does void foo(); return a byte?
Does void a; define a byte?
Nope.
>
> Ok, fine. So I adjusted, I would do this:
>
> void *buf;
> int n;
>
> (char *)buf += n;
>
> But now I'm getting nastygrams like "warning: target of assignment not
> really an lvalue; this will be a hard error in the future". Once again
> someone, probably the same person, decided that compilers shouldn't have
> to burden this great responsibility of understanding obvious things like
> that. (And why a cast of a pointer to another pointer type is no longer a
> pointer, I have no idea.)
It is a pointer but an r-value not an l-value.
>
> So now what am I supposed to do? It starts to get just downright silly.
> E.g.
>
> void *buf;
> int n;
> char *cmon;
>
> cmon = (char *)buf;
> cmon += n;
> buf = (void *)cmon;
>
> Is there a better way to work around this, at least until the *next*
> dumbing down of the language?
Instead of using void * for buf, why not use char *?
Dennis
Arithmetic has *never* been permitted on void* by standard C. Prior
to the C89 standard, void* did not exist.
gcc does permit arithmetic on void* as an extension. It does so by
the kludge of pretending that sizeof(void) is 1.
> Ok, fine. So I adjusted, I would do this:
>
> void *buf;
> int n;
>
> (char *)buf += n;
>
> But now I'm getting nastygrams like "warning: target of assignment not
> really an lvalue; this will be a hard error in the future". Once
> again someone, probably the same person, decided that compilers
> shouldn't have to burden this great responsibility of understanding
> obvious things like that. (And why a cast of a pointer to another
> pointer type is no longer a pointer, I have no idea.)
Of course it's still a pointer; it's just not an lvalue. A pointer
cast results in a pointer value, not a pointer object. It's the same
with any other cast.
Similarly:
int n;
n = 42; /* ok */
(double)n = 42; /* nope */
(n + 1) = 42; /* nope */
> So now what am I supposed to do? It starts to get just downright
> silly. E.g.
>
> void *buf;
> int n;
> char *cmon;
>
> cmon = (char *)buf;
> cmon += n;
> buf = (void *)cmon;
>
> Is there a better way to work around this, at least until the *next*
> dumbing down of the language?
There as been no dumbing down of the language; this aspect hasn't
changed since void* was introduced by ANSI in 1989.
You can just use gcc with command-line options that don't tell it
to disable this extension.
If you want to advance a void* so it points n bytes later in memory,
you can do this:
buf = (char*)buf + n;
(note that the char* result is implicitly converted back to void*).
One advantage if this is that you can advance by n ints rather than n
chars if you want to.
Or, if you know you're going to be performing byte-oriented arithmetic
on a pointer object, you can just declare it as a char* (or unsigned
char*) in the first place.
--
Keith Thompson (The_Other_Keith) ks...@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
> In the good old days, any self-respecting C compiler knew what you
> meant when you did arithmetic on a void * pointer, i.e. that the units
> were bytes. Then someone decided that we would all be more productive
> somehow if the compilers couldn't figure out obvious things like that
> on their own. So now you can't do arithmetic on void * pointers.
I don't recall such a time but maybe I've forgotten. void (and hence
void *) came into C along with the restriction on doing arithmetic on
such pointers about 20 years ago. There was a short time when there
were unofficial compilers with void * support that could do what them
liked with them (because there was no standard) but as soon as C was
standardised, so was this (to me quite reasonable) restriction.
> Ok, fine. So I adjusted, I would do this:
>
> void *buf;
> int n;
>
> (char *)buf += n;
That seems like the wrong adjustment. If the buf is byte buffer, why
is the pointer not a char * to start with?
> But now I'm getting nastygrams like "warning: target of assignment not
> really an lvalue; this will be a hard error in the future". Once
> again someone, probably the same person, decided that compilers
> shouldn't have to burden this great responsibility of understanding
> obvious things like that. (And why a cast of a pointer to another
> pointer type is no longer a pointer, I have no idea.)
That's no what it is saying. It is saying that a cast expression is
not the kind of thing you can assign to (that's roughly what an lvalue
is). You might get a similar warning if you wrote +x = 42;
> So now what am I supposed to do? It starts to get just downright silly. E.g.
You should write:
buf = (char *)buf + n;
> void *buf;
> int n;
> char *cmon;
>
> cmon = (char *)buf;
> cmon += n;
> buf = (void *)cmon;
That's overly complex.
> Is there a better way to work around this, at least until the *next*
> dumbing down of the language?
Personally, I like these rules. I think the smarten up the language
rather than dumb it down.
--
Ben.
> void *buf;
> int n;
>
> (char *)buf += n;
buf = (char *)buf + n;
--
pete
> Helpful C swamis,
>
> In the good old days, any self-respecting C compiler knew what you meant
> when you did arithmetic on a void * pointer, i.e. that the units were
> bytes. Then someone decided that we would all be more productive
> somehow if the compilers couldn't figure out obvious things like that on
> their own. So now you can't do arithmetic on void * pointers.
I've been programming in C since before the "void" type was invented, and I
don't think I've ever encountered a compiler that would do arithmetic on
(void *) objects. Since arithmetic on pointers is defined in terms of the
size of the object pointed to, why would you assume that sizeof(void) == 1?
>
> Ok, fine. So I adjusted, I would do this:
>
> void *buf;
> int n;
>
> (char *)buf += n;
Which has also never (in my experience) been legal. The result of a cast
expression is not something you can assign to, because it's a value
CONVERTED
TO A DIFFERENT TYPE, which in most cases one wouldn't expect to be a type
that's sufficiently compatible with the actual type of the original object
to make assignment reasonable.
There's a perfectly reasonable way to do this, which is
buf = (char *)buf + n;
which says exactly what you mean: "convert buf to a (char *), then add n
to that (char *), and assign the result back to buf".
This method has the advantage that it works equally well if the type
you're working with is anything OTHER than char, i.e. if (type *) is NOT
guaranteed
to have the same size and representation as (char *). E.g.
buf = (int *)buf + n;
or
buf = (struct foo *)buf + n;
--
Morris Keesan -- mke...@post.harvard.edu
I wasn't talking about the standard. I was talking about the compilers.
> There as been no dumbing down of the language;
You're correct. I meant dumbing down of the compilers to agree with
the religious extremism of the standard.
> If you want to advance a void* so it points n bytes later in memory,
> you can do this:
> buf = (char*)buf + n;
Thank you. That's what I'll do. Of course, at the loss of the
expessiveness and readability of the wonderful += operator of C.
On 2010-01-08 19:23:24 -0800, Dennis \(Icarus\) said:
> Is void a data type?
> Does void foo(); return a byte?
> Does void a; define a byte?
Is void * a pointer? Yes.
If I add n to the register that pointer is in, does it go up by n? Yes.
Isn't that what you'd expect? Yes.
Anyway, I get the concept that void * deliberately makes the size of
the type ambiguous, so in principle arithmetic on it should be
undefined. On the other hand, the standard could make life easier and
still entirely self-consistent if it simply said that arithmetic on
void * pointers treats the size as 1. Why make a useful expression
illegal when instead it can mean what everyone expects it to mean?
>> (And why a cast of a pointer to another pointer type is no longer a
>> pointer, I have no idea.)
>
> It is a pointer but an r-value not an l-value.
Why is it not an l-value? I can see no ambiguity whatsoever about what
it means as an l-value.
Mark
And compilers generally follow the standard. You may check to see if there's
a mode on te cpmpiler you're using that allows it as an extension.
Do you have an option like "disable language extensions" set?
>
>> There as been no dumbing down of the language;
>
> You're correct. I meant dumbing down of the compilers to agree with the
> religious extremism of the standard.
The C language is defined b the standard. Do you want to program in C or
not?
>
>> If you want to advance a void* so it points n bytes later in memory,
>> you can do this:
>> buf = (char*)buf + n;
>
> Thank you. That's what I'll do. Of course, at the loss of the
> expessiveness and readability of the wonderful += operator of C.
>
> On 2010-01-08 19:23:24 -0800, Dennis \(Icarus\) said:
>> Is void a data type?
>> Does void foo(); return a byte?
>> Does void a; define a byte?
>
> Is void * a pointer? Yes.
Yes it is, and dereferencing it will cause problems.
> If I add n to the register that pointer is in, does it go up by n? Yes.
That's assembly though - not C.
> Isn't that what you'd expect? Yes.
If I was programming assembly, sure, but not if I was programming in C.
>
> Anyway, I get the concept that void * deliberately makes the size of the
> type ambiguous, so in principle arithmetic on it should be undefined. On
> the other hand, the standard could make life easier and still entirely
> self-consistent if it simply said that arithmetic on void * pointers
> treats the size as 1. Why make a useful expression illegal when instead
> it can mean what everyone expects it to mean?
>
>>> (And why a cast of a pointer to another pointer type is no longer a
>>> pointer, I have no idea.)
>>
>> It is a pointer but an r-value not an l-value.
>
> Why is it not an l-value? I can see no ambiguity whatsoever about what it
> means as an l-value.
Morris Keesan explains this nicely.
Dennis
Until recently, gcc -Wall didn't complain about that.
> which in most cases one wouldn't expect to be a type
> that's sufficiently compatible with the actual type of the original object
> to make assignment reasonable.
So what about the cases where assignment *is* reasonable? Like casting
a pointer?
> This method has the advantage that it works equally well if the type
> you're working with is anything OTHER than char, i.e. if (type *) is
> NOT guaranteed
> to have the same size and representation as (char *). E.g.
>
> buf = (int *)buf + n;
> or
> buf = (struct foo *)buf + n;
I don't see why we should have to lose the += operator in this case.
(struct foo *)buf += n looks to me like it should make perfect sense to
the compiler.
Mark
What is this in reference to?
Mark
Oh, I see -- another post here.
I wasn't convinced by the argument that because sometimes casting
results in something that's not an l-value, that you must then assume
that all casts cannot be an l-value.
Mark
That depends how big a "void" is, doesn't it?
> Isn't that what you'd expect? Yes.
Not really.
--
Ian Collins
My current copy of gcc (4.2.1) will, with all warnings enabled (-Wall)
is perfectly happy with (no warnings):
void *buf;
int n;
buf += n;
and generates code that does what apparently only an idiot like myself
would expect, which is to add n to the pointer address.
Mark
It sounds like gcc has been improved.
>> which in most cases one wouldn't expect to be a type
>> that's sufficiently compatible with the actual type of the original object
>> to make assignment reasonable.
>
> So what about the cases where assignment *is* reasonable? Like
> casting a pointer?
You find it reasonable; I don't.
A cast specifies a conversion, which takes a value as its operand and
yields a converted value of the specified type.
An lvalue designates an object. For the result of a cast to designate
an object, there has to be some object *of the specified type* for it
to designate. Since the operand of the cast was of a different type,
there is no such object.
Conceivably the language could specify that a cast yields an lvalue if
the source and target types both have the same representation (this
happens to be guaranteed for char* and void*). But in my opinion that
would be ugly and inconsistent. Would you want this:
long l;
(int)l = 42;
to compile if and only if int and long happen to be the same size?
Would you want this:
int *ptr;
(void*)ptr = NULL;
to work as "expected" on most systems, but fail to compile on systems
where int* and void* have different representations?
And would you want such a special-case rule just so you can use "+="?
>> This method has the advantage that it works equally well if the type
>> you're working with is anything OTHER than char, i.e. if (type *) is
>> NOT guaranteed
>> to have the same size and representation as (char *). E.g.
>>
>> buf = (int *)buf + n;
>> or
>> buf = (struct foo *)buf + n;
>
> I don't see why we should have to lose the += operator in this case.
> (struct foo *)buf += n looks to me like it should make perfect sense
> to the compiler.
But it doesn't.
You can fix that and keep your += operator:
*(char **)&buf += n;
Now there will be a large subthread arguing whether that's technically legal
(which must be what you wanted since you could have just declared buf as a
char * to begin with)
P.S.
I usually read usenet without paying much attention to who wrote which
article, trying to focus on content not personalities. But...
http://en.wikipedia.org/wiki/Mark_Adler? adler32, etc? and thinks void *
arithmetic should be acceptable? What a world!
Human sacrifice, dogs and cats living together, mass hysteria!
Mark
Sez you. If what you said was correct, then we should never be able to
cast a pointer to another pointer type, since that object it's pointing
to doesn't exist.
Of course, in reality the object certainly does exist. There's a
reason the pointer to it was sent as a void *. The only reason I can
see for the void type is to have void * pointers (you can't have void
n), and the reason for void * pointers is to be able to carry a pointer
to any data while hiding and/or not caring what it is exactly. An
example is a custom memory allocator that needs to be passed the
structure it is using to manage the memory. The structure details are
hidden in the allocation code -- all the allocator needs is the pointer
and then it knows how to find the stuff that's there. (Pre-object
objects.)
> Would you want this:
>
> long l;
> (int)l = 42;
No I would not want that. l is not a pointer.
> int *ptr;
> (void*)ptr = NULL;
>
> to work as "expected" on most systems, but fail to compile on systems
> where int* and void* have different representations?
Uh oh. Now I'm really worried, if having different, non-transferrable
data pointer representations is permitted by the C standard. I sure
hope you're wrong.
I certainly would expect the code above to work, though I can think of
no reason to do that since NULL is of the type void *, and so can be
assigned to other pointers without an explicit cast. It should just be
ptr = NULL. (At least that's what the compilers allow -- I hope that
doesn't get messed up too. I don't want to have to cast NULL every
time I use it.)
Anyway, I thought that the void * type *must* be able to be a vessel
for a pointer to any other data object. In fact, it has no other
reason to exist. If you cannot put an anydata * in a void * variable,
pass it along, and later convert it back to an anydata * and get the
anydata it's pointing to, then the value of void * is lost.
So is it not true that void * must be able to be a vessel for a pointer
to any data object?
Mark
The two pointer types could have different sizes, provided sizeof(void*)
>= sizeof(int).
The problem above is the cast. Assignment would be fine.
> Anyway, I thought that the void * type *must* be able to be a vessel for
> a pointer to any other data object. In fact, it has no other reason to
> exist. If you cannot put an anydata * in a void * variable, pass it
> along, and later convert it back to an anydata * and get the anydata
> it's pointing to, then the value of void * is lost.
>
> So is it not true that void * must be able to be a vessel for a pointer
> to any data object?
Yes, but that doesn't mean it has the same representation.
--
Ian Collins
Huh?
If you're going to snip context, can you please indicate that you're
doing so? Here's what I wrote:
An lvalue designates an object. For the result of a cast to
designate an object, there has to be some object *of the specified
type* for it to designate. Since the operand of the cast was of a
different type, there is no such object.
I'm not talking about the object (if any) to which the pointer points.
I'm talking about the pointer object itself.
For example, given:
void *vp = ...;
... (char*)vp ...
vp is an object of type void*. There is no object of type char*.
There is a *value* (not an lvalue) of type char*, the result of
converting the value of vp from void* to char*.
[...]
>> Would you want this:
>>
>> long l;
>> (int)l = 42;
>
> No I would not want that. l is not a pointer.
And why does that matter?
>> int *ptr;
>> (void*)ptr = NULL;
>>
>> to work as "expected" on most systems, but fail to compile on systems
>> where int* and void* have different representations?
>
> Uh oh. Now I'm really worried, if having different, non-transferrable
> data pointer representations is permitted by the C standard. I sure
> hope you're wrong.
I'm not. Different pointer types can have different representations.
Consider a system where a machine address points to, say, a
64-bit word, but the C implementation specifies an 8-bit byte.
Word pointers can be represented as machine addresses, but byte
pionters need an additional offset stored somewhere. (I've worked
on such a system.)
You might take a look at section 4 of the comp.lang.c FAQ,
<http://www.c-faq.com/>.
> I certainly would expect the code above to work, though I can think of
> no reason to do that since NULL is of the type void *, and so can be
> assigned to other pointers without an explicit cast. It should just
> be ptr = NULL. (At least that's what the compilers allow -- I hope
> that doesn't get messed up too. I don't want to have to cast NULL
> every time I use it.)
I just used NULL as a trivial example. The point isn't whether it's a
sensible thing to do, it's whether it's legal.
> Anyway, I thought that the void * type *must* be able to be a vessel
> for a pointer to any other data object. In fact, it has no other
> reason to exist. If you cannot put an anydata * in a void * variable,
> pass it along, and later convert it back to an anydata * and get the
> anydata it's pointing to, then the value of void * is lost.
>
> So is it not true that void * must be able to be a vessel for a
> pointer to any data object?
Depends on what you mean by "vessel".
A value of any pointer type (other than a pointer-to-function type)
may be converted to void* and back to the original type without
loss of information. The language specifically guarantees this.
But it's important to understand that conversions operate on values,
not on objects. There is no implication that an object of type void*
can be treated as if it were an object of some other pointer type
(except for the special guarantee that void* and char* have the
same representation). Conversion is not type-punning.
It's true that, on many systems, all pointer types have the same
representation, but the language does not make this assumption.
I think the use of the unadorned word "pointer" can lead to a great
deal of confusion. A pointer object and a pointer value are two
very different things.
It seems to me like you dislike the fact that C doesn't let you
treat pointer objects as if they were of different pointer types.
That's fine; there are certainly things I dislike about C (though
that's not one of them). But the language is the way it is.
You might even consider the possibility that there might be rational
reasons for for some of the decisions, even if you might not
agree with them.
And there are plenty of other languages -- including the C-like
dialect that gcc implements.
I've never seen anything but gcc do that. There was no time ever when
it was part of the spec, and I think gcc was wrong.
If you want a byte pointer, use one.
> Ok, fine. So I adjusted, I would do this:
>
> void *buf;
> int n;
>
> (char *)buf += n;
>
> But now I'm getting nastygrams like "warning: target of assignment not
> really an lvalue; this will be a hard error in the future". Once again
> someone, probably the same person, decided that compilers shouldn't
> have to burden this great responsibility of understanding obvious
> things like that. (And why a cast of a pointer to another pointer type
> is no longer a pointer, I have no idea.)
It is a pointer, but it isn't an lvalue.
"(type) x" is an expression of the same class as "x + 1" -- it is a derived
value. Obviously, you know what is meant by:
x + 1 = 3;
but C doesn't work that way.
> Is there a better way to work around this, at least until the *next*
> dumbing down of the language?
Your premise is wrong; neither of the things you propose was ever standard,
and I don't think either of them makes sense.
If I really want to do this, I do:
v = ((char *) v) + 3;
... Except I bet I don't, because if I want to do arithmetic on a pointer,
that's because I have a concept of what kind of things it points to. If
I really want to use a pointer-to-bytes, that's called "unsigned char *".
"void *" is for a typeless address, and since it has no type, there is no
meaningful size to increment it by when adding one...
-s
--
Copyright 2010, all wrongs reversed. Peter Seebach / usenet...@seebs.net
http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
Could you name a single compiler other than gcc that actually accepted
this? I can't.
> Is void * a pointer? Yes.
> If I add n to the register that pointer is in, does it go up by n? Yes.
> Isn't that what you'd expect? Yes.
Not really. I'd expect it to go up by n*sizeof(*ptr), and since I don't
know what size object it points to, I can't have an expectation
> Why make a useful expression
> illegal when instead it can mean what everyone expects it to mean?
First: Because not everyone expects that. Many people might, for
instance, expect "v += 3" to increment by three times the size of the
thing v points to. Whatever that is.
Second: Because in practice such code is nearly always buggy.
> Why is it not an l-value? I can see no ambiguity whatsoever about what
> it means as an l-value.
On some real-world systems, "(int *)" and "(void *)" have different
representations. It is not at all clear what you want.
int i = 3;
(double) i = 3.75;
What should i be?
In general, you can't meaningfully assign to a computed value, only to
an object.
You don't seem to have understood the difference between the pointer
and the thing pointed to.
When you convert a pointer to a new type, you may well create a new
value which is different from the value you converted.
> Uh oh. Now I'm really worried, if having different, non-transferrable
> data pointer representations is permitted by the C standard. I sure
> hope you're wrong.
No, he's not. They've been around forever and have been essential for some
high-performance systems.
> So is it not true that void * must be able to be a vessel for a pointer
> to any data object?
Of course it must.
But that means a conversion. And in some cases, the conversion actually changes
the bit contents or even SIZE of the pointer.
For example ...?
Mark
even heroes step in things sometimes it seems...
well, I usually define a 'byte' type (as 'unsigned char'), and use pointers
to this for when I need bytes, and then cast to/from 'void *'...
elsewhere in the thread:
yeah, I guess I will note that I like to use '()' for no-args functions even
being aware that it is not strictly correct.
I remembered recently somene wanting to disallow the ability to type:
int foo()
{
...
}
which was a position I personally somewhat disageed with.
in my compiler, I dropped old-style declarations (mostly as I didn't feel
much need to implement them for my uses), but just made '()' and '(void)' do
the same thing, which to me seemed a perfectly reasonable option. but, this
opened up a big argument...
personally, I did not see it as a "change" of the semantics, rather a
"narrowing".
not like I have not seen other projects using the same notation though.
so, I guess it is more about how strict everyone wants to be over these
matters...
to some, it is "all or nothing" even over the tiny details, and to others it
is a bit more fluid.
as I see it, "what works works..." (though there are limits here, given
matters such as "what is in good taste" and "what is the right thing to do",
which is not generally one-and-the-same as picking over the fine details,
even if there is a lot of overlap at times...).
> Mark
>
Intel's icc presumably does, but only because it's designed to be
compatible with gcc (the goal is to be able to use it to build the
Linux kernel).
gcc -pedantic:
foo.c:9: warning: pointer of type �void *� used in arithmetic
Perhaps it's only warning idiots like me? ;-)
-Beej
> On 01/08/2010 08:12 PM, Mark Adler wrote:
>> My current copy of gcc (4.2.1) will, with all warnings enabled (-Wall)
>> is perfectly happy with (no warnings):
>>
>> void *buf;
>> int n;
>>
>> buf += n;
>
> gcc -pedantic:
>
> foo.c:9: warning: pointer of type ‘void *’ used in arithmetic
>
> Perhaps it's only warning idiots like me? ;-)
Apparently -Wall really only means -Wsome.
Mark
Ok, I'm a little slow, but I get it now. My mental model of a data
pointer always being simply an address pointing to a byte, with no
conversion due to the type pointed to, is not universal. I now agree
that (char *)b += n should not be allowed. (Sigh. Of course, I would
prefer that the world match all of my simple mental models. It's so
annoying when it doesn't.)
On 2010-01-08 21:43:37 -0800, Seebs said:
> "void *" is for a typeless address, and since it has no type, there is no
> meaningful size to increment it by when adding one...
Now I can play the game! (Gets up on soap box ...) I assert that a
function that takes a void * pointer and a size_t length to identify a
thing makes no sense whatsoever in the C standard, since length has no
meaning with respect to the void * pointer. Yet there are many such
functions that are also part of the C standard, such as memcpy(),
write(), etc. They should all really be char *, since only then can
the length have meaning. Those functions are in a sense implying char
* through the meaning of the length, but presenting void * as the
argument instead, and are therefore lying. (I realize of course that
they do this for the convenience of not having to cast to (char *)
everytime you use one of those functions.)
I would argue that alternatively we could eliminate this mixed message
of void both implicitly having a size and not having a size by changing
the C standard to give void a size, i.e. one. sizeof(void) == 1.
Nothing would break -- this would just add to the things that can be
expressed, in a way that is consistent with how library functions use
void * arguments. (The void type would still not be allowed by itself,
and so function(void) would still mean no arguments.) In fact, in the
evil gcc, n = sizeof(void); sets n to 1, and it seems to work fine.
Mark
Well, write() isn't part of the C standard, but that's not really
relevant to your point.
The point is that in most such functions, the size is specified to be
in bytes. Which means that the implementation, assuming it's written
in C, has to convert the void* pointer to, say, unsigned char* to
operate on it.
> They should all really be char *, since only then can
> the length have meaning. Those functions are in a sense implying char
> * through the meaning of the length, but presenting void * as the
> argument instead, and are therefore lying. (I realize of course that
> they do this for the convenience of not having to cast to (char *)
> everytime you use one of those functions.)
I think "lying" overstates it. I don't dispute that the definition is
a trifle conceptually inconsistent, but it's not *that* bad.
> I would argue that alternatively we could eliminate this mixed message
> of void both implicitly having a size and not having a size by
> changing the C standard to give void a size, i.e. one. sizeof(void)
> == 1. Nothing would break -- this would just add to the things that
> can be expressed, in a way that is consistent with how library
> functions use void * arguments. (The void type would still not be
> allowed by itself, and so function(void) would still mean no
> arguments.) In fact, in the evil gcc, n = sizeof(void); sets n to 1,
> and it seems to work fine.
I've seen errors that gcc failed to catch because of this extension
(sorry, I don't remember the details).
It's too late to redesign the language, but ...
I think part of the problem is that C use char (and its signed and
unsigned variants) for three distinct things: character data, small
integers, and the smallest addressible unit of memory. Perhaps it
would have been better to have a distinct "byte" type, not compatible
with any of the character types. Then a lot of the mem*() functions
could sensibly take arguments of type byte*. Presumably you could
implicitly convert to and from byte* just as with void*.
On the other hand, the fact that you can deference byte* and perform
pointer arithmetic on it might be a problem as well. Sometimes you
might *want* a type that's just a raw pointer, but that doesn't let
you manipulate what it points to.
So would the language have been better with both byte* and void*, or
would that have introduced too much complexity? I can imagine the
flame wars about which type should be used in which circumstances.
In any case, I don't recall anyone claiming that C is a paragon of
perfection.
Nonsense, the size is the size of the data pointed to. What the data is
is irrelevant.
> Yet there are many such
> functions that are also part of the C standard, such as memcpy(),
> write(), etc. They should all really be char *, since only then can the
> length have meaning.
How so? the length is a length in bytes. I really think you either
don't understand the concept of void*, or you are being obtuse.
> Those functions are in a sense implying char *
> through the meaning of the length, but presenting void * as the argument
> instead, and are therefore lying. (I realize of course that they do
> this for the convenience of not having to cast to (char *) everytime you
> use one of those functions.)
Eh?
consider
int n;
write( &n, sizeof(n) );
> I would argue that alternatively we could eliminate this mixed message
> of void both implicitly having a size and not having a size by changing
> the C standard to give void a size, i.e. one. sizeof(void) == 1.
Nowhere does void implicitly have a size.
> Nothing would break -- this would just add to the things that can be
> expressed, in a way that is consistent with how library functions use
> void * arguments. (The void type would still not be allowed by itself,
> and so function(void) would still mean no arguments.) In fact, in the
> evil gcc, n = sizeof(void); sets n to 1, and it seems to work fine.
So nonsense like:
int n;
int *p = n;
...
void* v = p;
++v;
would become legal?
--
Ian Collins
Ok, fwrite(). fread(). mem*().
> So would the language have been better with both byte* and void*, or
> would that have introduced too much complexity?
I'd like that even better than sizeof(void) == 1, so long that the type
conversions to and from byte * are "free", as they are with void *, as
you suggested.
Mark
And therein lies the nub of his problem (I think). A void* is a generic
pointer to an object of unspecified type and so unknown size. The rules
of pointer arithmetic specify that the unit of change for incrementing
or decrementing a pointer is the size of the data type it is pointing
to. So what should be the value of ++v_ptr (where that is of type
void*). The compiler has no idea and when faced with that there are two
choices, refuse to attempt it (which is what the C Standard mandates) or
use a default (and early versions of GNU C did that)
Why did GNU C make that choice? Perhaps unwisely, to help with
converting pre ISO C code where char* was sometimes used as a generic
pointer. Incidentally that usage is probably why char* and void* were
required to have the same size and layout.
Well, as far as I can tell, it really means "ALL the warnings the gcc
development team think might be handy" - i.e. hardly any. When writing
portable C, at a bare minimum you want -W -Wall -ansi -pedantic
--
Richard Heathfield <http://www.cpax.org.uk>
Email: -http://www. +rjh@
"Usenet is a strange place" - dmr 29 July 1999
Sig line vacant - apply within
int i = 0;
void * v_ptr = &i;
++v_ptr; // on most machines that is no longer the address of
// an int unless sizeof(int) = 1
I much prefer my compiler to baulk at that last line. And I suspect that
the (overwhelming) majority of C programmers prefer that as well. I
suspect that that is a reason for gcc being in the process of removing
their extension (of course they might never complete the process and
keep it around when a deep legacy flag is set.) GCC seems to be moving
away from extensions that lock code down to using it. And experienced
programmers are normally aware of the trap of using vendor extensions
even if they then accept the cost.
Is it now. The size in what units? void has no size. So how do we
know the size of the units a void * points to? Is that size the number
of int's? Of struct foo's?
> the length is a length in bytes.
Ah, always in bytes you say. Excellent. Now we know the implicit size
of void.
> int n;
> write( &n, sizeof(n) );
...
> So nonsense like:
>
> int n;
> int *p = n;
(I think you mean &n there.)
> ...
>
> void* v = p;
> ++v;
>
> would become legal?
Why not? How is that different from what write(&n, sizeof(n)) has to
do with the &n in your example? Inside write(), it must consider &n to
point to a sequence of bytes, not to an integer and not to a mystery.
Therefore we have fallen from the purer faith by conceding that void *v
points to a sequence of bytes. Having already sinned, ++v must mean
point to the next byte. write() could be something like:
extern void writeonebyte(void *);
void write(void *p, size_t n)
{
while (n) {
writeonebyte(p);
p++;
n--;
}
}
That's all entirely consistent with the C standard library
interpretation of void * as a pointer to a sequence of bytes.
Anyway, I like Keith's proposal better, which is to have a "byte" type
which would combine the type behavior of char (or better yet unsigned
char) with the casting behavior of void. Then functions that take
arbitrary chunks of memory would use byte * instead of void *, and
everything would make sense. void * would remain immune to arithmetic,
and would be used when you mean an obscured object as opposed to a
sequence of bytes. When you mean sequences of bytes, like for
memcpy(), you would then use byte *. But you wouldn't need to cast the
pointers to byte *, so you could in fact say memcpy(dest, &n,
sizeof(n)) as opposed to memcpy(dest, (byte *)(&n), sizeof(n)).
I guess part of the problem is that void is a bit overloaded in C,
meaning a bunch of rather different things depending on the context: no
arguments (int main(void)), doesn't return anything (void func(int)),
deliberately dispose of the return value ((void)fclose(f)), obscured
data object (void *foo), and sequence of bytes (memcpy(..., void *,
size_t)). The last two are in a spritual conflict.
Anyway, this has been enjoyable and instructive for me, so thank you
all for your time. Some people seem to get a little ruffled in these
religious discussions, so I hope I didn't offend anybody. So as to not
waste any more of your time, I'll leave you alone and go back to
working on the next release of zlib, which now has two occurences of
buf = (char *)buf + n.
Mark
You mean a change of state from being wrong to being correct?
If that's 'dumbing down' to you, then your perspective is completely
distorted.
>> If you want to advance a void* so it points n bytes later in memory,
>> you can do this:
>> buf = (char*)buf + n;
>
> Thank you. That's what I'll do.
If you're dealing with bytes, then you should almost certainly
be using an unsigned char* rather than a void* anyway.
> Of course, at the loss of the
> expessiveness and readability of the wonderful += operator of C.
Not lost if you use unsigned char*.
> On 2010-01-08 19:23:24 -0800, Dennis \(Icarus\) said:
>> Is void a data type?
>> Does void foo(); return a byte?
>> Does void a; define a byte?
>
> Is void * a pointer? Yes.
Is double* a pointer? Yes.
> If I add n to the register that pointer is in, does it go up by n? Yes.
If I add n to the register that double* pointer is in, does it go up by n?
Yes - but it may no longer be a valid double* pointer, and almost certainly
doesn't point to n doubles past where it previously did.
What happens when you diddle with register values is _irrelevant_.
> Isn't that what you'd expect? Yes.
Are your expectations completely pulled out of nowhere? Yes.
> Anyway, I get the concept that void * deliberately makes the size of
> the type ambiguous, so in principle arithmetic on it should be
> undefined. On the other hand, the standard could make life easier and
> still entirely self-consistent if it simply said that arithmetic on
> void * pointers treats the size as 1. Why make a useful expression
> illegal when instead it can mean what everyone expects it to mean?
I don't expect it to mean that.
>>> (And why a cast of a pointer to another pointer type is no longer a
>>> pointer, I have no idea.)
>>
>> It is a pointer but an r-value not an l-value.
>
> Why is it not an l-value? I can see no ambiguity whatsoever about
> what it means as an l-value.
Assigning to the result of a cast is not just 'ambiguous', it's
downright meaningless. You're advocating nonsense like:
int i=42;
(double)i+=1.;
Phil
--
Any true emperor never needs to wear clothes. -- Devany on r.a.s.f1
The size in bytes. As the standard tells us.
>> the length is a length in bytes.
>
> Ah, always in bytes you say. Excellent. Now we know the implicit
> size of void.
No we don't.
You're hallucinating.
No, all warnings in a clearly defined set.
However, that's not your mistake - your mistake is that you've forgotten
to chose which language to use, and so GCC has defaulted to something
which isn't strictly C.
Watch out fella, you're Daniel in the lions' den. Keep up the good
questions.
No, from the standpoint of science (linguistics), no standard can
"define" C. This is because independent of the standard are all sorts
of C compilers with a fuzzy relation to any one formal definition of
C. Somewhat like world received English, C is what intelligent users
"say" it is. In addition, linguists also add that the language should
be the canonical user's first language, but this is impossible with C,
of course.
Now, the problem is measuring "intelligence", especially here. It
cannot be measured by "correct use of C" since "the correct use of C"
is basically "what I say it is" or "what is in the Holy Standard
[which was written to protect vendor profits and leaves too much
undefined]".
Here, intelligence is only measurable by literacy in writing English
and basic honesty (not making crude lies such as Heathfield's "Nilges
has never posted in comp.risks"). Of the proponents of "standard C"
only Ben Bacarisse passes this test. Other posters spout what I regard
as incoherent nonsense, such as the claim that one can be clear and
false at the same time, and, they defend their complete nonsense to
the last round and the last man.
The original poster here is in fact asking good questions in a
literate way. He's toast since basic literacy and honesty are so
seldom found in this newsgroup.
>
>
>
> >> If you want to advance a void* so it points n bytes later in memory,
> >> you can do this:
> >> buf = (char*)buf + n;
>
> > Thank you. That's what I'll do. Of course, at the loss of the
> > expessiveness and readability of the wonderful += operator of C.
>
> > On 2010-01-08 19:23:24 -0800, Dennis \(Icarus\) said:
> >> Is void a data type?
> >> Does void foo(); return a byte?
> >> Does void a; define a byte?
>
> > Is void * a pointer? Yes.
>
> Yes it is, and dereferencing it will cause problems.
But if it is a pointer in truth, why would dereferencing it cause
problems, oh Divine Master? Yea this is hard for us mere mortals to
understand! For what is a pointer but that which dereferenceth?
>
> > If I add n to the register that pointer is in, does it go up by n? Yes.
>
> That's assembly though - not C.
Oh divine Master, thou speakest horseshit indeed: for hath not C a
register declaration? And are we not supposed to think ahead a little
bit to what happeneth when our code runneth? Yea, verily they speak
with forked tongue who say, "behold: C is close to the Machine, and if
thou codest in C, thou shalt be one with the Machine, warm in winter
owing to the heat of its circuitry, cool in summer's heat owing to its
air conditioning", but then slay those, as Seebach has slain those,
who darest to speak of the mysteries of runtime such as the Stack, or
here the Register.
>
> > Isn't that what you'd expect? Yes.
>
> If I was programming assembly, sure, but not if I was programming in C.
>
>
>
> > Anyway, I get the concept that void * deliberately makes the size of the
> > type ambiguous, so in principle arithmetic on it should be undefined. On
> > the other hand, the standard could make life easier and still entirely
> > self-consistent if it simply said that arithmetic on void * pointers
> > treats the size as 1. Why make a useful expression illegal when instead
> > it can mean what everyone expects it to mean?
>
> >>> (And why a cast of a pointer to another pointer type is no longer a
> >>> pointer, I have no idea.)
>
> >> It is a pointer but an r-value not an l-value.
>
> > Why is it not an l-value? I can see no ambiguity whatsoever about what it
> > means as an l-value.
>
> Morris Keesan explains this nicely.
Thou directest him away for thou art full of shit.
>
> Dennis
You poor bastard. Hast thou not heard? Hast thou not been told? Hast
thou not seen? Hast thou not gotten the memo? The motto of the regs
here is a song by David Byrne: Stop Making Sense.
>
> Mark
Mark Adler: note what the guy says: he uses a negative as his major
operator. To say "it is not the case that" or "that is undefined" is
what the regs do here. That way, they don't take the risk of saying
something that can be easily falsified, since they need to avoid
humiliation at all costs.
And why do they need to avoid humiliation at all costs?
This is because most of the regs here (Seebach, Heathfield, Thompson
and Rosenau being the four horsemen) make it their business to stab
newbies in the back and shame people for not talking their talk.
>
> --
> Ian Collins
This reply is sickeningly dishonest. Thompson wants the user of a
weakly typed language to use it as if it were strongly typed when
there are all sorts of languages that enforce type safety.
Kiki, get this through your head. Companies force programmers to use C
precisely because at the critical moment, C can be butt fucked into
doing things pathologically, and therefore "saving time" and
preserving short-term profits.
Hard magic constants, pre-allocated fixed arrays, void pointers and
all sorts of other crap are the norm in production C code.
Mark, are you the guy in the wikipedia article? Whether you are or are
not, you appear to be one of the few intelligent people to wander in
here. Be advised of the bozo factor, including the incorrect
designation of bozoidness being hurled by bozos.
If by "the good old days" you mean "before the ANSI Standard
in 1989," you might be sort of right. In those days there was no
formal definition of C, and all manner of compilers bestowed the
"C" label on themselves. Some of those may have had a void* type
(K&R C did not), and some of those may have permitted arithmetic
on that type. I never ran across a pre-ANSI compiler with these
characteristics, but there were lots and lots of compilers I
never ran across at all.
Also, if it's pre-ANSI C you speak of, I dispute the adjective
"good."
Starting with ANSI C, which first (formally) introduced void*
to the language, no conforming compiler has ever permitted
arithmetic on void* values. The only compiler I happen to know
of that permits it is gcc (in a non-conforming mode), but perhaps
there are others. There are certainly "self-respecting" compilers
that follow the language definition and forbid it.
> Then someone decided that we would all be more productive somehow
> if the compilers couldn't figure out obvious things like that on their
> own. So now you can't do arithmetic on void * pointers.
It'd be bogus. Since void is an incomplete type, the sizeof
the thing a void* points at is unknown. That's what makes void*
useful in the first place: You can point it at anything at all
without worrying about the sizeof the target. The very same void*
variable can point at a char, an int, and a struct foobar, all
during a single execution of one program. What is "the" size of
all these different targets?
Here's another incomplete type, and an example of how it
might be used in a hypothetical implementation of an ADT:
struct unknown; // incomplete type
struct unknown * newUnknown(int); // a "constructor"
struct unknown * uptr = newUnknown(42); // instantiation
Okay, uptr (we'll suppose) now points at a struct unknown instance.
If you now do ++uptr, what ought to happen? Should the address
advance by one byte? Forty-two bytes? Eleven? Where is the
"one past the end" location for the struct unknown object whose
size is a mystery? Arithmetic on a void* encounters exactly the
same problem: The size of the target is unknown, so the compiler
can't know how far to advance the pointer.
> Ok, fine. So I adjusted, I would do this:
>
> void *buf;
> int n;
>
> (char *)buf += n;
This is also bogus, for the same reason (double)n += 0.42
would be bogus. The left-hand side is an expression with an
operand, an operator, and a value, not an object to which you
can assign something (I'm speaking somewhat loosely here; look
up the term "assignable lvalue" for a more rigorous treatment).
> But now I'm getting nastygrams like "warning: target of assignment not
> really an lvalue; this will be a hard error in the future". Once again
> someone, probably the same person, decided that compilers shouldn't have
> to burden this great responsibility of understanding obvious things like
> that. (And why a cast of a pointer to another pointer type is no longer
> a pointer, I have no idea.)
Because the cast is an operator that derives a value from
its operand. Try this one: (char*)(ptr + 42) = "Hello". Where
do you think the pointer to 'H' should be stored, and why?
> So now what am I supposed to do? It starts to get just downright silly.
> E.g.
>
> void *buf;
> int n;
> char *cmon;
>
> cmon = (char *)buf;
> cmon += n;
> buf = (void *)cmon;
>
> Is there a better way to work around this, at least until the *next*
> dumbing down of the language?
buf = (char*)buf + n;
And before you say "That's just dim-witted," ponder
buf = (int*)buf + n;
It seems to me there are two misconceptions buried in your
mental model of the language. One is the notion that a pointer
is "just an address," which is wrong: A pointer is an address
*and* a type, and the type is important. The second mistake is
to think of the cast operator as meaning "let's pretend this
thing is of a different type," when in fact it means "take the
value of this thing, convert it to a new type, and deliver the
converted value (which may even be unequal to the original)."
You are not the first to fall victim to these two mistaken
notions, nor will you be the last.
--
Eric Sosman
eso...@ieee-dot-org.invalid
ROTFLMAO, another for the Red Book, along with Heathfield
("comp.programming not about programmers", "Nilges not in comp.risks",
"functions return expressions") and Seebach ("the 'heap' is a DOS
term"):
"The point isn't whether it's a sensible thing to do, it's whether
it's legal"
...meaning it must conform to the Holy Standard. When asked why, the
*imams* of the Standard tell their *taliban* that their code must be
portable.
This is completely absurd. C is so dangerously NON portable that ANY
port needs a line by line analysis for gotchas no matter how "good" is
the code. When people want to be portable they use Java.
The reality is that if it works on a specific compiler, that is the
goal in 99.9% of cases.
>
> > Anyway, I thought that the void * type *must* be able to be a vessel
> > for a pointer to any other data object. In fact, it has no other
> > reason to exist. If you cannot put an anydata * in a void * variable,
> > pass it along, and later convert it back to an anydata * and get the
> > anydata it's pointing to, then the value of void * is lost.
>
> > So is it not true that void * must be able to be a vessel for a
> > pointer to any data object?
>
> Depends on what you mean by "vessel".
>
> A value of any pointer type (other than a pointer-to-function type)
> may be converted to void* and back to the original type without
> loss of information. The language specifically guarantees this.
>
> But it's important to understand that conversions operate on values,
> not on objects. There is no implication that an object of type void*
> can be treated as if it were an object of some other pointer type
> (except for the special guarantee that void* and char* have the
> same representation). Conversion is not type-punning.
>
> It's true that, on many systems, all pointer types have the same
> representation, but the language does not make this assumption.
>
> I think the use of the unadorned word "pointer" can lead to a great
> deal of confusion. A pointer object and a pointer value are two
> very different things.
>
> It seems to me like you dislike the fact that C doesn't let you
> treat pointer objects as if they were of different pointer types.
> That's fine; there are certainly things I dislike about C (though
> that's not one of them). But the language is the way it is.
> You might even consider the possibility that there might be rational
> reasons for for some of the decisions, even if you might not
> agree with them.
>
> And there are plenty of other languages -- including the C-like
> dialect that gcc implements.
I have said it before, and I'll say it again. You and the other regs
consistently morph friendly technical discussions into cybernetic
lynchings by starting this shit, Peter. You start using language that
instead of sticking to the technical issues, challenges the
professional standing of your interlocutor. You did this with Schildt,
and now it appears that you are doing it to Adler. And he's the guy
with the track record in technology, it appears now from his email and
the wikipedia article.
He wants to use C in one of the ways it was originally intended: as a
(somewhat) machine independent assembly language. Had you ever
programmed in assembler language, you'd know that it uses puns in many
cases including "void" pointers: in fact, in pure assembler, all
pointers are void, and we somehow figured out how to manage this. It
wasn't great (which is why I debugged a nonworking Fortran compiler in
1972) but it was doable.
Whereas C programmers in a contradictory fashion want people to think
of their language as simultaneously Adult (portable and Standard) yet
also Close to the Machine where they can, like Peter Pan, have Fun.
>
> When you convert a pointer to a new type, you may well create a new
> value which is different from the value you converted.
>
> > Uh oh. Now I'm really worried, if having different, non-transferrable
> > data pointer representations is permitted by the C standard. I sure
> > hope you're wrong.
>
> No, he's not. They've been around forever and have been essential for some
> high-performance systems.
>
> > So is it not true that void * must be able to be a vessel for a pointer
> > to any data object?
>
> Of course it must.
>
> But that means a conversion. And in some cases, the conversion actually changes
> the bit contents or even SIZE of the pointer.
>
> -s
> --
> Copyright 2010, all wrongs reversed. Peter Seebach / usenet-nos...@seebs.nethttp://www.seebs.net/log/<-- lawsuits, religion, and funny pictureshttp://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
At runtime, I would have the choice:
(1) Let the pointer to int be a four byte (assuming the existence of
bytes in the first place) value which has to be multiplied by four
every time I use it (assuming byte addresibility and 32 bit ints)
(2) Let the pointer be a byte address at all times which is always
divisible by four if all ints must be aligned.
Number (1) is a major performance boner: you might as well be using
array indexes in Visual Basic.
I understand that pointers come in different flavors, especially on
different machines. But in most cases, having different pointer
"shapes" in machine language is a design flaw.
>
> Mark
Mark - I'm curious. How did this issue become a problem for you.
Presumably, you were tootling along just fine, doing arithmetic on your
void *s, thinking nothing of it. Then, something changed to make it a
problem. Did you switch compilers (I'm going to guess from gcc [a
sensible compiler] to something else, something more religious, something
more in keeping with the tards on this newsgroup)
Or, did you "upgrade" to a newer version of gcc, that has more warnings?
Are you under some corporate mandate that your code must compile w/o
warnings?
Then you're just not into the dogma in force around here.
It is well known that legal C code can be bulls*t. There is (was?)
even a contest for producing the worst (most obfuscated) but legal
(compiles and runs) C code. C is a used Ford Pinto, fun to drive in
its own way as long as you realize you're putting your life on the
line.
>
> --
> Ian Collins
You've never used gcc?
Not that that is impossible, of course, but, as the saying goes, it
sounds like bullshit to me.
Using norms from the realm of natural languages isn't necessarily
helpful. Natural languages and computer languages share much in common,
but much is not common. Indeed, you note one further down, when you
point out that it would difficult for anyone to claim C as their first
language.
Whilst it's true that (in line with natural languages) there are many
dialects of C, programming languages are (I would argue) necessarily
constrained by the need for programmers to agree very specific outcomes
to very specific constructs; where (most) humans will happy handle (a
certain level of) variation in usage and still be able to comprehend the
essential meaning of the words, most computer languages and their
execution environments struggle to be more than literal. As a result
(and in my experience), languages which don't have formal definitions
which implementations *try* to conform to tend to either exist in a
monoculture (as single implementations) or fail to gain widespread
adoption.
What I'm saying is, there *is* a widely agreed definition of what C
"is", and that's not invalidated by the fact that most implementations
(to one extent or another) fail to fully conform.
To address the issue of "C is what intelligent users say it is", to an
extent I agree. Equally (as with revisions of the OED), it's also
almost the other way round in that "C adapts to reflect what intelligent
users demand". As a result, subsequent revisions of the standard tend
to take on board the best ideas of non-conforming implementations and,
as a result, many features from many different "C" compilers have ended
up as part of future standards. That's as it should be.
It would be nice to think, however, that both compiler writers *and*
software engineers (try to) only go outside the standard deliberately,
and are fully aware of what that means. As someone who has (over the
course of a twenty year career) had to deal with cross platform
development, environment changes, moves between 16, 32 and 64 bit
implementations and so on, life is *much* easier where that is built
into the assumptions of the developers from the start.
Of course, it also depends on the context. If the code is *known* to be
used within quite limited environments and, perhaps more significantly,
if it's known to be short-lived*, this becomes less important.
* I have, however, known code that "will only be in use for the next
couple of months" that suddenly became permanent.
>> Yes it is, and dereferencing it will cause problems.
>
> But if it is a pointer in truth, why would dereferencing it cause
> problems, oh Divine Master? Yea this is hard for us mere mortals to
> understand! For what is a pointer but that which dereferenceth?
In C, a pointer is something which can contain something which looks
like a pointer. If it contains a valid pointer, it can be safely
dereferenced. If it contains something which looks like a valid pointer
but isn't (isn't a pointer or isn't valid), it will cause a problem.
>> That's assembly though - not C.
>
> Oh divine Master, thou speakest horseshit indeed: for hath not C a
> register declaration? And are we not supposed to think ahead a little
> bit to what happeneth when our code runneth? Yea, verily they speak
> with forked tongue who say, "behold: C is close to the Machine, and if
> thou codest in C, thou shalt be one with the Machine, warm in winter
> owing to the heat of its circuitry, cool in summer's heat owing to its
> air conditioning", but then slay those, as Seebach has slain those,
> who darest to speak of the mysteries of runtime such as the Stack, or
> here the Register.
Unfortunately, "register" is somewhat tricky concept in C. There is no
obligation for the register keyword to do anything. The "register"
storage-class specifier hints to the compiler that this storage
declaration declares an object (small-o) which the compiler should try
to ensure fast accesses to. That request may be validly disregarded.
In some implementations (and originally, hence the wording) this may
be achieved by ensuring that it's kept in a register. If an
architecture has a way of producing fast accesses which doesn't involve
registers, the compiler is perfectly entitled to emit code as a direct
result of "register" which has nothing to do with registers.
A void pointer is a thing of holy dread
It may only be copied, writ or read
If thou dost arithmetic on a void pointer
Thou shalt be damn'd in the hereinafter.
Hark, I here Satan and his imps
Coming to get you twerps and gimps
Who darest to increment a void pointer
Jes' cuz it's permitted by your compiler!
Hell and Perdition yawn for thee
Heaven's trumpets summon me
For I codeth standard C
And I shall wear dem golden sandals
Whilst in Hell thou'rt sodomiz'd wif candles!
Here, I think you are barking up the wrong tree. Kiki is wrong about
just about everything and anything that is useful or relevant, but when
it comes to the C standard, he is never wrong. As we know, he sleeps
with a copy of it under his pillow.
You might want to search Google groups for a post I made some time ago
about Kiki preparing for a vacation on a sunny tropical isle. I think
if you search for author (Kenny MacCormack - or however I spell it) and
the word "lotion" (or maybe "oil"), you should find it.
Portable C is an oxymoron. Any responsible port of C takes if done
properly the same time as a rewrite in Java, or more. Therefore, any
organization with an inventory of C contemplating a port to a new
machine should rewrite the code in Java UNLESS the code is truly time-
critical, because the time it takes to do an acceptable job of
verifification that the C code will work on the new platform can be
used to rewrite in Java, which is portable, period.
This is incorrect. Since C is a weakly typed and punning language
which permits values to be casted with few constraints, what you call
an "incomplete" pointer is a Von Neumann pointer to the smallest
addressable unit, whose size is by definition, 1. C assumes von
Neumann architecture.
> That's what makes void*
> useful in the first place: You can point it at anything at all
> without worrying about the sizeof the target.
On the one hand, the *imams* here of Holy C claim of its pathology
(such as the fact that sprintf() can be broken at any time in running
code simply by passing it a to-long string) that if one is "competent"
one won't get bitten (although it is impossible to write code that
prevents the sprintf() problem without predeciding a limit that should
not be predecided).
On the other hand, having blessed all sorts of nonsense, they suddenly
get religion when it comes to void pointer arithmetic, like the vicar
returning from the whorehouse goin' back to church!
>The very same void*
> variable can point at a char, an int, and a struct foobar, all
> during a single execution of one program. What is "the" size of
> all these different targets?
>
> Here's another incomplete type, and an example of how it
> might be used in a hypothetical implementation of an ADT:
>
> struct unknown; // incomplete type
> struct unknown * newUnknown(int); // a "constructor"
> struct unknown * uptr = newUnknown(42); // instantiation
>
> Okay, uptr (we'll suppose) now points at a struct unknown instance.
> If you now do ++uptr, what ought to happen? Should the address
> advance by one byte? Forty-two bytes? Eleven? Where is the
> "one past the end" location for the struct unknown object whose
> size is a mystery? Arithmetic on a void* encounters exactly the
> same problem: The size of the target is unknown, so the compiler
> can't know how far to advance the pointer.
>
Apples and oranges. A struct has different lengths. Memory has a
smallest addressable unit. The fact that there might be constraints on
addresses at the smallest unit (alignment) doesn't change this.
> > Ok, fine. So I adjusted, I would do this:
>
> > void *buf;
> > int n;
>
> > (char *)buf += n;
>
> This is also bogus, for the same reason (double)n += 0.42
> would be bogus. The left-hand side is an expression with an
> operand, an operator, and a value, not an object to which you
> can assign something (I'm speaking somewhat loosely here; look
> up the term "assignable lvalue" for a more rigorous treatment).
Brilliant. Yet Another piece of silliness alongside sequence points,
invented PURELY to pretend that C is a grown-up's language: the
Unassignable LValue! A pointer that cannot be dereferenced! It's a
bird! It's a plane! It's a mule!
> esos...@ieee-dot-org.invalid
>In the good old days, any self-respecting C compiler knew what you
>meant when you did arithmetic on a void * pointer, i.e. that the units
>were bytes.
I'll ignore most of your post because it's already been addressed.
Initially character pointers served three purposes: pointers to real
characters, pointers to generic bytes, and pointers to "opaque"
objects - objects of locally unknown type. With the addition of void
pointers, we now have two types for three kinds of thing.
When void pointers are used for objects of unknown type, it's
a good thing that you can't do arithmetic on them. It allows
the compiler to detect some errors that might otherwise go
undetected. On the other hand, this means that you need to use
character pointers to do byte arithmetic.
Incidentally, even if we had three types for the three kinds it
wouldn't always be clear which to use. Consider qsort(). The items
are opaque in that their addresses are going to be passed to a
comparison function which does konw their type and will probably cast
them appropriately, but they are generic bytes in that qsort() will
move them around simply by knowing their size.
-- Richard
--
Please remember to mention me / in tapes you leave behind.
Yea verily hath he not the dogma
This bird dog hath not gotten the word, dawg:
He hath not taken Communion
Eaten the dog's dinner made here of C:
He hath not fed of the canine repast
He hath drunk not the kool ade
He hath not eaten of the insane root
Which makes men mad, having drunk the C
As sailors do when no land they see,
And thirst for drink other than pee.
If he loiters, and if he stay
Then every night and every day
He will be dogged by the hell hounds
And plagued shall he be with the noisome sounds
Of the Parliament of foulish fools
Feeding on fantasies like churlish curs.
But Adler, mighty Eagle become a man
Has already I wot said, doggone it
This place is Bedlam, a place of burning
And the dancing of trolls inflamed.
It is not fit place for a puissant Eagle:
It is a place for Pup, mutt, stray and Beagle.
Yup. Well put.
You can always see that, in pretty much any Usenet thread, but most
especially in CLC - where they do an especially good job of hiding it -
of pretending to be objective - which just makes it worse. That point
at which the thing turns personal - in TA terms, starts sending "You
messages". You don't understand this. You probably wet your bed as a
child. Etc, etc.
But I have to say that, I understand where these people are coming from,
and, believe me, it isn't a pretty place. I have long since stopped
getting angry at it. I just feel pity for them.
Yes. This has been a source of error/confusion with GCC for quite some
time. I think they are going to change -Wall to something like
-Wcommon.
FWIW, I always compile with -W -Wall, which gets everything important.
I never compile with -crippled (aka, -pedantic).
OK, there are differences. However, the alternative (C as math or a
formal language) fails to fit the facts.
>
> Whilst it's true that (in line with natural languages) there are many
> dialects of C, programming languages are (I would argue) necessarily
> constrained by the need for programmers to agree very specific outcomes
> to very specific constructs; where (most) humans will happy handle (a
> certain level of) variation in usage and still be able to comprehend the
> essential meaning of the words, most computer languages and their
> execution environments struggle to be more than literal. As a result
> (and in my experience), languages which don't have formal definitions
> which implementations *try* to conform to tend to either exist in a
> monoculture (as single implementations) or fail to gain widespread
> adoption.
The problem is that C's "standardised formality" is bought by
simultaneously calling things undefined and permitting vendors to
continue to support the "undefined" concepts. C is simply not
formalized save in the most "Platonist" sense as opposed to the
"Intuitionist" sense, where Platonism in the philosophy of mathematics
is the belief that mathematical objects exist *in reality* whether or
not we know them, whereas Intuitionism is the belief that it doesn't
exist until it's constructed.
Now, it would seem that Intuitionism would be the philosophy of the
competent programmer.
[And at this point let me mention my sadness on hearing that Dik
Winter has passed away, for this type of statement is what he loved to
jump on: perhaps as a Dutch person he felt a special connection with
Intuitionism, and he always had something interesting to contribute to
the discussion, especially when I mentioned Brouwer or Dijkstra.]
Applying this to programming, to say "doing arithmetic on a void
pointer has no result" is Platonic since Adler showed (constructively
and in an intuitive/Intuitionist way) how this has a sensible meaning:
the sizeof a void pointer is the sizeof the smallest unit of memory.
It is Platonist to insist on the (unconstructed) possibility of an
alternative interpretation.
>
> What I'm saying is, there *is* a widely agreed definition of what C
> "is", and that's not invalidated by the fact that most implementations
> (to one extent or another) fail to fully conform.
>
> To address the issue of "C is what intelligent users say it is", to an
> extent I agree. Equally (as with revisions of the OED), it's also
> almost the other way round in that "C adapts to reflect what intelligent
> users demand". As a result, subsequent revisions of the standard tend
> to take on board the best ideas of non-conforming implementations and,
> as a result, many features from many different "C" compilers have ended
> up as part of future standards. That's as it should be.
Read Fowler/Burchfield Modern English Usage. Fowler makes no attempt
whatsoever to legislate what English should be, only what literate
people do. For example, Burchfield usefully notes that UK speakers
have FINALLY (as of the latest edition) given us an English
"construct" that is sorely needed: a reflexive pronoun that means
"himself or herself". "Themself" has been spotted by Burchfield:
"someone has hurt themself". Likewise, Adler's idiom makes perfect
sense.
>
> It would be nice to think, however, that both compiler writers *and*
> software engineers (try to) only go outside the standard deliberately,
> and are fully aware of what that means. As someone who has (over the
> course of a twenty year career) had to deal with cross platform
> development, environment changes, moves between 16, 32 and 64 bit
> implementations and so on, life is *much* easier where that is built
> into the assumptions of the developers from the start.
>
> Of course, it also depends on the context. If the code is *known* to be
> used within quite limited environments and, perhaps more significantly,
> if it's known to be short-lived*, this becomes less important.
>
> * I have, however, known code that "will only be in use for the next
> couple of months" that suddenly became permanent.
>
> >> Yes it is, and dereferencing it will cause problems.
>
> > But if it is a pointer in truth, why would dereferencing it cause
> > problems, oh Divine Master? Yea this is hard for us mere mortals to
> > understand! For what is a pointer but that which dereferenceth?
>
> In C, a pointer is something which can contain something which looks
> like a pointer. If it contains a valid pointer, it can be safely
> dereferenced. If it contains something which looks like a valid pointer
> but isn't (isn't a pointer or isn't valid), it will cause a problem.
Your last statement is literally untrue in most cases, in C. There is
no runtime check, and there are cases of code that dereferences "bad"
pointers and gets away with it,.
>
> >> That's assembly though - not C.
>
> > Oh divine Master, thou speakest horseshit indeed: for hath not C a
> > register declaration? And are we not supposed to think ahead a little
> > bit to what happeneth when our code runneth? Yea, verily they speak
> > with forked tongue who say, "behold: C is close to the Machine, and if
> > thou codest in C, thou shalt be one with the Machine, warm in winter
> > owing to the heat of its circuitry, cool in summer's heat owing to its
> > air conditioning", but then slay those, as Seebach has slain those,
> > who darest to speak of the mysteries of runtime such as the Stack, or
> > here the Register.
>
> Unfortunately, "register" is somewhat tricky concept in C. There is no
> obligation for the register keyword to do anything. The "register"
> storage-class specifier hints to the compiler that this storage
> declaration declares an object (small-o) which the compiler should try
> to ensure fast accesses to. That request may be validly disregarded.
> In some implementations (and originally, hence the wording) this may
> be achieved by ensuring that it's kept in a register. If an
> architecture has a way of producing fast accesses which doesn't involve
> registers, the compiler is perfectly entitled to emit code as a direct
> result of "register" which has nothing to do with registers.
All very true but irrevelant to Adler's (Intuitionist) point: that one
test of a construct is whether we can construct a working runtime
model.
Whereas an earlier generation of C programmers (myself included)
graduated into C (and out of it in my case when I realized its
limitations) from assembler, many posters here started with C and may
have been crippled by it to the extent that they assault authors like
Schildt for speaking of stacks and heaps.
> For example ...?
Some Big Iron is not naturally byte-addressed, so (void *) has to have
a regular address plus an offset, same as (char *).
Basically, it really comes across as though you have little to no
experience with a broad range of systems or compilers, and you're just
looking for a fight, but you haven't even suggested a single reason
for which you shouldn't just use (unsigned char *) for the kinds of
operations you appear to wish to perform.
-s
--
Copyright 2010, all wrongs reversed. Peter Seebach / usenet...@seebs.net
Yeah. I think there's a few warnings you get with plain -W that aren't in
-Wall either.
> Now I can play the game! (Gets up on soap box ...) I assert that a
> function that takes a void * pointer and a size_t length to identify a
> thing makes no sense whatsoever in the C standard, since length has no
> meaning with respect to the void * pointer. Yet there are many such
> functions that are also part of the C standard, such as memcpy(),
> write(), etc.
write() is not part of the C standard.
And actually, no. size_t is the size-in-bytes, canonically, since that's
what sizeof(x) yields.
> I would argue that alternatively we could eliminate this mixed message
> of void both implicitly having a size and not having a size by changing
> the C standard to give void a size, i.e. one. sizeof(void) == 1.
> Nothing would break -- this would just add to the things that can be
> expressed, in a way that is consistent with how library functions use
> void * arguments. (The void type would still not be allowed by itself,
> and so function(void) would still mean no arguments.) In fact, in the
> evil gcc, n = sizeof(void); sets n to 1, and it seems to work fine.
It works, but it's dumbing things down -- it's encouraging sloppy thinking
about pointer types, and that ain't good.
In bytes, because that's what sizeof() yields.
> Ah, always in bytes you say. Excellent. Now we know the implicit size
> of void.
No. void is an incomplete type that cannot be completed.
A "void *" is not a pointer to a byte, it's a pointer to an object of
unknown type which may consist of multiple bytes.
<snip>
>> (I'm speaking somewhat loosely here; look
>> up the term "assignable lvalue" for a more rigorous treatment).
>
> Brilliant. Yet Another piece of silliness alongside sequence points,
> invented PURELY to pretend that C is a grown-up's language: the
> Unassignable LValue! A pointer that cannot be dereferenced! It's a
> bird! It's a plane! It's a mule!
It's an array.
The proper term is "modifiable lvalue".
<snip>
Oops - cause for confusion there. To disambiguate: the term "assignable
lvalue" apparently used by Eric Sosman (I didn't see his article, so I'm
taking the dangerous course of relying on the accuracy of the quoted
material) is more formally known as "modifiable lvalue". An array is, of
course, an example of an lvalue that is *not* modifiable.
I guess that there is an issue of context, as I suggested elsewhere.
If you want to use C within one context (one where you treat the
behaviour of a particular implementation as being the de-facto
definition of "C" - which may not be an unreasonable approach, depending
on the situation), you view it one way. If you want any chance of
writing something that will behave in a predictable way in a different
environment (different compiler/OS/whatever) or when underlying
assumptions change (word sizes changing on your platform, for example),
you take a different view.
I accept that you take the view that there is no such thing as
portability when it comes to C. (I'd also agree that if you take the
first approach, you're right.)
> [And at this point let me mention my sadness on hearing that Dik
> Winter has passed away, for this type of statement is what he loved to
> jump on: perhaps as a Dutch person he felt a special connection with
> Intuitionism, and he always had something interesting to contribute to
> the discussion, especially when I mentioned Brouwer or Dijkstra.]
>
> Applying this to programming, to say "doing arithmetic on a void
> pointer has no result" is Platonic since Adler showed (constructively
> and in an intuitive/Intuitionist way) how this has a sensible meaning:
> the sizeof a void pointer is the sizeof the smallest unit of memory.
> It is Platonist to insist on the (unconstructed) possibility of an
> alternative interpretation.
Clearly, doing arithmetic on a void pointer produces a result, but
whether it's useful or reliable is a different issue.
>> What I'm saying is, there *is* a widely agreed definition of what C
>> "is", and that's not invalidated by the fact that most implementations
>> (to one extent or another) fail to fully conform.
>>
>> To address the issue of "C is what intelligent users say it is", to an
>> extent I agree. 嚙瘟qually (as with revisions of the OED), it's also
>> almost the other way round in that "C adapts to reflect what intelligent
>> users demand". 嚙璀s a result, subsequent revisions of the standard tend
>> to take on board the best ideas of non-conforming implementations and,
>> as a result, many features from many different "C" compilers have ended
>> up as part of future standards. 嚙確hat's as it should be.
>
> Read Fowler/Burchfield Modern English Usage. Fowler makes no attempt
> whatsoever to legislate what English should be, only what literate
> people do. For example, Burchfield usefully notes that UK speakers
> have FINALLY (as of the latest edition) given us an English
> "construct" that is sorely needed: a reflexive pronoun that means
> "himself or herself". "Themself" has been spotted by Burchfield:
> "someone has hurt themself". Likewise, Adler's idiom makes perfect
> sense.
Sure - I've read Fowler, though it's a few years since I picked it up.
It describes some ways that people use English, with the recognition
that natural language evolves and changes. Indeed, in the case of
English, there is a good bit of parallel development because of the
number of large, but geographically separated, first-language users of
English.
But I'm still not convinced that we should try to map between languages
for humans and languages for machines in quite this way at this stage in
language development.
>> It would be nice to think, however, that both compiler writers *and*
>> software engineers (try to) only go outside the standard deliberately,
>> and are fully aware of what that means. 嚙璀s someone who has (over the
>> course of a twenty year career) had to deal with cross platform
>> development, environment changes, moves between 16, 32 and 64 bit
>> implementations and so on, life is *much* easier where that is built
>> into the assumptions of the developers from the start.
>>
>> Of course, it also depends on the context. 嚙瘢f the code is *known* to be
>> used within quite limited environments and, perhaps more significantly,
>> if it's known to be short-lived*, this becomes less important.
>>
>> * I have, however, known code that "will only be in use for the next
>> 嚙�couple of months" that suddenly became permanent.
>>
>> >> Yes it is, and dereferencing it will cause problems.
>>
>> > But if it is a pointer in truth, why would dereferencing it cause
>> > problems, oh Divine Master? Yea this is hard for us mere mortals to
>> > understand! For what is a pointer but that which dereferenceth?
>>
>> In C, a pointer is something which can contain something which looks
>> like a pointer. 嚙瘢f it contains a valid pointer, it can be safely
>> dereferenced. 嚙瘢f it contains something which looks like a valid pointer
>> but isn't (isn't a pointer or isn't valid), it will cause a problem.
>
> Your last statement is literally untrue in most cases, in C. There is
> no runtime check, and there are cases of code that dereferences "bad"
> pointers and gets away with it,.
Yes, it is true that you can often get away with all sorts of things.
If, however, you read it in conjunction with the paragraphs just above,
you see the problem; you might get away with it today, tomorrow or the
next decade. When the situation changes (word sizes, pointer handling,
whatever), your code suddenly doesn't work as you intended (and how it
had been behaving) and the vendor may or may not feel that it's there
responsibility to reproduce the old (but undocumented) behaviour in
newer iterations of the compiler.
Sticking to behaviours that are documented should avoid these problems.
>> Unfortunately, "register" is somewhat tricky concept in C. 嚙確here is no
>> obligation for the register keyword to do anything. 嚙確he "register"
>> storage-class specifier hints to the compiler that this storage
>> declaration declares an object (small-o) which the compiler should try
>> to ensure fast accesses to. 嚙確hat request may be validly disregarded.
>> In some implementations (and originally, hence the wording) this may
>> be achieved by ensuring that it's kept in a register. 嚙瘢f an
>> architecture has a way of producing fast accesses which doesn't involve
>> registers, the compiler is perfectly entitled to emit code as a direct
>> result of "register" which has nothing to do with registers.
>
> All very true but irrevelant to Adler's (Intuitionist) point: that one
> test of a construct is whether we can construct a working runtime
> model.
Fair enough - but the delivery the right output isn't a good basis on
which to judge the correctness/appropriateness of the inputs or the
process applied. Language features shouldn't be black boxes.
> Whereas an earlier generation of C programmers (myself included)
> graduated into C (and out of it in my case when I realized its
> limitations) from assembler, many posters here started with C and may
> have been crippled by it to the extent that they assault authors like
> Schildt for speaking of stacks and heaps.
I would agree that there are people who have not experience a
wide-enough range of language-types and have tunnel-vision on the
"right" way to do things based on a narrow definition of "right"ness.
I haven't read Schildt (so I can't and won't comment on him), but I have
found it to be the case in a fair number of other language guides and
references. I have particular experience of this in C++* where the
quality of the commentary varied wildly, and the language was often
misrepresented -- not that C++ doesn't have lots of things to dislike in
any case. ;-)
Where authors present concepts or actual code which mislead readers
about norms within the language standards (without acknowledging that
fact), I have problems with it - and that includes cases where it has
been done to assist the beginner. If we believe a feature is wrong, we
should say so up front. If we choose to simplify for a beginner, we
should state that and (I would argue) it should probably be corrected
later in the book once the beginner is at a point where they can manage
the unsimplified version.
The practical outcome of misleading students used to come up from time
to time. For example, I would set exercises and find that students
would present solutions which only worked when compiled with a specific
compiler and/or executed on a specific platform**.
Simplification for the beginner is a good thing, but it shouldn't be
that the beginner gets a nasty surprise later on when someone says
"You're doing that wrong!". It can be very embarrassing for them.
* I taught it at Undergraduate level for a number of years, and was sent
dozens of books for review, and there were many that were just bad in
a variety of different ways.
** The worst case of this, I had to find an old version of Windows,
compile it with an obscure compiler and it still didn't work
properly. The student felt hard-done-by that he lost a lot of marks (I
wrote the markscheme so that I could still give marks for concepts - it
was an SE module which explicitly used C++ rather than a C++ module)
because the book he used (which was designed to go with this compiler)
contained examples which he based his submission on. It just didn't
match the language and didn't work as advertised.
I *had* warned them that I would be testing them with g++ on Linux
and VC++ on Windows (with specified versions), so I wasn't just being
mean.
FWIW, it is part of POSIX, which FWIW is almost as much a definition of "the
truth of the matter" as the C standard, given that both are of a comparable
age and are of similar origins.
nevermind that not all OS's are POSIX conformant, which more sets a
practical limit than a formal one.
it is all about which standard has more weight and more authority and in
what context.
for the Windows' developer, every word from MS's mouth is gold, and every
value of every flag is etched in stone...
> And actually, no. size_t is the size-in-bytes, canonically, since that's
> what sizeof(x) yields.
>
I think the point is that it is inconsistent to pass one type but give a
measurement in another.
to think that 'void' is purely without size would essentially make giving a
size in bytes as a semantic inconsistency. to accept that the pointer-type
is 'void *', yet that the size is bytes, thus "strongly implies" the
existence of a size (even if one does not exist in someones' formalistic
notions...).
>> I would argue that alternatively we could eliminate this mixed message
>> of void both implicitly having a size and not having a size by changing
>> the C standard to give void a size, i.e. one. sizeof(void) == 1.
>> Nothing would break -- this would just add to the things that can be
>> expressed, in a way that is consistent with how library functions use
>> void * arguments. (The void type would still not be allowed by itself,
>> and so function(void) would still mean no arguments.) In fact, in the
>> evil gcc, n = sizeof(void); sets n to 1, and it seems to work fine.
>
> It works, but it's dumbing things down -- it's encouraging sloppy thinking
> about pointer types, and that ain't good.
>
it makes sense to the CPU, and in the end, it is this which is what matters.
maybe the "slopiness" is humans for believing in a world with "objects"
which are anything other than bytes?... (much as they believe in a physical
world composed of things too far above the level of individual particles and
the internal reactions of so many fields?...).
it may well be the formality which is what is incorrect, rather than that of
the world being formalized.
since, a formalized world may well allow things which are impossible in the
actual world, and in this case, it is the formalism said to be incorrect
rather than the world being incorrect for not causing the formal definition
to work exactly as expected.
"all hail Von Neumann..."
nevermind that for many practical reasons, one may confine themselves to
these alternative formalistic realities. the fantasy of types apart from
representation, of value-propagation apart from a set of mutable states, ...
only that any such definition is not an equivalent of the reality in
question, merely a sort of tool, be it for mental or practical application
(a sort of self-deciet as it were, to live in realities where one knows they
are not really the case, none the less it is all the case that one live and
behave as if they were...).
or such...
For debug builds, I like to use these gcc options:
-pipe -ansi -pedantic -fhosted -fno-builtin -Wall -Wextra -Wfloat-equal \
-Wundef -Wshadow -Wlarger-than-32767 -Wpointer-arith -Wbad-function-cast \
-Wcast-qual -Wcast-align -Wwrite-strings -Wstrict-prototypes -Wformat=2 \
-Wmissing-prototypes -Wmissing-declarations -Wredundant-decls \
-Wnested-externs -Wunreachable-code -Winline -Wlong-long
Adding -O2 may or may not be useful. Both -Wall and -Wextra depend on
the selected optimization level. For example, -Wuninitialized is
included by either of them iff the optimization level is at least 1.
Sometimes -Wuninitialized catches real errors, at other times it
produces false warnings.
Cheers,
lacos
There are, however, functions in the Standard library taking
size_t parameters that do not specify byte counts. calloc(),
qsort(), bsearch(), fwrite(), and fread() come to mind immediately,
and there may be others.
--
Eric Sosman
eso...@ieee-dot-org.invalid
No, -Wall is equivalent to a specific set of other warning flags.
"gcc -Wall" enables warnings that "gcc" by itself does not.
See "info gcc" or
<http://gcc.gnu.org/onlinedocs/gcc-4.4.2/gcc/Warning-Options.html>.
--
Keith Thompson (The_Other_Keith) ks...@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
No it isn't. The sizeof any pointer is the number of bytes used to store
a pointer value (generalised address). The size of a void* is required
to be the same size as the size of a char*. On most systems it is 4 or
8. That is not the same as the size of the thing pointed to. And that
is the point, a void* object holds a location not the address of an
object. If the programmer knows what is at that location they can
convert the location information into an appropriate pointer value.
This is not rocket science and is generally well understood by qualified
computer scientists (i.w. those with CS degrees) who have learnt C.
> It is Platonist to insist on the (unconstructed) possibility of an
> alternative interpretation.
>
No it isn't. Both Platonists and Intuitionists along with several other
schools of mathematical philosophy well understand the difference
between the container and the contents, the reference and the referee.
Francis's post was obviously confused and bewildered.
I don't think it was really necessary to rebut it.
No, we don't, because void has no implicit size. We know the units
specified for the size in those specific functions because it's
documented. The units for the size argument are give by the function
not by the void* argument.
fread(), fwrite(), and calloc() all take size_t arguments that denote
sizes in objects, not in bytes.
[...]
> Anyway, I like Keith's proposal better, which is to have a "byte" type
> which would combine the type behavior of char (or better yet unsigned
> char) with the casting behavior of void.
Thanks. I'm not entirely sure I do. 8-)}
[...]
> Anyway, this has been enjoyable and instructive for me, so thank you
> all for your time. Some people seem to get a little ruffled in these
> religious discussions, so I hope I didn't offend anybody. So as to
> not waste any more of your time, I'll leave you alone and go back to
> working on the next release of zlib, which now has two occurences of
> buf = (char *)buf + n.
One thing that I find a bit offputting (I wouldn't say offensive)
is the idea that these discussions are "religious". C is just
a language; this newsgroup is, for me at least, just a hobby.
Very likely you're being facetious and I'm overreacting, but we've
had people here who really do treat this like a religious argument.
Small quibble: An expression of array type is (usually) an example of
an lvalue that is not modifiable. In C, lvalues are a subset of
expressions; an object (I assume that by "array" you meant "array
object") cannot be an lvalue.
And you are the prototype for it, and the prototypical instance.
Please stop misreading people's words. None of us have ever written
about an incomplete pointer.
And C does not assume a Von Neumann architecture. Indeed several of the
memory models used by earlier versions of Windows were effectively
Harvard architectures. Many embedded devices are programmed in C and
here too we frequently find more complex architectures with multiple
address spaces and Harvard architectures.
Now the rest of your response is so laden with emotive language,
gratuitous insults and name calling that it does not merit a response.
Does that mean you're going to take a step back and reflect on the way
you make them "religious"?
No, thought not.
> this newsgroup is, for me at least, just a hobby.
Yeah, sure. And I'm Elvis Presley.
This group is your life, Keith. It's what gives your cold, autistic life
meaning. No insult intended - it's just a blindingly obvious fact.
> Very likely you're being facetious and I'm overreacting, but we've had
> people here who really do treat this like a religious argument.
Do you really have such little self-awareness, Keith? One can only
imagine what it must be like to be trapped in your autistic head - a sad
and frightening thought. Once again, no insult intended - I mean that
sincerely.
What matters more is what makes sense to the programmer and to the
reader of the code. What matters is constructing a consistent model
for computation, and letting the compiler map that model onto some
particular hardware.
If we only cared about what makes sense to the CPU, we wouldn't have
more than a handful of types, all defined by the system, and portable
code would be a pipe dream.
[...]
OK, I stand corrected. But it is a pretty awful name for that.
"We're safe. We're in the hold of one of the ships of the Vogon
Constructor Fleet."
"Ah. This is obviously some strange definition of the word 'safe' I
hadn't previously been aware of."
-- Ford and Arthur, The Hitchhikers Guide to the Galaxy
Yeah... It's not exactly well-named. Here's what I use to compile stuff
when I'm testing for compliance:
alias c99='gcc -Wall -Wextra -std=c99 -pedantic'
But even that might not be enough to get /all/ the warnings; by the gcc
man page:
Note that some warning flags are not implied by -Wall. Some of them
warn about constructions that users generally do not consider
questionable, but which occasionally you might wish to check for;
others warn about constructions that are necessary or hard to avoid
in some cases, and there is no simple way to modify the code to
suppress the warning. Some of them are enabled by -Wextra but many
of them must be enabled individually.
The man page says this about -pedantic:
Issue all the mandatory diagnostics listed in the C standard. Some
of them are left out by default, since they trigger frequently on
harmless code.
-Beej
granted, but these are still secondary, since if the CPU could not
understand the code or the data, the program would not run.
no matter how elegant the conceptual model, it would be pointless to have SW
which doesn't run...
the CPU is inescapable, as noted by how people may still end up needing to
fall back to using assembler in many situations, and just the same, it is
sometimes / often needed to make use of these low-level details of how data
is represented on particular HW to have much of any real hope of making the
app work effectively...
universal portability for non-trivial apps IS a "pipe dream", apart from the
fact that luckily most HW tends to represent most things in similar enough
ways that one can gloss over the details in the majority of cases (and fall
back to good old "#ifdef" for most of the rest...).
and so, abstraction is a tower built on top of the HW, and not the other way
around.
none of this really changes the end case, that in the end it is the CPU
which matters...
if what were important were appeasing the mind of the reader of the code,
then people would be authors, not programmers, and the end result would be a
novella, rather than a codebase...
Sure, but the particular abstractions we're talking about, such as
having void* be a raw pointer type that doesn't let you access what it
points to without first converting the pointer, don't prevent the
software from running. Implementers and programmers are entirely
capable of writing working software using the abstractions defined by
the C language.
>
> the CPU is inescapable, as noted by how people may still end up needing to
> fall back to using assembler in many situations, and just the same, it is
> sometimes / often needed to make use of these low-level details of how data
> is represented on particular HW to have much of any real hope of making the
> app work effectively...
I don't think that's as common as you imply. I don't remember the
last time I needed to resort to assembler. (For a lot of my own
programming, I don't even resort to C, but that's another story.)
> universal portability for non-trivial apps IS a "pipe dream", apart from the
> fact that luckily most HW tends to represent most things in similar enough
> ways that one can gloss over the details in the majority of cases (and fall
> back to good old "#ifdef" for most of the rest...).
>
> and so, abstraction is a tower built on top of the HW, and not the other way
> around.
The abstraction level provided by standard C seems to be just about
right for a lot of purposes, and I don't find that it gets in the way
of writing efficient code in most cases. And in cases where it does,
you can often write non-portable code, making additional
implementation-specific assumptions, without leaving the language.
I really wouldn't want to make C any lower level than it already is.
> none of this really changes the end case, that in the end it is the CPU
> which matters...
>
> if what were important were appeasing the mind of the reader of the code,
> then people would be authors, not programmers, and the end result would be a
> novella, rather than a codebase...
For the software I work on, I spend a lot more time maintaining it
than running it. It's not a work of literature, but clarity and
legibility are vitally important if I'm going to finish the job
quickly enough for performance on the CPU to matter.
granted.
however, C was also designed to run well on actual computers...
>>
>> the CPU is inescapable, as noted by how people may still end up needing
>> to
>> fall back to using assembler in many situations, and just the same, it is
>> sometimes / often needed to make use of these low-level details of how
>> data
>> is represented on particular HW to have much of any real hope of making
>> the
>> app work effectively...
>
> I don't think that's as common as you imply. I don't remember the
> last time I needed to resort to assembler. (For a lot of my own
> programming, I don't even resort to C, but that's another story.)
>
ASM is, in my case, my second most commonly used language (after C), the 3rd
is C++.
some also goes into very-specialized mini-languages (the most complex and
general of which, at this point, is my compilers' IL, but there are many
cases where I use very limited notations which usually drive specialized
state machines, ...).
many of these specialized mini-languages are used in close proximity to
machine code, either in encoding or decoding machine instructions, or in
some cases driving logic which essentially produces machine code from them.
most of my use of ASM is mixed up with the use of C, where a lot of C code
exists which produces ASM fragments (probably by far my largest-scale use of
ASM at this point).
it is fairly rare that I produce non-trivial amounts of standalone ASM code,
since for what I do, "ASM metaprogramming" is generally more useful.
the weak point though is that a lot of this code has to be either duplicated
or tweaked for every target (so, I don't often use it needlessly...).
use command strings to drive state-machines (typically specialized and
tweaked for a specific target or use) which produce ASM, which is in turn
sent to a dynamic assembler, which may parse it and produce machine code
(this process itself done with more specialized string-driven state
machines), and then linked into the running image. from this point, one may
fetch function pointers and call into the code, or maybe produce yet more
machine code from yet so many more strings...
apart from this, there are a few natural limitations to C which would be
very problematic to work around...
similarly, it is often very useful to be able to treat memory and data (and,
sometimes, machine code) as raw collections of bytes...
>> universal portability for non-trivial apps IS a "pipe dream", apart from
>> the
>> fact that luckily most HW tends to represent most things in similar
>> enough
>> ways that one can gloss over the details in the majority of cases (and
>> fall
>> back to good old "#ifdef" for most of the rest...).
>>
>> and so, abstraction is a tower built on top of the HW, and not the other
>> way
>> around.
>
> The abstraction level provided by standard C seems to be just about
> right for a lot of purposes, and I don't find that it gets in the way
> of writing efficient code in most cases. And in cases where it does,
> you can often write non-portable code, making additional
> implementation-specific assumptions, without leaving the language.
>
> I really wouldn't want to make C any lower level than it already is.
>
granted, but C is also one of the lower-level languages around...
it seemed to me like you were trying to invert the "grand ontology", as in,
another of those people who try to claim that computers are based on the
formal notions humans have about them, ... which to me seems absurd.
but, these types also tend to promote pure FPL's, such as Haskell, ...
I guess, it is the great thing where, as I see it, the basis of reality is
reality...
>> none of this really changes the end case, that in the end it is the CPU
>> which matters...
>>
>> if what were important were appeasing the mind of the reader of the code,
>> then people would be authors, not programmers, and the end result would
>> be a
>> novella, rather than a codebase...
>
> For the software I work on, I spend a lot more time maintaining it
> than running it. It's not a work of literature, but clarity and
> legibility are vitally important if I'm going to finish the job
> quickly enough for performance on the CPU to matter.
>
granted...
well, it is not like I am advocating writing tangled horrors of code either,
as I have also seen plenty of nasty enough code around.
but, I have also seen people try to promote "English-based" programming
languages, and have almost just as often seen these languages go nowhere...
maybe, it is that computers don't do English well, or, more likely, it is
that English is a very bad choice to try to base a programming language on.
but, if humans were in-fact the primary concern, then likely it would have
caught on regardless, considering how much easier of a time most people have
with English than with programming languages...
the fact that they have not, likely indicates that the primary concerns lie
elsewhere.
I personally suspect that it is the computer which remains primary...
humans adapt their mind to the computer, and not the computer around their
mind.
this is probably preferable anyways, as most peoples' minds are fluid and
vague, and it would probably suck if the everything were random and
ambiguous...
Not near as overloaded as static in C++ ;-)
> meaning a bunch of rather different things depending on the context:
<snip>
>
> Anyway, this has been enjoyable and instructive for me, so thank you all
> for your time. Some people seem to get a little ruffled in these
> religious discussions, so I hope I didn't offend anybody. So as to not
> waste any more of your time, I'll leave you alone and go back to working
> on the next release of zlib, which now has two occurences of buf = (char
> *)buf + n.
No offense taken at this end. Good luck with zlib.
Dennis
Sure. The strategy being to say it's all a joke AFTER you've destroyed
people's reputations. They is just good old boys. They's just having
fun.
We know that.
> is the point, a void* object holds a location not the address of an
This is Scholastic pettifogging in service of the false proposition
that C is strongly typed. The location is the address. You're trying
to give a punning language a false respectability. C committs so many
crimes against common sense (such as the preprocessor) it wouldn't
hurt to allow void pointers of sizeof 4 or 8 on many machines,
pointing at the smallest addressable unit of storage, something with
sizeof==1. It would allow us to use C as a truly portable assembly
language. Not that I want to.
> object. If the programmer knows what is at that location they can
> convert the location information into an appropriate pointer value.
>
> This is not rocket science and is generally well understood by qualified
> computer scientists (i.w. those with CS degrees) who have learnt C.
>
> > It is Platonist to insist on the (unconstructed) possibility of an
> > alternative interpretation.
>
> No it isn't. Both Platonists and Intuitionists along with several other
> schools of mathematical philosophy well understand the difference
> between the container and the contents, the reference and the referee.
Hmm, there are only three according to Stefan Korner's out of print
survey: logicism-Platonism, Intuitionism, and formalism. Stewart
Shapiro (The Oxford Handbook of Philosophy of Mathematics and Logic)
lists only five: logicism, Mills' and Quine's empiricism, nominalism,
intuitionism and structuralism. I regard Korner's as the better
taxonomy as long as you add empiricism, better in the sense of being
mutually exclusive and exhaustive.
The existence or nonexistence of personae who well understand is a
rather shadowy argument, that uses poppets to "prove" that
everything's under control. The question is whether you can understand
in the sense of explain.
Most American universities socialize their students into Platonism (cf
Robert M Pirsig's experience at the University of Chicago as
documented in Zen and the Art of Motorcycle Maintenance). This is
because to function in the world as upper middle class high earners,
the students must accept the existence of Platonic Ideas in the form
of immortal corporations with actual human rights under the Fourteenth
Amendment.
This causes people here to treat any sort of assertion *with which
they agree* as of equal value with more informative and intuitionist-
in-the-sense-of-constructive assertions. It's why it's the height of
wisdom to groan that something's "undefined" and that one may not
assign void pointers a dereferencing meaning.
Hypothesis: maybe, in the ancient early history of GCC, that's in
fact what it meant. Then maybe then new warnings crept into the code
and developers realized that they don't want the -Wall option to
actually turn on some of these new ones, because among them there are
false positives: warnings about situations that don't necessarily need
to be fixed. Nobody bothered to rename -Wall to -Wsome, or
-Whouse-special-combo.
Programmerese and boilerplate. Since 1970, EVERY programmer has made
this claim. It starts with the disclaimer that OF COURSE it isn't
"literature" or any girlie stuff (why not) and rolls on to the claim
that the speaker is terribly interested in writing clear and legible
code. In making the claim, the writer or speaker often uses English in
a way that shows he hasn't fully mastered that language, the mastery
of which was thought by Dijkstra to be a prerequisite for mastering
programming.
Here, for example, Kiki says he writes "legible" code. But "legible"
means readable hand-writing! The last time it mattered that one writes
"legibly" was when we prepared Cobol on green and white coding sheets
for the lovely ladies of the keypunch room.
This is a subtle error, like claiming that Schildt is "clear but
false", but it's noticed by the careful reader.
And, just as we start with the necessary disclaimer that one is not
being so presumptuous and "disruptive" as to write Great Literature,
one ends with the absolute value of time to market as linked under the
rose with the time value of money, which rules our lives so savagely.
This sort of language paces out a space within a prison cell
constructed by economic relations which in the USA could have been
questioned long ago and today are in the process of collapse. The
result is the twisted lower middle class resentment that believes that
it, the lower middle class subject, has been virtuous for naught,
grinding away writing clear and legible code whilst elsewhere welfare
bums and dancing trolls chortle at him amongst the burning tyres.
>> buf = (int *)buf + n;
>> or
>> buf = (struct foo *)buf + n;
>
> I don't see why we should have to lose the += operator in this case.
This isn't portable (see below), but it's not an error:
*(struct foo **)&buf += n;
> (struct foo *)buf += n looks to me like it should make perfect sense to
> the compiler.
So how should the compiler interpret:
int x;
(float)x = 7.0;
If you can see the problem with that, consider what happens when a void*
uses a different representation to a "struct foo *" (this is why the above
alternative isn't portable).
Assignment only makes sense for lvalues, casts only make sense for
expressions.
> On 2010-01-08 21:47:42 -0800, Seebs said:
>> On some real-world systems, "(int *)" and "(void *)" have different
>> representations.
>
> For example ...?
I'd strongly suspect on the C90 vintage Crays for a start. On these
machines everything /except/ char was 8 bytes long (so to answer
someone's question in another thread from a couple of days ago "how do I
define a 64 bit integer" the answer was simple - 'short'). Pointers to
char were different, as they had to point within an 8 byte chunk. Since
a void * has to be able to hold a char *, I'd expect a void * to look
like a char * and /not/ like an int.
Of course, the system could have somehow encoded in the void * whether
it was "really" a char * or any other sort. In that case void * wouldn't
look like anything else, and it could be argued that adding 1 to a void
* on such as system should move it forward 8 bytes for an int and 1 for
char - which just goes to show how arithmetic on void * doesn't make
sense.
I think the OP is confused. If you want a generic memory pointer, use a
char * (better, an unsigned char *). Before void came along we used
those for both generic memory pointers and for pointers to anything that
we will revert to a proper type later. Adding void * replaced char *
for the second of these, but not for the first - if you do that you have
no problems.
--
Online waterways route planner | http://canalplan.eu
Plan trips, see photos, check facilities | http://canalplan.org.uk
void is useful only for define functions; something like
void function(void){}
in all other case i think it is better to use: unsigned char* or char*
Unfortunately you do not always have a choice. Look at the type of the
function pointer required by qsort(). That compare function must take
its arguments as void* and convert them internally to what they really
are. That means that when writing a compare function for use in, among
other things, qsort() you need to work with void*.
In addition using unsigned char* to handle memory that you need to step
through and void* for cases where you need a generic pointer to data
makes code easier to understand.
> Mark Adler <mad...@alumni.caltech.edu> writes:
>
>> On 2010-01-08 21:47:42 -0800, Seebs said:
>>> On some real-world systems, "(int *)" and "(void *)" have different
>>> representations.
>>
>> For example ...?
>
> I'd strongly suspect on the C90 vintage Crays for a start.
Yes, any word-addressed machine has this feature. C's parentage is
from languages that are word-oriented (B and BCPL) because such
machine were very common at one time.
I doubt they will become common again any time soon. The engineering
motivation was probably to increase the memory capacity with fewer
expensive address lines and that is not a strong motivator anymore.
Even if there is a reason to favour that design again, they will not
be popular because so much C code will break on them. C's rules are
designed to cope with word addressing but not all programs stick to
the rules.
<snip>
--
Ben.
Indeed. So well put. They's just good ole boys>..
Excellent. Of course no one else here has any clue as to what you are
talking about (pearls before swine to the max!), but you have got it so
dead to rights.
Especialy the bit about "girlie" stuff. As I've noted before, the ethic
of this group is so tightly bound to the prototype of an uneducated (I
don't need no stinkin' college degree!) but manly (no girlie stuff for
me!) programming stud. And, as you've noted elsewhere, they share the
idea that they don't wanna go into management, because going into
management means the end of their eternal youth.
...
>This sort of language paces out a space within a prison cell
>constructed by economic relations which in the USA could have been
>questioned long ago and today are in the process of collapse. The
>result is the twisted lower middle class resentment that believes that
>it, the lower middle class subject, has been virtuous for naught,
>grinding away writing clear and legible code whilst elsewhere welfare
>bums and dancing trolls chortle at him amongst the burning tyres.
Yup. I'll bet a lot of the group members here wish they could just get
out of programming and make a fortune doing some scam. The scams are
all around us.