第七章 C语言声明详解

 

人们常说,C语言的声明太复杂了,的确,这也是C语言饱受批评的地方之一。不过,笔者认为,真正要受到批评的不是语言本身,而是那些传播者。传播者们通常都有一个共识:讲述要由浅入深。作为原则,笔者并非要反对它,毕竟笔者对C语言的学习,也经历了相同的过程。但是,由浅入深并不意味着一切从简,以偏盖全。计算机语言不同于数学理论(虽然它的确根植于数学,与数学密不可分),数学理论是一种循序渐进的过程,后面的理论以前面的理论为基础。但C语言归根说底,就是一堆语言规则而已,应该让学习者一开始就全面且详细地了解它,而不是象现在某些教材所做的那样,只说一部分,不说另一部分,以为这就是由浅入深了,实际上这是以偏盖全。

        语言如此,声明作为C语言的一部分更是如此。我们最常见到的对声明的描述是这样的:

存储类别  类型限定词  类型  标识符

这种说明会给人们一种暗示:C语言的声明是静止的、死板的,什么声明都能够以这个为基础,往上一套就OK了。事实真的如此吗?说句心里话,笔者也祈祷事实真的如此,这样世界就简单多了、清静多了。但别忘了,这个世界总是让人事与愿违的。实际上,C的声明的组织形式是以嵌套为基础的,是用嵌套声明组织起来的,并非象上面所述那么死板,存储类说明符一定得放在限定词前面吗?类型说明符一定要紧贴标识符吗?不!C标准从来没有这样说过!下面来看一看C89对声明的形式是如何规定的:

声明:

声明说明符   初始化声明符表opt  [opt的意思是option,可选]

其中声明说明符由以下三项构成:

声明说明符:

存储类说明符  声明说明符opt
类型说明符    声明说明符opt
类型限定符    声明说明符opt

在这里,一个声明说明符可以包含另一个声明说明符,这就是声明的嵌套,这种嵌套贯穿于整个声明之中,今天我们看来一个非常简单的声明,其实就是由多个声明嵌套组成的,例如:

static const int i=10, j=20, k=30;

变量i前面就是声明说明符部分,有三个声明说明符:static const int,static是一个存储类说明符,它属于这种形式:

static 声明说明符

static后面的声明说明符就是const int,const是一个类型限定符,这也是个嵌套,它是由

const 声明说明符

组成,最后的int是一个类型说明符,到这里已经没有嵌套了,int就是最底的一层。对于存储类说明符、类型说明符和类型限定符的排列顺序,C标准并没有规定其顺序,谁嵌套谁都可以。换言之,上面的声明可以写成:

int static const i=10, j=20, k=30;或者const int static i=10, j=20, k=30;

这无所谓,跟原声明是一样的。再举一个有趣的例子:

const int *p;与int const *p;

有些人会对后面一种形式感到困惑,因为他一直以来学习的都是那种死板的形式,因此他无法理解为什么那个const可以放在int的后面。实际上对于标准来说,这是再正常不过的行为了。

上面举的例子是变量的声明,函数的声明也同样道理,例如:

static const int func(void);
......

int main(void)
{
        int static const (*p)(void);
        p=func;
        .........
        return 0;
}

const int static func(void)
{
        .......
        return 0;
}

func的函数原型声明、函数定义跟main内的函数指针p的声明是一样的。但是,笔者并非鼓励大家把声明说明符写得乱七八糟,作为一个良好的风格,应该按照已经习惯约定的方式排列说明符,但懂得其中的原理非常重要。

声明static const int i=10, j=20, k=30;的int后面的部分就是初始化声明符表,这比较容易理解,这个符表实际上也是嵌套的:

初始化声明符表:

初始化声明符
初始化声明符表, 初始化声明符


初始化声明符:

声明符
声明符=初值


        声明符是初始化声明符的主体,现在来讨论一下声明符是如何规定的:

声明符:

指针opt  直接声明符

这里写的指针opt指的是那个指针声明符*,要注意的是,*属于声明符,而不是声明说明符的一部分。

