[comp.lang.c] char* vs void*

ark@alice.att.com (Andrew Koenig) (12/26/90)

In article <277640DD.4A70@tct.uucp> chip@tct.uucp (Chip Salzenberg) writes:

> This statement is false.  It is entirely possible to do so, thanks to
> ANSI's guarantee that pointer-to-char and pointer-to-void will always
> have identical representations.

How's that again?  Why do you think that char* and void* have to have
identical representations?
-- 
				--Andrew Koenig
				  ark@europa.att.com

chip@tct.uucp (Chip Salzenberg) (12/26/90)

According to ark@alice.UUCP ():
>In article <277640DD.4A70@tct.uucp> chip@tct.uucp (Chip Salzenberg) writes:
>> It is entirely possible to [write a portable implementation of qsort()],
>> thanks to ANSI's guarantee that pointer-to-char and pointer-to-void will
>> always have identical representations.
>
>How's that again?  Why do you think that char* and void* have to have
>identical representations?

The ANSI C standard requires that |char *| and |void *| have identical
representations.  This requirement bows to existing practice.  After
all, before ANSI came along, the return type of malloc() was |char *|.
So pre-ANSI C's generic pointer type was, of necessity, |char *|.

The section of E&S discussing differences between C and C++ makes no
mention of any change to this requirement; thus it is part of C++ too.

This equivalence is important because pointers of type |void *| do not
support the pointer arithmetic required to access and rearrange the
elements of the array to be sorted.  Such pointer arithmetic requires
the use of |char *| values.  The equivalence guarantee means that a
portable qsort() may convert pointers from |void *| to |char *| and
back without fear of loss of information.
-- 
Chip Salzenberg at Teltronics/TCT     <chip@tct.uucp>, <uunet!pdn!tct!chip>
"Please don't send me any more of yer scandalous email, Mr. Salzenberg..."
		-- Bruce Becker

dbrooks@penge.osf.org (David Brooks) (12/28/90)

In article <2778A795.6E71@tct.uucp> chip@tct.uucp (Chip Salzenberg) writes:
>
>The ANSI C standard requires that |char *| and |void *| have identical
>representations.  This requirement bows to existing practice.  After
>all, before ANSI came along, the return type of malloc() was |char *|.
>So pre-ANSI C's generic pointer type was, of necessity, |char *|.

How's that again, again?

I don't understand.  In the case of void*, what is being represented?
I mean, what semantic commonality is there between the object popinted
at by a char* and the unobject pointed at by a void* that allows you
to make this statement?

Do you mean "can both be converted to the same integer type and back
without loss of information?"  Do you mean "can be converted to each
other and back without loss of information?"

Damn: my question-mark key just wore out.
-- 
David Brooks				dbrooks@osf.org
Systems Engineering, OSF		uunet!osf.org!dbrooks
In Memoriam: Chris Naughton, aged 16, killed by a drunk driver Dec 22, 1990

wirzeniu@cs.Helsinki.FI (Lars Wirzenius) (12/29/90)

In article <17590@paperboy.OSF.ORG> dbrooks@osf.org (David Brooks) writes:
>In article <2778A795.6E71@tct.uucp> chip@tct.uucp (Chip Salzenberg) writes:
>>
>>The ANSI C standard requires that |char *| and |void *| have identical
>>representations.  This requirement bows to existing practice.  After
>> [deleted]
>
>How's that again, again?
>
>I don't understand.  In the case of void*, what is being represented?

Pointers of type |char *| and |void *| are capable of pointing to *any*
data object and that the way the compiler stores those types of pointers
(their representation, that is) is the same.  Given two pointer
variables,

	void *void_ptr;
	char *char_ptr;

and some data object, say data_object, and you assign the address of
that object to both pointers, their bit patterns will be exactly the
same: 

	void_ptr = &data_object;
	char_ptr = (char *) &data_object;
	printf("%d", memcmp(&void_ptr, &char_ptr, sizeof(void *)));

(the sizes of the two pointers are the same).

