C中的void类型 [英] The void type in C

查看:117
本文介绍了C中的void类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在各种不同情况下,C语言中的 void 类型似乎很奇怪.有时它的行为类似于普通的对象类型,例如 int char ,有时它只是没有意义(应该如此).

看看我的片段.首先,您可以声明一个 void 对象似乎很奇怪,这意味着您什么都不声明.

然后我创建了一个 int 变量并将其结果转换为 void 并丢弃:

如果任何其他类型的表达式被评估为void表达式,其值或指示符将被丢弃.(ISO/IEC 9899:201x,6.3.2.2无效)

我尝试使用 void 强制转换来调用函数,但是我的编译器给了我(Clang 10.0):

错误:函数调用的参数过多,预​​期为0,具有1

因此,原型中的 void 表示没什么,而不是 void 类型.

但是,然后,我创建了一个指向 void 的指针,将其取消引用,然后将"结果"分配给我的 int 变量.我收到"不兼容类型"错误.这意味着 void 类型确实存在.

  extern void a;//为什么要为此授权???void foo(void);//此函数不带参数.不是"void"类型.int main(无效){整数a = 42;无效* p;//将表达式结果强制转换为"void",并将其丢弃(根据C标准).(无效)//强制转换为'void'也会使参数不存在...foo((void)a);//从不兼容类型'void'分配给'int':因此'void'类型的确存在...a = * p;//我没有传递'void'类型吗?foo(* p);返回0;} 

void 是实际类型,还是没有任何意义的关键字?因为有时它的行为就像"此处不允许任何东西"这样的指令,有时又像实际的类型一样.

编辑:此问题不是重复.这纯粹是关于 void 类型的语义的.我不想要任何有关如何使用 void ,指向 void 的指针或任何其他东西的解释.我想要按照C标准的答案.

解决方案

在C语言中,引入了 void 类型,其含义是不在乎"而不是"null"或一无所有",它用于不同的范围.

void 关键字可以引用 void类型 void 的引用, void表达式 void操作数 void函数.它还明确定义了没有参数的函数.

让我们看看其中的一些.


void 类型

首先, void 对象存在并且具有某些特殊属性,如 ISO/IEC 9899:2017,第6.2.5节类型中所述:

  1. void类型包含一组空值;这是一种不完整的对象类型,无法完成.


指针

对void 或 void * 的更有用的引用是对不完整类型的引用,但其自身定义良好,然后又是一个完整类型,具有大小,并且可以用作 ISO/IEC 9899:2017§6.2.5类型中所述的任何其他标准变量:

  1. 指向void的指针应与指向字符类型的指针具有相同的表示形式和对齐要求.

    类似地,指向兼容类型的合格或不合格版本的指针应具有相同的表示和对齐要求.

    所有指向结构类型的指针应具有相同的表示和对齐要求.

    所有指向联合类型的指针应具有相同的表示和对齐要求.

    指向其他类型的指针不必具有相同的表示或对齐要求.


投射到 void

它可以用作 cast 来使表达式无效,但可以完成该表达式的任何副作用.在 ISO/IEC 9899:2017§6.3转换,§6.3.2.2无效:

中的标准中对此概念进行了说明.

  1. 不得以任何方式使用void表达式(具有void类型的表达式)的(不存在)值,并且不得将隐式或显式转换(除void外)应用于此类表达式.

    如果任何其他类型的表达式被评估为void表达式,则其值或指示符将被丢弃.(将评估void表达式的副作用.)

将其强制转换为 void 的一个实际示例是它用于防止对函数定义中未使用的参数发出警告:

  int fn(int a,int b){(无效)b;//这会将参数b标记为已使用...//您的代码在这里返回0;} 

上面的代码片段显示了用于使编译器警告静音的标准做法.将参数 b void 强制转换为不生成代码的有效表达式,并将 b 标记为用于防止编译器抱怨.


void 函数

该标准的§6.3.2.2无效段落还涵盖了有关 void 函数的一些解释,这些函数不会返回任何可用的值.表达式,但无论如何都调用函数来实现副作用.


void 指针属性

正如我们之前所说,指向 void 的指针要有用得多,因为由于 ISO/IEC 9899:2017,§中介绍的属性,它们允许以通用方式处理对象引用.6.3.2.3指针:

  1. 指向void的指针可以与任何对象类型的指针进行转换.

    指向任何对象类型的指针都可以转换为void指针,然后再返回;结果应等于原始指针.

作为一个实际示例,想象一个函数根据输入参数返回一个指向不同对象的指针:

 枚举{FAMILY,//软件家族为整数VERSION,//软件版本为floatNAME//软件版本名称为char字符串}电子发行;无效* GetSoftwareInfo(eRelease par){静态const int iFamily = 1;静态const float fVersion = 2.0;静态常量* char szName ="Rel2 Toaster";开关(标准){案例家族:返回& iFamily;案例版本:返回& fVersion;案例名称:返回szName;}返回NULL;} 

在此代码段中,您可以返回取决于输入 par 值的通用指针.


void 作为函数参数

在所谓的ANSI标准之后,引入了在函数定义中使用 void 参数的方法,以有效地消除具有可变参数个数的函数与具有无参数的函数之间的歧义.>.

根据标准 ISO/IEC 9899:2017,6.7.6.3函数声明符(包括原型):

  1. 列表中的唯一项是 void 类型的未命名参数的特殊情况,它表示该函数没有参数.

实际的编译器仍支持带有空括号的函数声明以实现向后兼容性,但这是一个过时的功能,最终将在将来的标准版本中删除.请参见将来的方向-§6.11.6函数声明符:

  1. 使用带空括号的函数声明符(不是原型格式的参数类型声明符)是过时的功能.

请考虑以下示例:

  int foo();//可变参数函数的原型(向后兼容)int bar(无效);//无参数的原型函数int a = foo(2);//允许int b = foo();//允许int c = bar();//允许int d = bar(1);//错误! 

现在类似于您的测试,如果我们按以下方式调用函数 bar :

  int a = 1;bar((void)a); 

触发一个错误,因为将对象强制转换为 void 不会使该对象为空.因此,您仍在尝试将 void 对象作为参数传递给没有任何功能的函数.


副作用

根据要求,这是副作用概念的简短说明.

副作用是从语句执行中得到的对象和值的任何改变,而不是直接的预期效果.

  int a = 0;(void)b = ++ a; 

在void表达式上方的代码段中,失去了直接效果,分配了 b ,但是由于副作用而增加了 a 的值./p>

可以在 5.1.2.3程序执行中找到该标准中唯一的解释含义的参考:

  1. 访问易失性对象,修改对象,修改对象文件,或调用执行任何这些操作的函数副作用,即执行状态的变化环境.

    表达式的评估通常包括两个值计算和副作用的产生.

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.

解决方案

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:

  1. 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:

  1. 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:

  1. 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:

  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.

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

  1. 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:

  1. 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:

  1. 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.

这篇关于C中的void类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