热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

C中的void类型

如何解决《C中的void类型》经验,为你挑选了3个好方法。

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

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

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

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

我试图用void演员调用我的函数,但是我的编译器给了我(Clang 10.0):

error: too many arguments to function call, expected 0, have 1

所以void在原型中没有任何意义,而不是类型void.

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

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

void实际类型,还是关键字意味着什么?因为有时它的行为类似于" 这里不允许任何东西 "的指令,有时候就像一个实际的类型.

编辑:这个问题重复.它纯粹是关于void类型的语义.我不想要任何关于如何使用void,指针void或任何其他事情的解释.我希望按照C标准给出答案.



1> Eric Postpis..:

void是一种类型.根据C 2018 6.2.5 19,类型没有值(它可以表示的值集是空的),它是不完整的(它的大小是未知的),并且它不能完成(它的大小不可知).

关于extern void a;,这没有定义对象.它声明了一个标识符.如果a在表达式中使用(除了作为sizeof_Alignof运算符的一部分),则必须在程序中的某处定义它.由于void在严格符合C的情况下不能定义对象,a因此不能在表达式中使用.所以我认为这个声明在严格符合C的情况下是允许的,但没有用.它可能在C实现中用作扩展,允许获取其类型未知的对象的地址.(例如,a在一个模块中定义一个实际对象,然后将其声明为extern void a;另一个模块,并&a在那里使用它来获取其地址.)

(void)作为参数列表的函数声明是一个kludge.理想情况下,()可能用于表示函数不带参数,如C++中的情况.然而,由于C的历史,()用于表示未指定的参数列表,因此必须发明其他东西以表示没有参数.因此(void)被采纳了.因此,(void)是一个例外,那会说,规则(int)是拍摄的功能int,(double)是拍摄的双重功能,等等- (void)是一个特殊的情况下这意味着一个函数没有参数,不,它需要void.

在中foo((void) a),强制转换不会使值"不存在".它会转换a为类型void.结果是类型的表达式void.该表达式"存在",但它没有值,不能在表达式中使用,因此使用它会foo((void) a)导致错误消息.



2> Frankie_C..:

在C语言中,void类型的引入带有"不关心"的含义而不是"空"或"无",它用于不同的范围.

void关键字可以引用void type,一reference to void,一void expression,一个void operand或一个void function.它还明确定义了一个没有参数的函数.

我们来看看其中的一些.


void类型

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

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


指针

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

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

    同样,指向兼容类型的限定或非限定版本的指针应具有相同的表示和对齐要求.

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

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

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


投射到 void

它可以用作强制转换来使表达式无效,但允许完成此类表达式的任何副作用.这一概念在ISO/IEC 9899:2017标准中解释,§6.3转换,§6.3.2.2无效:

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

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

转换的一个实际示例void是用于防止在函数定义中警告未使用的参数:

int fn(int a, int b)
{
    (void)b;    //This will flag the parameter b as used 

    ...    //Your code is here

    return 0;
}

上面的代码段显示了用于静音编译器警告的标准做法.铸到void参数的b作为不产生代码和标记的有效表达b所用的防止编译器抱怨.


void 功能

段落§6.3.2.2无效标准,也包括对void函数的一些解释,这些函数不返回表达式中可用的任何值,但是函数被调用以实现副作用.


void 指针属性

正如我们之前所说的,指针void更有用,因为它们允许以通用方式处理对象引用,因为它们的属性在ISO/IEC 9899:2017,§6.3.2.3中有说明:

    指向void的指针可以转换为指向任何对象类型的指针.

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

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

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

在此片段中,您可以返回可依赖于输入par值的通用指针.


void 作为函数参数

使用的void函数定义参数中后引入的,即所谓的,ANSI标准,以有效地消除歧义从具有功能可变数量的参数的函数没有参数.

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

    类型的未命名参数void作为列表中唯一项的特殊情况指定该函数没有参数.

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

    使用带有空括号的函数声明符(不是prototype-format参数类型声明符)是一个过时的功能.

请考虑以下示例:

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!

现在类似于你的测试,如果我们调用函数bar如下:

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

触发错误,因为转换为void对象不会使其为空.所以你仍然试图将一个void对象作为参数传递给一个没有任何函数的函数.


副作用

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

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

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

在上面的片段中,void表达式失去直接效果,分配b,但由于副作用增加了值a.

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

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

    表达式的评估通常包括值计算和副作用的开始.



3> H.S...:

从C标准#6.2.5p19:

19 void类型包含一组空值; 它是一个不完整的对象类型,无法完成.

这表明该void类型存在.

疑惑1:

void foo(void); // This function takes no argument. Not the 'void' type.

正确.
来自C标准#6.7.6.3p10 [强调我的]:

10 void类型的未命名参数作为列表中唯一项的特殊情况指定该函数没有参数.