The difference between |char *| and |void *| is that you can mix 
|void *| with pointers to other types without having to use casts. With
|char *|, and any other pointer type except |void *|, you need the casts
to make sure everything works on all machines.

Lars Wirzenius    wirzeniu@cs.helsinki.fi    wirzenius@cc.helsinki.fi

mcdaniel@adi.com (Tim McDaniel) (12/29/90)

In the ANSI C standard, the concept of "representation" is not related
to semantics.  "Representation" refers to bit-level layout.

ANSI C standard, section 3.1.2.5, "Types" (page 25, lines 25-28 in
the ANSI edition):

   A pointer to void shall have the same representation and alignment
   requirements as a pointer to a character type.  Similarly, pointers
   to qualified or unqualified versions of compatable types shall have
   the same representation and alignment requirements.{16}  Pointers
   to other types need not have the same representation or alignment
   requirements.

Footnote {16} (page 24; the introduction says that "the footnotes ...
are not part of the standard"):

   16. The same representation and alignment requirements are meant
       to imply interchangeability as arguments to functions, return
       values from functions, and members of unions.

(page 24, lines 15-16):

   The three types char, signed char, and unsigned char are
   collectively called the _character_types_.

The "Types" section also requires identical representation and
alignment for certain other types.  "Compatable" is defined elsewhere
in the standard.

--
Tim McDaniel                 Applied Dynamics Int'l.; Ann Arbor, Michigan, USA
Work phone: +1 313 973 1300                        Home phone: +1 313 677 4386
Internet: mcdaniel@adi.com                UUCP: {uunet,sharkey}!amara!mcdaniel

chip@tct.uucp (Chip Salzenberg) (12/29/90)

According to dbrooks@osf.org (David Brooks):
>In article <2778A795.6E71@tct.uucp> chip@tct.uucp (Chip Salzenberg) writes:
>>The ANSI C standard requires that |char *| and |void *| have identical
>>representations.
>
>I don't understand.  In the case of void*, what is being represented?

The address of an object.  ("Object" explicitly excludes functions.)

>I mean, what semantic commonality is there between the object pointed
>at by a char* and the unobject pointed at by a void* that allows you
>to make this statement?

Pointers of types |char *| and |void *| must both be able to hold the
address of any object.  The reason why this is so differs for the two
types.  For |char *| it's existing practice, and for |void *| it's the
decree of ANSI.

Existing practice for |char *| requires it to be able to hold the
address of any object, since any object type may be allocated with
malloc(), and pre-ANSI malloc() returned a |char *|.  Were ANSI C a
brand new language, |char *| might not have had that property, but
given existing practice, it does.  We can't afford to break all those
dusty decks.

When ANSI invented |void *|, they made it capable of holding any
pointer.  Since |void *| can hold any pointer, and |char *| can hold
any pointer, there would be no point (:-)) in an implementation giving
them different representations.  So ANSI went that final step and
_required_ that their representations be identical.

>Do you mean "can be converted to each other and back without loss of
>information?"

Yes, and that's the important point for this discussion.  A portable
qsort() can cast its |void *| array parameter to |char *| to do
pointer arithmetic, then cast the results of the pointer arithmetic
back to |void *| to generate the comparison function's arguments.
-- 
Chip Salzenberg at Teltronics/TCT     <chip@tct.uucp>, <uunet!pdn!tct!chip>
"Please don't send me any more of yer scandalous email, Mr. Salzenberg..."
		-- Bruce Becker

henry@zoo.toronto.edu (Henry Spencer) (12/30/90)

In article <17590@paperboy.OSF.ORG> dbrooks@osf.org (David Brooks) writes:
>>The ANSI C standard requires that |char *| and |void *| have identical
>>representations...
>
>Do you mean "can both be converted to the same integer type and back
>without loss of information?"  Do you mean "can be converted to each
>other and back without loss of information?"

He means "if you take any `char *' and convert it to a `void *', or vice
versa, and then inspect the bits of both in memory, they must be identical".
The intent is specifically that code expecting one can be handed the other,
across an interface without complete type-checking (e.g. a function call
without a prototype in scope), and everything will still work fine provided
that other constraints of the code are satisfied.
-- 
"The average pointer, statistically,    |Henry Spencer at U of Toronto Zoology
points somewhere in X." -Hugh Redelmeier| henry@zoo.toronto.edu   utzoo!henry