指针opt又包含:

指针:

* 类型限定符表opt
* 类型限定符表opt 指针

在这里有一个常见的问题,就是const int *p;与int * const p的区别,第一个声明的const属于声明说明符,它跟int一起,是用来说明*p这个声明符的,因此const修饰的是p所指向的那个对象,这个对象是const的。而第二个声明的const是声明符的一部分,它修饰的对象是p本身,因此p是const的。

        上面规定的第二条值得注意,这条规定产生了一种指针与const的复杂形式,例如:

const int * const *** const ** const p;(是不是有种想冲向厕所的冲动?)这是一种复杂的声明嵌套,如何解读这种声明?其实只要掌握了它的规律,无论它有多少个const、多少个*都不难解读的,这个内容我将在第九章进行解释。

        剩下的就是直接声明符和类型限定词表的内容:

直接声明符:

标识符
(声明符)
直接声明符[常量表达式opt]
直接声明符(形式参数类型表)
直接声明符(标识符表opt)


类型限定符表:

类型限定符
类型限定符表 类型限定符


        这一章的最后一个内容,是讨论一下typedef,typedef用来声明一个别名,typedef后面的语法,是一个声明。本来笔者以为这里不会产生什么误解的,但结果却出乎意料,产生误解的人不在少数。罪魁祸首又是那些害人的教材。在这些教材中介绍typedef的时候通常会写出如下形式:

typedef int PARA;

这种形式跟#define int PARA几乎一样,如前面几章所述,这些教材的宗旨是由浅入深,但实际做出来的行为却是以偏盖全。的确,这种形式在所有形式中是最简单的,但却没有对typedef进一步解释,使得不少人用#define的思维来看待typedef,把int与PARA分开来看,int是一部分,PARA是另一部分,但实际上根本就不是这么一回事。int与PARA是一个整体!就象int i:声明一样是一个整体声明,只不过int i定义了一个变量,而typedef定义了一个别名。这些人由于持有这种错误的观念,就会无法理解如下一些声明:

typedef int a[10];
typedef void (*p)(void);

他们会以为a[10]是int的别名,(*p)(void)是void的别名,但这样的别名看起来又似乎不是合法的名字,于是陷入困惑之中。实际上,上面的语句把a声明为具有10个int元素的数组的类型别名,p是一种函数指针的类型别名。

虽然在功能上,typedef可以看作一个跟int PARA分离的动作,但语法上typedef属于存储类声明说明符,因此严格来说,typedef int PARA整个是一个完整的声明。

Android功耗系列专题理论之六:外设功耗问题分析方法 本文介绍了移动设备功耗问题的分析方法,重点针对传感器和GPU两大模块。传感器部分详细讲解了通过ADB命令获取日志、状态差异分析和单传感器隔离测试的排查方法,并给出优化建议。GPU部分从基础功耗测量、场景测试到功能模块拆解进行多维度分析。同时阐述了GNSS/GPS功耗的测试方法,包括前后台对比、星座双频拆解及SAP/DPO等特殊功能的电流分析,为移动设备功耗优化提供系统性的诊断思路。 阅读详情

相关推荐

基于PyTorch的BP神经网络PID控制器动态调参实战

本文详细介绍了基于PyTorch的BP神经网络PID控制器动态调参实战,结合BP神经网络的自适应能力和PID控制算法的经典优势,实现工业控制中的高效参数调整。通过PyTorch实现BP-PID融合架构,包括网络设计、损失函数优化及动态调参技巧,显著提升控制性能,适用于温度控制、电机控制等场景。

weixin_29094775的博客 522

C/C++语言中的声明

一般的声明形式如下: 声明说明符+声明符列表 声明说明符:描述声明的变量或函数的性质。         声明说明符分为三大类:         1.存储类型:auto、static、extern、register。每次声明只能出现一个存储类型,并且放在声明语句的最前面。         2.类型限定符:const、volatile、(:C99restrict)。每次声明可以包含零个或多个...

