问题
The void
type in C seems to be strange from various different situations. Sometimes it behaves like a normal object type, such as int
or char
, and sometimes it just means nothing (as it should).
Look at my snippet. First of all, it seems strange that you can declare a void
object, meaning you just declare nothing.
Then I created an int
variable and casted its result to void
, discarding it:
If an expression of any other type is evaluated as a void expression, its value or designator is discarded. (ISO/IEC 9899:201x, 6.3.2.2 void)
I tried to call my function with a void
cast, but my compiler gave me (Clang 10.0):
error: too many arguments to function call, expected 0, have 1
So the void
in a prototype means nothing, and not the type void
.
But then, I created a pointer to void
, dereferenced it, and assigning the “result” to my int
variable. I got the “incompatible type” error. That means the void
type does exist here.
extern void a; // Why is this authorised ???
void foo(void); // This function takes no argument. Not the 'void' type.
int main(void)
{
int a = 42;
void *p;
// Expression result casted to 'void' which discards it (per the C standard).
(void)a;
// Casting to 'void' should make the argument inexistant too...
foo((void)a);
// Assigning to 'int' from incompatible type 'void': so the 'void' type does exists...
a = *p;
// Am I not passing the 'void' type ?
foo(*p);
return 0;
}
Is void
an actual type, or a keyword to means nothing ? Because sometimes it behaves like the instruction “nothing is allowed here”, and sometimes like an actual type.
EDIT: This questions is NOT a duplicate. It is a purely about the semantics of the void
type. I do not want any explanation about how to use void
, pointers to void
or any other things. I want an answer per the C standard.
回答1:
In C language the void
type has been introduced with the meaning of 'don't care' more than 'null' or 'nothing', and it's used for different scopes.
The void
keyword can reference a void type
, a reference to void
, a void expression
, a void operand
or a void function
. It also explicitly defines a function having no parameters.
Let's have a look at some of them.
The void
type
First of all void
object exists and have some special properties, as stated in ISO/IEC 9899:2017, §6.2.5 Types:
- The void type comprises an empty set of values; it is an incomplete object type that cannot be completed.
Pointers
The more useful reference to void
, or void *
, is a reference to an incomplete type, but itself is well defined, and then is a complete type, have a size, and can be used as any other standard variable as stated in ISO/IEC 9899:2017, §6.2.5 Types:
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 compatible types shall have the same representation and alignment requirements.
All pointers to structure types shall have the same representation and alignment requirements as each other.
All pointers to union types shall have the same representation and alignment requirements as each other.
Pointers to other types need not have the same representation or alignment requirements.
Casting to void
It can be used as cast to nullify an expression, but allowing the completion of any side effect of such expression. This concept is explained in the standard at ISO/IEC 9899:2017, §6.3 Conversions, §6.3.2.2 void:
The (nonexistent) value of a void expression (an expression that has type void) shall not be used in any way, and implicit or explicit conversions (except to void) shall not be applied to such an expression.
If an expression of any other type is evaluated as a void expression, its value or designator is discarded. (A void expression is evaluated for its side effects.)
A practical example for the casting to void
is its use to prevent warning for unused parameters in function definition:
int fn(int a, int b)
{
(void)b; //This will flag the parameter b as used
... //Your code is here
return 0;
}
The snippet above shows the standard practice used to mute compiler warnings. The cast to void
of parameter b
acts as an effective expression that don't generate code and marks b
as used preventing compiler complains.
void
Functions
The paragraph §6.3.2.2 void of the standard, covers also some explanation about void
functions, that are such functions that don't return any value usable in an expression, but functions are called anyway to implement side effects.
void
pointers properties
As we said before, pointers to void
are much more useful because they allow to handle objects references in a generic way due to their property explained in ISO/IEC 9899:2017, §6.3.2.3 Pointers:
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.
As practical example imagine a function returning a pointer to different objects depending on input parameters:
enum
{
FAMILY, //Software family as integer
VERSION, //Software version as float
NAME //Software release name as char string
} eRelease;
void *GetSoftwareInfo(eRelease par)
{
static const int iFamily = 1;
static const float fVersion = 2.0;
static const *char szName = "Rel2 Toaster";
switch(par)
{
case FAMILY:
return &iFamily;
case VERSION:
return &fVersion;
case NAME:
return szName;
}
return NULL;
}
In this snippet you can return a generic pointer that can be dependent on input par
value.
void
as functions parameter
The use of void
parameter in functions definitions was introduced after the, so called, ANSI-Standard, to effectively disambiguate functions having variable number of arguments from functions having no arguments.
From standard ISO/IEC 9899:2017, 6.7.6.3 Function declarators (including prototypes):
- The special case of an unnamed parameter of type
void
as the only item in the list specifies that the function has no parameters.
Actual compilers still support function declaration with empty parenthesis for backward compatibility, but this is an obsolete feature that will eventually be removed in future release of standard. See Future directions - §6.11.6 Function declarators:
- The use of function declarators with empty parentheses (not prototype-format parameter type declarators) is an obsolescent feature.
Consider the following example:
int foo(); //prototype of variable arguments function (backward compatibility)
int bar(void); //prototype of no arguments function
int a = foo(2); //Allowed
int b = foo(); //Allowed
int c = bar(); //Allowed
int d = bar(1); //Error!
Now resembling your test, if we call the function bar
as follows:
int a = 1;
bar((void)a);
Triggers an error, because casting to void
an object doesn't null it. So you are still trying to pass a void
object as parameter to a function that don't have any.
Side effects
As requested this is a short explain for side effects concept.
A side effect is whichever alteration of objects and values derived from the execution of a statement, and which are not the direct expected effect.
int a = 0;
(void)b = ++a;
In the snippet above the void expression lose the direct effect, assigning b
, but as side effect increase the value of a
.
The only reference, explaining the meaning, in the standard can be found in 5.1.2.3 Program execution:
Accessing a volatile object, modifying an object, modifying a file, or calling a function that does any of those operations are all side effects, which are changes in the state of the execution environment.
Evaluation of an expression in general includes both value computations and initiation of side effects.
回答2:
void
is a type. Per C 2018 6.2.5 19, the type has no values (the set of values it can represent is empty), it is incomplete (its size is unknown), and it cannot be completed (its size cannot be known).
Regarding extern void a;
, this does not define an object. It declares an identifier. If a
were used in an expression (except as part of a sizeof
or _Alignof
operator), there would have to be a definition for it somewhere in the program. Since there cannot a definition of void
object in strictly conforming C, a
cannot be used in an expression. So I think this declaration is allowed in strictly conforming C but is not useful. It might be used in C implementations as an extension that allows getting the address of an object whose type is not known. (For example, define an actual object a
in one module, then declare it as extern void a;
in another module and use &a
there to get its address.)
The declaration of functions with (void)
as a parameter list is a kludge. Ideally, ()
might be used to indicate a function takes no parameters, as is the case in C++. However, due to the history of C, ()
was used to mean an unspecified parameter list, so something else had to be invented to mean no parameters. So (void)
was adopted for that. Thus, (void)
is an exception to the rules that would say (int)
is for a function taking an int
, (double)
is for a function taking a double, and so on—(void)
is a special case meaning that a function takes no parameters, not that it takes a void
.
In foo((void) a)
, the cast does not make the value “not exist.” It converts a
to the type void
. The result is an expression of type void
. That expression “exists,” but it has no value and cannot be used in an expression, so using it in foo((void) a)
results in an error message.
回答3:
From C Standard#6.2.5p19:
19 The void type comprises an empty set of values; it is an incomplete object type that cannot be completed.
This indicate that the void
type exists.
Doubt 1:
void foo(void); // This function takes no argument. Not the 'void' type.
Correct.
From C Standard#6.7.6.3p10 [emphasis mine]:
10 The special case of an unnamed parameter of type void as the only item in the list specifies that the function has no parameters.
This is a special case they had to add to the language syntax because void foo();
already meant something different (void foo();
doesn't specify anything about foo
's parameters). If it weren't for the old meaning of void foo();
, void foo();
would have been the syntax to declare a no-argument function. You can't generalize anything from this. It's just a special case.
Doubt 2:
// Casting to 'void' should make the argument inexistant too...
foo((void)a);
No, it will not because void
is also an object type though it is incomplete.
Doubt 3:
// Assigning to 'int' from incompatible type 'void': so the 'void' type does exists...
a = *p;
Yes, it does exist and hence the compiler is reporting error on this statement.
Doubt 4:
// Am I not passing the 'void' type ?
foo(*p);
Declaration of foo()
function:
void foo(void);
^^^^
The void
in parameter list indicates that function will not take any argument because it has been declared with no parameters.
Just for reference, check this from C Standard#5.1.2.2.1p1 [emphasis mine]:
1 The function called at program startup is named main. The implementation declares no prototype for this function. It shall be defined with a return type of int and with no parameters:
int main(void) { /* ... */ } ^^^^
Doubt 5:
extern void a; // Why is this authorised ???
This is authorized because void
is a valid type and it is just a declaration. No storage will allocate to a
.
回答4:
In C, void
can't be considered as a data type, it is a keyword used as a placeholder in place of a data type to show that actually there is no data. Hence this
void a;
is not valid.
while here
void foo(void);
void
keyword is used to inform to the compiler that foo
is not going to take any input argument nor it has return type.
In below case
int a = 42;
void *p;
a = *p; /* this causes error */
a = *p;
is wrong because you can't dereference void pointer directly, you need to perform proper type casting first. for e.g
a = *(int*)p; /* first typecast and then do dereference */
Also this
foo(*p);
is wrong because of two reason,
- firstly
foo()
doesn't expects any argument. - secondly you can't do
*p
asp
is void pointer. Correct one isfoo(*(int*)p);
iffoo()
declaration isvoid foo(int);
.
Note that this
(void)a;
doesn't do anything so your compiler might not giving any warning but when you do like
int b = (void)a;
compiler won't allow as void
is not consider as data type.
Finally this
extern void a; // Why is this authorised ???
this is just a declaration not definition, a
doesn't exist until you define it, since a
is having extern
storage class, you need to define somewhere & when you are going define like
a = 10;
compiler throws a error as
error: ‘a’ has an incomplete type
From C standard 6.2.5 Types
The
void
type comprises anempty set of values
; it is an incomplete object type that cannot be completed.
6.3.2.2 void
The (nonexistent) value of a
void
expression (an expression that has type void) shall not be used in any way, and implicit or explicit conversions (except to void) 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. (A void expression is evaluated for its side effects.)
6.3.2.3 Pointers
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
storage-class specifier
or type qualifier modifies the keywordvoid
as a function parameter type list (6.7.6.3).An attempt is made to use the value of a
void
expression, or an implicit or explicit conversion (except to void) is applied to a void expression (6.3.2.2).
回答5:
First of all, it seems strange that you can declare a void object, meaning you just declare nothing.
void
is an incomplete object type that cannot be completed. This mostly defines its uses in regular contexts, i.e. contexts that do not provide special treatment for void
. Your extern
declaration is one of such regular contexts. It is OK to use an incomplete data type in a non-defining declaration.
However, you will never be able to provide a matching definition for that declaration.
So the void in a prototype means nothing, and not the type void.
Correct. The parameter must be unnnamed. And the (void)
combination is given special treatment: it is not one parameter of type void
, but rather no parameters at all.
But then, I created a pointer to void, dereferenced it, and assigning the “result” to my int variable. I got the “incompatible type” error. That means the void type does exist here.
No. It is illegal to apply unary *
operator to a void *
pointer. Your code is invalid for that reason already. Your compiler issued a misleading diagnostic message. Formally, diagnostic messages are not required to properly describe the root of the problem. The compiler could've just said "Hi!".
Is void an actual type, or a keyword to means nothing ?
It is a type. It is an incomplete object type that cannot be completed.
来源:https://stackoverflow.com/questions/53970709/the-void-type-in-c