【问题标题】:Understanding the exact meaning of the "void" Keyword in C/C++理解 C/C++ 中“void”关键字的确切含义
【发布时间】:2011-11-06 02:16:23
【问题描述】:

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

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

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

生成编译器错误。

2) 作为函数参数列表中的唯一参数。 AFAIK,空函数的参数列表与编译器完全相同,因此以下两行含义相同: (编辑:这仅在 c++ 中是正确的。cmets 显示了 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”(在第四次使用的上下文中) 只是一种特殊类型,任何类型都可以转换为它(无论是 c-style 还是 cpp- style),它永远不能用作左值或右值?

【问题讨论】:

  • 2 仅适用于 C++,在 C 中,空函数的参数列表意味着该函数采用任意数量的未指定参数。
  • 您还可以从返回类型为 void 的函数返回计算结果为 void 的表达式。
  • 最后一个问题的答案是否定的:void() 创建了一个void 类型的右值。
  • @KerrekSB: void(e) == (void)e == static_cast&lt;void&gt;(e)。表示故意不使用变量是一种常见的习惯用法。
  • 我认为尝试理解 void 本身的含义并不值得。它只是一个关键字,在不同的上下文中意味着不同的事物。同样,关键字static 在 C++ 中意味着 8 种不同的东西,我认为您不会找到一个完全解释它的定义。

标签: c++ c


【解决方案1】:

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

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

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

C++11 §5.2.3/2:
“表达式T(),其中Tsimple-type-specifiertypename-specifier,用于非数组完整对象类型或(可能是cv-限定)void 类型,创建指定类型的纯右值,它是值初始化的(void() 情况下不进行初始化)。”

这使得编写类似...的代码成为可能

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

并使用foo&lt;void&gt;()(我想很可能来自其他模板代码)。

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

【讨论】:

    【解决方案2】:

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

    int foo();
    int foo(void);
    

    等价的。

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

    第二个特别说明foo 不接受任何参数,而foo(42) 需要诊断。第二个声明是原型;第一个不是。添加(void) 语法是因为需要一些特殊语法才能声明无参数函数,而无需编写看起来像旧式声明的内容。

    在新的 C 代码中,您应该始终使用原型。旧式声明保留在语言中只是为了支持旧代码。

    C++ 放弃了旧式声明。在 C++ 中,foo 的两个声明 是等价的;支持 int foo(void) 形式只是为了与 C 兼容。

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

    foo();
    

    作为一个独立的语句可能会触发一个警告,表明您正在丢弃结果,但是

    (void)foo();
    

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

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

    【讨论】:

    • int foo() 和 int foo(void) 在 C++ 中是等价的,而不是在 C 中。
    • @SalvatorePreviti:我相信这正是我所说的。
    • 使用强制转换为void 来禁止警告是不正当的,但如果有人试图使用类似函数的宏的值,那么当您想要创建 错误时它会非常有用这是为了不返回值。
    【解决方案3】:

    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* 并再次返回是不可移植的。

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

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

    #4 也用于向编译器明确指示不需要某个值。

    // Generates "unused parameter warning"
    int func(int x)
    {
        return 3;
    }
    
    // Does not generate any warnings
    int func(int x)
    {
        (void) x;
        return 3;
    }
    

    我建议始终为您的项目打开未使用的参数警告,因为它们经常让您发现简单的拼写错误。

    【讨论】:

      【解决方案4】:

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

      对于void*,我想到了三个常见用途:1) 它大到足以容纳任何对象指针。 2) 对 void 指针的动态强制转换产生一个指向最派生类型的指针。 3)它是原始内存指针的类型,即std::malloc()::operator new()的结果类型,std::free()::operator delete()的参数类型,以及placement-new。它不应该用于严格的 C++ 中的任何其他内容,尽管它也用作 C 库中 freadfwrite 的缓冲区参数。

      【讨论】:

      • void() 创建一个 void 类型的右值(或者,在 C++11 中,一个纯右值)。
      • @JamesMcNellis:你怎么会有一个不完整类型的值?
      • C++11 §5.2.3/2:“表达式T(),其中Tsimple-type-specifiertypename-非数组完整对象类型或(可能是 cv 限定的)void 类型的说明符 创建指定类型的纯右值,它是值初始化的(void() 情况下不进行初始化)。 "
      猜你喜欢
      • 2014-02-17
      • 2021-06-13
      • 2011-04-16
      • 1970-01-01
      • 2015-05-16
      • 2018-04-03
      • 1970-01-01
      • 2019-12-08
      • 1970-01-01
      相关资源
      最近更新 更多