linux驱动开发大纲 1632

ArcMap投影变换避坑指南:为什么你的坐标转换总出错?常见问题解析与解决方案

本文深入解析ArcMap中坐标转换与投影变换的核心区别与常见陷阱。针对坐标转换总出错的问题,从混淆“定义投影”与“投影变换”、栅格数据重采样选择、矢量数据地理变换参数设置等关键环节,提供清晰的避坑指南与实战解决方案,帮助用户确保空间数据的精确匹配。

weixin_29210727的博客 357

《C专家编程》第三章——分析C语言的声明

  前面一章我们已经说过C语言存在的一些问题和它晦涩的地方,让我们对这门神奇的语言有了更深的了解。现在这一章则集中精力来讨论C语言的声明,分为三块,首先是说明C语言声明晦涩难懂的原因和声明是如何形成的,其次就是学习怎样对C语言的声明进行分析,另外本文将详细来探讨一个分析C语言声明的工具——cdecl,分析和编写它的源代码。   C语言的声明晦涩难懂这一点应该是名不虚传的,比如说下面这个声明: ...

weixin_30387663的博客 263

FAST-LIO2实战:如何在无人机上部署并实现厘米级定位(附ROS配置全流程)

本文详细介绍了如何在无人机上部署FAST-LIO2激光雷达惯性里程计系统,以实现厘米级精确定位。内容涵盖从硬件选型(如激光雷达与机载计算机)、软件环境配置(ROS Noetic)、传感器标定,到FAST-LIO2参数调优、与PX4飞控集成以及实际飞行测试的全流程。重点解决了实时定位中的外参标定、数据同步等关键问题,为无人机在无GPS或复杂光照环境下提供稳定可靠的定位解决方案。

weixin_29062865的博客 251

国产FPGA逆袭!深度评测复旦微RFVU3P5G核心板在相控阵雷达中的表现

本文深度评测了复旦微RFVU3P5G核心板在相控阵雷达中的实战表现。该核心板凭借射频直采架构、高采样率ADC/DAC及丰富的DSP资源,在多通道同步精度、动态范围及系统稳定性等关键指标上表现出色,展现了国产FPGA在高端雷达应用中的硬核实力与供应链自主价值。

b0c1d2的博客 726

c语言中的指针声明符,C语言中的指针

在复习C语言的过程中,遇到了二维数组的指针表示问题。因此凭借这次机会查阅了相关的资料并进行了部分的代码实验。本篇即是一次个人总结。0x00 指针与地址我们知道,在计算机内存中所有的数据均是线性排布。每个单元都有其唯一的地址与之对应,我们可以理解为一条街所有的房子都有唯一的门牌号。而指针就是门牌号,我们可以通过指针找到内存中任意单元内的值,就好比我们可以通过门牌号找到任意一栋房子。而在这里,我们需要...

weixin_29070721的博客 927

保姆级教程:用GEE(Google Earth Engine)复现CASA模型计算NPP,附完整数据集清单与Python代码

本文提供了一份详细的保姆级教程,指导如何使用GEE(Google Earth Engine)复现CASA模型计算NPP(净初级生产力)。教程涵盖环境准备、关键数据集获取与处理、Python代码实现全流程,以及结果验证与常见问题解决方案,帮助生态学研究者高效完成NPP计算。

weixin_28707365的博客 334

【C语言】标识符和关键字

1. 标识符 2. 关键字

Qiuhan_909的博客 1万+

YOLOv8改进 - 注意力篇 - 引入CAA注意力机制

YOLOv8改进 - 注意力篇 - 引入CAA注意力机制

浪子L的博客 5223

Windows 10下Carla 0.9.15安装避坑指南:从下载到运行的全流程解析

本文详细解析了在Windows 10系统上安装和运行Carla 0.9.15自动驾驶仿真平台的全流程。内容涵盖从系统环境准备、资源下载、Python依赖安装,到首次启动验证及常见错误(如DLL缺失、显卡驱动问题)的深度解决方案,旨在帮助用户快速搭建稳定的开发环境,并运行第一个自动驾驶Python脚本。

