In strongly-typed languages like Java and C#, void
(or Void
) as a return type for a method seem to mean:
This method doesn’t return anything. Nothing. No return. You will not receive anything from this method.
What’s really strange is that in C, void
as a return type or even as a method parameter type means:
It could really be anything. You’d have to read the source code to find out. Good luck. If it’s a pointer, you should really know what you’re doing.
Consider the following examples in C:
void describe(void *thing)
{
Object *obj = thing;
printf("%s.n", obj->description);
}
void *move(void *location, Direction direction)
{
void *next = NULL;
// logic!
return next;
}
Obviously, the second method returns a pointer, which by definition could be anything.
Since C is older than Java and C#, why did these languages adopt void
as meaning “nothing” while C used it as “nothing or anything (when a pointer)”?
12
The keyword void
(not a pointer) means “nothing” in those languages. This is consistent.
As you noted, void*
means “pointer to anything” in languages that support raw pointers (C and C++). This is an unfortunate decision because as you mentioned, it does make void
mean two different things.
I have not been able to find the historical reason behind reusing void
to mean “nothing” and “anything” in different contexts, however C does this in several other places. For example, static
has different purposes in different contexts. There is obviously precedent in the C language for reusing keywords this way, regardless of what one may think of the practice.
Java and C# are different enough to make a clean break to correct some of these issues. Java and “safe” C# also do not allow raw pointers and do not need easy C compatibility (Unsafe C# does allow pointers but the vast majority of C# code does not fall into this category). This allows them to change things up a bit without worrying about backwards compatibility. One way of doing this is introducing a class Object
at the root of the hierarchy from which all classes inherit, so an Object
reference serves the same function of void*
without the nastiness of type issues and raw memory management.
14
void
and void*
are two different things. void
in C means exactly the same thing as it does in Java, an absence of a return value. A void*
is a pointer with an absence of a type.
All pointers in C need to be able to be dereferenced. If you dereferenced a void*
, what type would you expect to get? Remember C pointers don’t carry any runtime type information, so the type must be known at compile time.
Given that context, the only thing you can logically do with a dereferenced void*
is ignore it, which is exactly the behavior the void
type denotes.
5
Perhaps it would be more useful to think of void
as the return type. Then your second method would read “a method that returns an untyped pointer.”
3
Let’s tighten up the terminology a bit.
From the online C 2011 standard:
6.2.5 Types
…
19 Thevoid
type comprises an empty set of values; it is an incomplete object type that cannot be completed.
…
6.3 Conversions
…
6.3.2.2 void1 The (nonexistent) value of a
void
expression (an expression that has typevoid
) shall not
be used in any way, and implicit or explicit conversions (except tovoid
) shall not be
applied to such an expression. If an expression of any other type is evaluated as avoid
expression, its value or designator is discarded. (Avoid
expression is evaluated for its
side effects.)6.3.2.3 Pointers
1 A pointer to
void
may be converted to or from a pointer to any object type. A pointer to
any object type may be converted to a pointer to void and back again; the result shall
compare equal to the original pointer.
A void
expression has no value (although it may have side effects). If I have a function defined to return void
, like so:
void foo( void ) { ... }
then the call
foo();
doesn’t evaluate to a value; I can’t assign the result to anything, because there is no result.
A pointer to void
is essentially a “generic” pointer type; you can assign a value of void *
to any other object pointer type without needing an explicit cast (which is why all the C programmers will yell at you for casting the result of malloc
).
You cannot directly dereference a void *
; you must first assign it to a different object pointer type before you can access the pointed-to object.
void
pointers are used to implement (more-or-less) generic interfaces; the canonical example is the qsort
library function, which can sort arrays of any type, as long as you provide a type-aware comparison function.
Yes, using the same keyword for two different concepts (no value vs. generic pointer) is confusing, but it’s not like there isn’t precedent; static
has multiple distinct meanings in both C and C++.
In Java and C#, void as a return type for a method seem to mean: this method doesn’t return anything. Nothing. No return. You will not receive anything from this method.
That statement is correct. It is correct for C and C++ as well.
in C, void as a return type or even as a method parameter type means something different.
That statement is incorrect. void
as a return type in C or C++ means the same thing that it does in C# and Java. You are confusing void
with void*
. They are completely different.
Isn’t it confusing that
void
andvoid*
mean two completely different things?
Yep.
What is a pointer? What is a void pointer?
A pointer is a value which may be dereferenced. Dereferencing a valid pointer gives a storage location of the pointed-to type. A void pointer is a pointer which has no particular pointed-to type; it must be converted to a more specific pointer type before the value is dereferenced to produce a storage location.
Are void pointers the same in C, C++, Java and C#?
Java does not have void pointers; C# does. They are the same as in C and C++ — a pointer value which does not have any specific type associated with it, that must be converted to a more specific type before dereferencing it to produce a storage location.
Since C is older than Java and C#, why did these languages adopt void as meaning “nothing” while C used it as “nothing or anything (when a pointer)”?
The question is incoherent because it presupposes falsehoods. Let’s ask some better questions.
Why did Java and C# adopt the convention that
void
is a valid return type, instead of, for instance, using the Visual Basic convention that functions are never void returning and subroutines are always void returning?
To be familiar to programmers coming to Java or C# from languages where void
is a return type.
Why did C# adopt the confusing convention that
void*
has a completely different meaning thanvoid
?
To be familiar to programmers coming to C# from languages where void*
is a pointer type.
void describe(void *thing);
void *move(void *location, Direction direction);
The first function returns nothing. The second function returns a void pointer. I would have declared that second function as
void* move(void* location, Direction direction);
If might help if you think of “void” as meaning “without meaning”. The return value of describe
is “without meaning”. Returning a value from such a function is illegal because you told the compiler that the return value is without meaning. You can’t capture the return value from that function because it is without meaning. You can’t declare a variable of type void
because it is without meaning. For example void nonsense;
is nonsense and is in fact illegal. Also note that an array of void void void_array[42];
is also nonsense.
A pointer to void (void*
) is something different. It is a pointer, not an array. Read void*
as meaning a pointer that points to something “without meaning”. The pointer is “without meaning”, which means it doesn’t make sense to dereference such a pointer. Trying to do so is in fact illegal. Code that dereferences a void pointer won’t compile.
So if you can’t dereference a void pointer, and you can’t make an array of voids, how can you use them? The answer is that a pointer to any type can be cast to and from void*
. Casting some pointer to void*
and casting back to a pointer to the original type will yield a value equal to the original pointer. The C standard guarantees this behavior. The primary reason you see so many void pointers in C is that this capability provides a way to implement information hiding and object-based programming in what is very much a non-object oriented language.
Finally, the reason you often see move
declared as void *move(...)
rather than void* move(...)
is because putting the asterisk next to the name rather than the type is a very common practice in C. The reason is that the following declaration will get you in big trouble: int* iptr, jptr;
This would make you think you are declaring two pointers to an int
. You are not. This declaration makes jptr
an int
rather than a pointer to an int
. The proper declaration is int *iptr, *jptr;
You can pretend the asterisk belongs with the type if you stick to the practice of only declaring one variable per declaration statement. But keep in mind that you are pretending.
2
Simply put, there is no difference. Let me give you some examples.
Say I have an class called Car.
Car obj = anotherCar; // obj is now of type Car
Car* obj = new Car(); // obj is now a pointer of type Car
void obj = 0; // obj has no type
void* = new Car(); // obj is a pointer with no type
I believe here the confusion here is based off how you would define void
vs void*
. A return-type of void
means “I return nothing”, while a return type of void*
means “I return a pointer of type nothing”.
This is due to the fact that a pointer is a special case. A pointer object will always point to a location in memory, whether there’s a valid object there or not. Whether my void* car
references a byte, a word, a Car, or a bicycle doesn’t matter because my void*
points to something with no defined type. It’s up to us to later on define it.
In languages such as C# and Java, memory is managed and the concept of pointers is rolled over into the Object class. Instead of having a reference to an object of type “nothing” we know that at the very least our object is of type “Object”. Let me explain why.
In conventional C/C++ if you create an object and delete its pointer, then it’s impossible to get access to that object. This is what’s known as a memory leak.
In C#/Java, everything is an object and this enables the runtime to keep track of every object. It doesn’t necessarily need to know that my object is a Car, it simply needs to know some basic information that is already taken care of by the Object class.
For us programmers, that means that much of the information we would have to manually take care of in C/C++ is taken care of for us by the Object class, removing the need for the special case that is the pointer.
1
I’ll try to say how one could think of these things in C. The official language in the C standard is not really at ease with void
, and I won’t try to be entirely consistent with it.
The type void
is not a first class citizen among the types. Although it is an object type, it cannot be the type of any object (or value), of a field, or of a function parameter; however it can be the return type of a function, and thereby be the type of an expression (basically of calls of such functions, but expressions formed with the conditional operator can also have void type). But even the minimal use of void
as a value type that the above leaves the door open for, namely ending a function returning void
with a statement of the form return E;
where E
is an expression of type void
, is explicitly forbidden in C (it is allowed in C++ though, but for reasons not applicable to C).
If objects of type void
were allowed, they would have 0 bits (that is the amount of information in the value of an expression of void
type); it would not be a major problem allowing such objects, but they would be rather useless (objects of size 0 would give some difficulty with defining pointer arithmetic, which would maybe best just be forbidden). The set of different values such an object would have one (trivial) element; its value could be taken, trivially because it does not have any substance, but it cannot be modified (lacking any different value). The standard is therefore confused in saying void
comprises an empty set of values; such a type could be useful to describe expressions that cannot be evaluated (such as jumps or non-terminating calls) although the C language does not actually employ such a type.
Being disallowed as value type, void
has been used in at least two not directly related ways to designate “forbidden”: specifying (void)
as parameter specification in function types means providing any arguments to them is forbidden (while ‘()’ would to the contrary mean everything is allowed; and yes, even providing a void
expression as argument to functions with (void)
parameter specification is forbidden), and declaring void *p
means that the dereferencing expression *p
is forbidden (but not that it is a valid expression of type void
). So you are right that these uses of void
are not really consistent with void
as a valid type of expressions. However an object of type void*
is not really a pointer to values of any type, it means a value that is treated as a pointer although it cannot be dereferenced and pointer arithmetic with it is forbidden. The “treated as a pointer” then really only means it can be cast from and to any pointer type without loss of information. However it can also be used to cast integer values to and from, so it does not have to point to anything at all.
1
In C# and Java, every class is derived from an Object
class. Hence whenever we wish to pass a reference to “something”, we can use a reference of type Object
.
Since we don’t need to use void pointers for that purpose in these languages, void
doesn’t have to mean “something or nothing” here.
In C, it is possible to have a pointer to things of any type [pointers behave as a type of reference]. A pointer may identify an object of a known type, or it may identify an object of arbitrary (unknown) type. The creators of C chose to use the same general syntax for “pointer to thing of arbitrary type” as for “pointer to thing of some particular type”. Since the syntax in the latter case requires a token to specify what the type is, the former syntax requires putting something where that token would belong if the type were known. C chose to use the keyword “void” for that purpose.
In Pascal, as with C, it is possible to have pointers to things of any type; more popular dialects of Pascal also allow “pointer to thing of arbitrary type”, but rather than using the same syntax as they use for “pointer to thing of some particular type”, they simply refer to the former as Pointer
.
In Java, there are no user-defined variable types; everything is either a primitive or a heap object reference. One can define things of type “reference to a heap object of a particular type” or “reference to a heap object of arbitrary type”. The former simply uses the type name without any special punctuation to indicate that it’s a reference (since it can’t be anything else); the latter uses the type name Object
.
The use of void*
as the nomenclature for a pointer to something of arbitrary type is so far as I know unique to C and direct derivatives like C++; other languages I know of handle the concept by simply using a distinctly-named type.
You can kind of think of the keyword void
like an empty struct
(In C99 empty structs are apparently disallowed, in .NET and C++ they take up more than 0 memory, and last I recall everything in Java is a class):
struct void {
};
…which means it’s a type with 0 length. This is how it’s working when you do a function call that returns void
… instead of allocating 4 bytes or so on the stack for an integer return value, it doesn’t change the stack pointer at all (increments it by a length of 0).
So, if you declared some variables like:
int a;
void b;
int c;
… and then you took the address of those variables, then perhaps b
and c
might be located at the same place in memory, because b
has zero length. So a void*
is a pointer in memory to the built-in type with zero length. The fact that you might know that there’s something immediately after it that might be useful to you is another matter, and requires you to really know what you’re doing (and is dangerous).
1