这是他们必须添加到语言语法的特殊情况,因为void foo();已经意味着不同的东西(void foo();没有指定任何关于foo参数的东西).如果它不是旧的含义void foo();,那void foo();将是声明无参数函数的语法.你无法概括任何事情.这只是一个特例.

疑惑2:

// Casting to 'void' should make the argument inexistant too...
foo((void)a);

不,它不会因为void它也是一个对象类型,尽管它不完整.

疑问3:

// Assigning to 'int' from incompatible type 'void': so the 'void' type does exists...
a = *p;

是的,它确实存在,因此编译器在此语句上报告错误.

疑惑4:

// Am I not passing the 'void' type ?
foo(*p);

foo()功能声明:

void foo(void);
         ^^^^

void参数列表中显示,因为它已被宣布不带参数的功能将不承担任何说法.
仅供参考,请从C标准#5.1.2.2.1p1 [emphasis mine]中查看:

1程序启动时调用的函数名为main.该实现声明此函数没有原型.它应该使用int的返回类型定义,并且没有参数:

    int main(void) { /* ... */ }
             ^^^^

疑惑5:

extern void a; // Why is this authorised ???

这是授权的,因为它void是一个有效的类型,它只是一个声明.没有存储将分配给a.


推荐阅读
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
  • Java序列化对象传给PHP的方法及原理解析
    本文介绍了Java序列化对象传给PHP的方法及原理,包括Java对象传递的方式、序列化的方式、PHP中的序列化用法介绍、Java是否能反序列化PHP的数据、Java序列化的原理以及解决Java序列化中的问题。同时还解释了序列化的概念和作用,以及代码执行序列化所需要的权限。最后指出,序列化会将对象实例的所有字段都进行序列化,使得数据能够被表示为实例的序列化数据,但只有能够解释该格式的代码才能够确定数据的内容。 ... [详细]
  • C语言注释工具及快捷键,删除C语言注释工具的实现思路
    本文介绍了C语言中注释的两种方式以及注释的作用,提供了删除C语言注释的工具实现思路,并分享了C语言中注释的快捷键操作方法。 ... [详细]
  • baresip android编译、运行教程1语音通话
    本文介绍了如何在安卓平台上编译和运行baresip android,包括下载相关的sdk和ndk,修改ndk路径和输出目录,以及创建一个c++的安卓工程并将目录考到cpp下。详细步骤可参考给出的链接和文档。 ... [详细]
  • 前景:当UI一个查询条件为多项选择,或录入多个条件的时候,比如查询所有名称里面包含以下动态条件,需要模糊查询里面每一项时比如是这样一个数组条件:newstring[]{兴业银行, ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • 本文介绍了深入浅出Linux设备驱动编程的重要性,以及两种加载和删除Linux内核模块的方法。通过一个内核模块的例子,展示了模块的编译和加载过程,并讨论了模块对内核大小的控制。深入理解Linux设备驱动编程对于开发者来说非常重要。 ... [详细]
  • 本文介绍了如何使用Express App提供静态文件,同时提到了一些不需要使用的文件,如package.json和/.ssh/known_hosts,并解释了为什么app.get('*')无法捕获所有请求以及为什么app.use(express.static(__dirname))可能会提供不需要的文件。 ... [详细]
  • 海马s5近光灯能否直接更换为H7?
    本文主要介绍了海马s5车型的近光灯是否可以直接更换为H7灯泡,并提供了完整的教程下载地址。此外,还详细讲解了DSP功能函数中的数据拷贝、数据填充和浮点数转换为定点数的相关内容。 ... [详细]
  • 本文介绍了在MFC下利用C++和MFC的特性动态创建窗口的方法,包括继承现有的MFC类并加以改造、插入工具栏和状态栏对象的声明等。同时还提到了窗口销毁的处理方法。本文详细介绍了实现方法并给出了相关注意事项。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • 本文介绍了如何在给定的有序字符序列中插入新字符,并保持序列的有序性。通过示例代码演示了插入过程,以及插入后的字符序列。 ... [详细]
  • 浏览器中的异常检测算法及其在深度学习中的应用
    本文介绍了在浏览器中进行异常检测的算法,包括统计学方法和机器学习方法,并探讨了异常检测在深度学习中的应用。异常检测在金融领域的信用卡欺诈、企业安全领域的非法入侵、IT运维中的设备维护时间点预测等方面具有广泛的应用。通过使用TensorFlow.js进行异常检测,可以实现对单变量和多变量异常的检测。统计学方法通过估计数据的分布概率来计算数据点的异常概率,而机器学习方法则通过训练数据来建立异常检测模型。 ... [详细]
  • 本文讨论了编写可保护的代码的重要性,包括提高代码的可读性、可调试性和直观性。同时介绍了优化代码的方法,如代码格式化、解释函数和提炼函数等。还提到了一些常见的坏代码味道,如不规范的命名、重复代码、过长的函数和参数列表等。最后,介绍了如何处理数据泥团和进行函数重构,以提高代码质量和可维护性。 ... [详细]
author-avatar
帅宁
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有