alice7model的博客 546

再再论指针

 再再论指针----篇首语        指针是C语言规范里面一项核心内容,指针具有与生俱来的优势,利用指针可以写出许多短小精悍、效率极高的代码,它是C语言一把无可替代的利器,凭着这把利器,C语言与其它高级语言相比至少在效率方面高人一筹。但是,由于指针的原理与使用方式跟人们通常的思维习惯有较大的差别,造成了指针比C语言其它概念难理解得多,这使得对指针认识不足成为了一种在C程序员中普遍存在的

云,风扬起的地方 1897

机器学习实战1-四种算法对比对客户信用卡还款情况进行预测与模型评估

大家好,我是微学AI,今天给大家带来一个机器学习实战案例:利用机器学习的四种算法对比对客户信用卡还款情况进行分类。信用卡又叫贷记卡,是由商业银行或信用卡公司对信用合格的消费者发行的信用证明。现在的年轻人,特别是80后,90后甚至00后到喜欢超前消费,每个人名下多多少少都有至少一张信用卡,有些人由于过度超前消费,导致下个月无法还款导致的逾期,这样会对个人征信产生影响,今天我们就来分析分析具有哪些特性的人会有信用卡逾期的可能。

微学AI的博客 4710

第七章 C语言声明详解

        人们常说,C语言的声明太复杂了,的确,这也是C语言饱受批评的地方之一。不过,笔者认为,真正要受到批评的不是语言本身,而是那些传播者。传播者们通常都有一个共识:讲述要由浅入深。作为原则,笔者并非要反对它,毕竟笔者对C语言的学习,也经历了相同的过程。但是,由浅入深并不意味着一切从简,以偏盖全。计算机语言不同于数学理论(虽然它的确根植于数学,与数学密不可分),数学理论是一种循序渐进的过程

8052

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

第七章 C语言声明详解- -

        人们常说,C语言的声明太复杂了,的确,这也是C语言饱受批评的地方之一。不过,笔者认为,真正要受到批评的不是语言本身,而是那些传播者。传播者们通常都有一个共识:讲述要由浅入深。作为原则,笔者并非要反对它,毕竟笔者对C语言的学习,也经历了相同的过程。但是,由浅入深并不意味着一切从简,以偏盖全。计算机语言不同于数学理论(虽然它的确根植于数学,与数学密不可分),数学理论是一种循序渐进的过程...

weixin_34356555的博客 92

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

【第七章】 C语言之牛客网&力扣刷题笔记 【点进来保证让知识充实你一整天】

同学们,新的学期早已开始啦😚~相信大家都想在这学期偷偷地卷”死“同学吧😇大学里里可以做的事情有很多哦,我们一定要好好地利用好这段时间,努力地提升自己,大胆放手一搏吧!✊星光不负赶路人,时光不有心人只要我们以深深地谦卑去忍耐“暗无天日”的日子,定会迎来属于我们豁然开朗地一刻!让我们一起为心中所想,去奋斗吧!!以下内容干货满满,跟上步伐吧~综上,特辑的第七章呐🍭~让我们大家一同在平凡的生活中奔赴热爱吧~毕竟,“热爱的东西永远不要说放弃”💫如果有错误❌,欢迎指正呀💫httpshttps。...

Dream_Y.ocean的博客 2686

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

C语言第七章函数声明

函数声明 #include <stdio.h> #include <stdlib.h> /* run this program using the console pauser or add your own getch, system("pause") or input loop */ //函数声明代码放在最前面,这样保证所有函数可以顺利调用其他函数 //函数只要被声明...

zzugsh的博客 1687

第七章 C语言函数_C语言函数声明以及函数原型

C语言代码由上到下依次执行,原则上函数定义要出现在函数调用之前,否则就会报错。但在实际开发中,经常会在函数定义之前使用它们,这个时候就需要提前声明。 所谓声明(Declaration),就是告诉编译器我要使用这个函数,你现在没有找到它的定义不要紧,请不要报错,稍后我会把定义补上。 函数声明的格式非常简单,相当于去掉函数定义中的函数体,并在最后加上分号;,如下所示: dataType functionName( dataType1 param1, dataType2 param2 ... ); 也..