bobj@glinj.gli.com (Robert Jacobs) (01/03/91)

>Pointers of types |char *| and |void *| must both be able to hold the
>address of any object.  The reason why this is so differs for the two
>types.  For |char *| it's existing practice, and for |void *| it's the
>decree of ANSI.

>When ANSI invented |void *|, they made it capable of holding any
>pointer.  Since |void *| can hold any pointer, and |char *| can hold
>any pointer, there would be no point (:-)) in an implementation giving
>them different representations.  So ANSI went that final step and
>_required_ that their representations be identical.

>>Do you mean "can be converted to each other and back without loss of
>>information?"

>Yes, and that's the important point for this discussion.  A portable
>qsort() can cast its |void *| array parameter to |char *| to do
>pointer arithmetic, then cast the results of the pointer arithmetic
>back to |void *| to generate the comparison function's arguments.

What I can't understand is that ANSI decided that one cannot do pointer
arithmitic on a void*, like a char*. Because of this, new work is forced
to use char* as the generic pointer that can do pointer arithmitic.
This continues the existing practice that char* is special. Void*
should be allowed to do pointer arithmitic so that char* does not
have to be used for new work.

But I guess my opinion is too late to be in any version of ANSI C
or C++. Too bad.

Robert Jacobs                                   | 
General Logistics International Inc.            |
bobj@gli.com                                    |

donald@cae780.csi.com (Donald Maffly) (01/04/91)

In article <1991Jan2.171429.11566@glinj.gli.com> bobj@glinj.gli.com (Robert Jacobs) writes:
>
>>Pointers of types |char *| and |void *| must both be able to hold the
>>address of any object.  The reason why this is so differs for the two
>>types.  For |char *| it's existing practice, and for |void *| it's the
>>decree of ANSI.
>>
>> [ ... ]
>>
>
>What I can't understand is that ANSI decided that one cannot do pointer
>arithmitic on a void*, like a char*. Because of this, new work is forced
>to use char* as the generic pointer that can do pointer arithmitic.
>This continues the existing practice that char* is special. Void*
>should be allowed to do pointer arithmitic so that char* does not
>have to be used for new work.


Why did ANSI decide that one cannot do pointer arithmetic on a void* 
pointer, you ask.

Well, BY DEFINITION, void* means that 
we are dealing with a pointer to an object of unknown type and size.  
In pointer arithmetic, how could a C compiler possibly know how much 
to decrement or increment a pointer by if it didn't know the size of 
the object it was pointing to? 
(rhetorical).

For example, if we had a dynamically allocated array of objects of
unknown size, and "ptr" of type (void*) points to some object in the array,
what sense would "ptr++" make?  Surely, the intent is to reset the
pointer to point to the next object in the array.  But without the
contextual knowledge of the size of each object, the expression "ptr++" 
is not well defined.

I haven't been following this char* vs. void* discussion very closely,
so excuse me if I have missed something crucial and I am way off base.

Donald Maffly

chip@tct.uucp (Chip Salzenberg) (01/04/91)

According to bobj@glinj.gli.com (Robert Jacobs):
>What I can't understand is that ANSI decided that one cannot do pointer
>arithmitic on a void*, like a char*. Because of this, new work is forced
>to use char* as the generic pointer that can do pointer arithmitic.
>This continues the existing practice that char* is special.

Perhaps ANSI figured that since |char *| already serves as the generic
pointer that supports pointer arithmetic, making |void *| do the same
would render |void *| redundant and therefore useless.

As the language stands now, your choice of |char *| or |void *|
communicates to the compiler and to human readers your intentions
as to pointer arithmetic (|char *| if yes, |void *| if no).
-- 
Chip Salzenberg at Teltronics/TCT     <chip@tct.uucp>, <uunet!pdn!tct!chip>
"Please don't send me any more of yer scandalous email, Mr. Salzenberg..."
		-- Bruce Becker