理解C / C ++中“void”关键字的确切含义

时间:2011-11-06 02:16:24

标签: c++ c

正如所解释的那样,here,void关键字有3个主要用途(更有经验的C / C ++程序员可以跳到第4次使用):

1)作为不返回任何内容的函数的返回类型。这个     将导致这样的代码示例:

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

生成编译器错误。

2)作为函数参数列表中的唯一参数。 AFAIK,一个空函数的参数列表与编译器完全相同,因此以下两行的含义相同: (编辑:只有在c ++中才有效。评论显示c中的差异。)

int foo();
int foo(void);

3)void *是一种特殊类型的通用指针 - 它可以指向任何未使用const或volatile关键字声明的变量,转换为/来自任何类型的数据指针,以及指向所有非成员函数。另外,它不能被解除引用。我不会举例。

还有第四种用法,我不太了解:

4)在条件编译中,它通常在表达式(void)0中使用,如下所示:

// 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

我试图通过实验来理解这个表达的行为。以下所有内容均有效(编译良好):

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);

但这些不是:

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

我能否从中得出结论:“void”(在第4次使用的上下文中)只是一种特殊类型,任何类型都可以强制转换为它(无论是c风格还是cpp-样式),它永远不能用作左值或左值?

4 个答案:

答案 0 :(得分:7)

  

我可以从中得出结论,“void”(在第4次使用的上下文中)只是一种特殊类型,任何类型都可以强制转换它(无论是c风格还是cpp风格),它永远不会用作左值还是左值?

James McNellis在a comment above中指出void可以通过表达式void()用作右值。

他引用了当前的C ++标准:

  

C ++11§5.2.3/ 2:
  “表达式T(),其中T简单类型说明符 typename-specifier ,用于非数组完整对象类型或(可能是cv-qualified)void类型,创建一个指定类型的prvalue,它是值初始化的(没有为void()情况进行初始化)。“

这使得编写像......

这样的代码成为可能
template< class T >
T foo() { return T(); }

并使用foo<void>()(很可能来自其他模板化代码,我会想象)。

正式void只是永远无法完成的不完整类型

干杯&amp;第h

答案 1 :(得分:6)

至于你的 2),在C中这些:

int foo();
int foo(void);

不等于

第一个是旧式声明(在最新的C标准中仍然支持),表示foo采用固定的但未指定的数字和参数类型。像foo(42)之类的调用不需要诊断,但除非foo定义表明它有一个int参数,否则其行为是未定义的。

第二个具体说明foo没有参数,foo(42)需要诊断。第二个宣言是原型;第一个不是。添加了(void)语法是因为需要一些特殊的语法才能声明无参数函数而无需编写看起来像旧式声明的东西。

在新的C代码中,您应始终使用原型。旧式声明仅保留在语言中以支持旧代码。

C ++删除了旧式声明。在C ++中,foo 的两个声明是等价的;只有与C兼容才支持int foo(void)表单。

您的案例 4)与条件编译没有任何直接关系。将表达式转换为void是一种评估表达式并明确丢弃其值的方法。它通常用于禁止警告。例如,如果foo()返回int结果,则

foo();

作为独立语句可能会触发警告您要丢弃结果,但

(void)foo();

可能告诉编译器你打算这样做。

void是一个无法完成的不完整类型。这意味着类型void的表达式只能 才能在不期望值的上下文中使用。你是对的,类型void的表达式不能用作左值或右值。 编辑:除了Alf所描述的情况外,仅限于C ++。

答案 2 :(得分:4)

Void是一种没有值的类型。由于它没有值,因此void类型的表达式只能用于它们的副作用。以下是一些更正:

#1 是的。

#2 如上所述,在C ++中是正确的,但在C中是不正确的。

int foo(); // In C, this is an "old-style" prototype
           // which doesn't declare its parameters

// Definition for the above function: valid in C, not valid in C++
int foo(int x, int y) { return x + y; }

#3 void*是一种特殊类型的通用指针:不完全。

void*

但是,您可以将任何函数指针转换为C中的任何其他函数指针类型,然后再返回,而不必担心可移植性。

#4 还用于向编译器明确指出不需要值。

void func(void);
void *x = func;
void (*f)(void) = func; // NOT portable

我建议您始终为项目启用未使用的参数警告,因为它们通常会让您捕获简单的拼写错误。

答案 3 :(得分:2)

void始终是不完整的类型,这意味着您永远不能实例化void类型的任何对象。 因此,不能有此类型的任何值,既不是L也不是R. 虽然void()void*,但不能有这种类型的左值。 (p)的右值。 [感谢@James!]

对于std::malloc(),我想到了三个常见用途:1)它足以容纳任何对象指针。 2)dynamic-cast to void指针产生一个指向派生类型最多的指针。 3)它是原始内存指针的类型,即::operator new()std::free()的结果类型,::operator delete()fread的参数类型,以及placement-new的参数类型。它不应该用于严格的C ++中的任何其他内容,尽管它也在后一个角色中用作C库中fwrite和{{1}}的缓冲区参数。