qq_43205256的博客 220

C语言程序设计核心详解 第七章 函数和预编译命令

第七章详细介绍了函数和预编译命令的概念及应用。首先,通过函数定义、调用及例题分析,阐述了函数的基本结构和调用过程,特别是原型声明的重要性。接着,深入探讨了变量的三属性,包括类型、作用范围和存储类别,详细说明了auto、static、register和extern四种存储类别的特点。最后,介绍了预编译命令,特别是宏的定义和使用,帮助读者理解代码预处理阶段的工作机制。

weixin_62613321的博客 953

【C总集篇】第七章 函数

​ C允许函数调用它自己,这种调用过程称为递归(recursion)。递归有时难以捉摸,有时却很方便实用。结束递归是使用递归的难点,因为如果递归代码中没有终止递归的条件测试部分,一个调用自己的函数会无限递归。​ 可以使用循环的地方通常都可以使用递归。有时用循环解决问题比较好,但有时用递归更好。递归方案更简洁,但效率却没有循环高。​ 指针?什么是指针?从根本上看,指针是一个值为内存地址的变量(或数据对象)。​ 正如char类型变量的值是字符,int类型变量的值是整数,指针变量的值是地址。

铁匠匠匠的博客 2973

C语言第七章 函数的进阶应用

首先我们先来实现一个函数,这个函数的功能是:判断一个数是否是素数。判断一个数是否是素数的基本思想已经在循环结构的文章里讲过,这里只是把它实现的功能封装成一个函数,所以判断素数的基本思想不再过多赘述,直接给出代码int main()i <= 100;i++)//调用函数,判断i是否是素数return 0;i < num;i++)printf("%d不是素数\n", num);return;printf("%d是素数\n", num);如果运行上面的代码,会发现,编译器报错了这是什么原因呢。

Nightmare1000的博客 1060

从零开始学C语言系列之第七章《指针简介2》

​   另外,程序必须知道储存在指定地址上的数据类型。long 和 float 可能占用相同的存储空间,但是它们储存数字却大相径庭。​   例如,可以把两个整数相乘,但是不能把两个指针相乘。所以,指针实际上是一个新类型,不是整数类型。,在大部分系统内部,该地址由一个无符号整数表示。但是,不要把指针认为是整数类型。​   pc指向的值(*pc)是char 类型。pc本身是什么类型?声明的意思是 pi是一个指针,​   我们描述它的类型是“

铁匠匠匠的博客 1894

从零开始学C语言系列之第七章《函数中返回值》

​ ​  条件表达式的值是n和m中的较小者,该值要被返回给主调函数。虽然这里不要求用圆括号把返回值括起来,但是如果想让程序条理更清楚或统一风格,可以把返回值放在圆括号内。​  关键字 return 后面的表达式的值就是函数的返回值。在该例中,该函数返回的值就是变量 min 的值。​ 变量 min 属于 imin()函数私有,但是 return 语句把 min 的值传回了主调函数。那是否能像写成下面这样。

铁匠匠匠的博客 1884

【C语言笔记】第七章 结构体

第七章 结构体一、目的二、特点三、声明、定义、初始化和使用1.声明2.定义3.初始化 一、目的 程序中的变量大多数是互相独立、无内在联系的变量,内存中的地址也互不相关。但在实际问题中,有些数据是有内在联系并成组出现的。需要把这些数据组成一个组合数据,存放在一块内存单元中。 二、特点 结构体是由不同类型数据组成的组合型数据结构,在内存中这些数据按各自所占内存大小连续存放,每个结构体变量所占的内存为各...

z1446731325的博客 431
上一篇: 第六章 “另类”数组
下一篇: 第八章 右左法则----复杂指针解析
cubelee
博客等级 码龄15年 4粉丝 23原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值