Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding the exact meaning of the "void" Keyword in C/C++

Tags:

c++

c

As explained, for example, here, there are 3 main uses for the void keyword (more experienced C/C++ programmers can skip to the 4th use):

1) As a return type for function that doesn't return anything. This will cause a code sample like this:

void foo();
int i = foo();

to generate a compiler error.

2) As the only parameter in a function's parameter list. AFAIK, an empty function's parameter list is exactly the same to the compiler and therefore the following 2 lines are identical in meaning: (edit: it is only true in c++. The comments show the difference in c).

int foo();
int foo(void);

3) void* is a special type of generic pointer- it can point to any variable that is not declared with the const or volatile keyword, convert to/from any type of data pointer, and point to all non-member functions. In addition, it cannot be dereferenced. I will not give examples.

There is also a 4th use that I don't fully understand:

4) In conditional compilation it is often used in the expression (void)0 as following:

// procedure that actually prints error message 
void _assert(char* file, int line, char* test); 
#ifdef NDEBUG 
#define assert(e) ((void)0) 
#else
#define assert(e)     \
((e) ? (void)0 :   \
__assert(__FILE__, __LINE__, #e)) 
#endif

I'm trying to understand the behavior of this expression through experiments. All the following are valid (compile well):

int foo(); // some function declaration
int (*fooPtr)(); // function pointer
void(foo);
void(fooPtr);
void(0);
(void)0;
void('a');
void("blabla");
exampleClass e; //some class named exampleClass with a default ctor
void(e);
static_cast<void>(e);

but these are not:

void(0) // no semicolon
int i = void(0);

Can I conclude from this that "void" (in the context of the 4th use) is simply a special type that any type can cast to it (whether it is c-style or cpp-style), and it can never be used as an lvalue or rvalue?

like image 278
infokiller Avatar asked Nov 06 '11 02:11

infokiller


2 Answers

Can I conclude from this that "void" (in the context of the 4th use) is simply a special type that any type can cast to it (whether it is c-style or cpp-style), and it can never be used as an lvalue or rvalue?

James McNellis pointed out in a comment above that void can be used as an rvalue via the expression void().

He quoted this from the current C++ standard:

C++11 §5.2.3/2:
“The expression T(), where T is a simple-type-specifier or typename-specifier for a non-array complete object type or the (possibly cv-qualified) void type, creates a prvalue of the specified type, which is value-initialized (no initialization is done for the void() case).”

This makes it possible to write code like …

template< class T >
T foo() { return T(); }

and use foo<void>() (most likely from other templated code, I would imagine).

Formally void is just an incomplete type that can never be completed.

like image 140
Cheers and hth. - Alf Avatar answered Oct 26 '22 04:10

Cheers and hth. - Alf


As for your 2), in C these:

int foo();
int foo(void);

are not equivalent.

The first is an old-style declaration (still supported in the latest C standard) that says foo takes a fixed but unspecified number and type of argument(s). A call like foo(42) doesn't require a diagnostic, but its behavior is undefined unless the definition of foo says it has a single int parameter.

The second says specifically that foo takes no arguments, and foo(42) requires a diagnostic. The second declaration is a prototype; the first is not. The (void) syntax was added because some special syntax was needed to be able to declare a parameterless function without writing something that looks like an old-style declaration.

In new C code, you should always use prototypes. Old-style declarations are kept in the language only to support old code.

C++ dropped old-style declarations. In C++, the two declarations of foo are equivalent; the int foo(void) form is supported only for compatibility with C.

Your case 4) really has nothing directly to do with conditional compilation. Casting an expression to void is a way of evaluating the expression and explicitly discarding its value. It's often used to inhibit warnings. For example, if foo() returns an int result, then

foo();

as a standalone statement might trigger a warning that you're discarding the result, but

(void)foo();

probably tells the compiler that you intended to do so.

void is an incomplete type that cannot be completed. That means that an expression of type void can only be used in a context that doesn't expect a value. And you're right, an expression of type void cannot be used as an lvalue or rvalue. EDIT: Except in the case Alf described, and only in C++.

like image 40
Keith Thompson Avatar answered Oct 26 '22 03:10

Keith Thompson