为何不精通C? 03 深入剖析声明

    对于复杂的C函数声明,或者被typedef别名后的声明,很多人往往一头雾水。本文主要解析下C语言中声明过程所遵循的原则。

声明

    引用《C专家编程》的第三章内容,说明下声明的优先级规则:

  1. 声明从它的名字开始读,然后按照优先级顺序依次读取。
  2. 优先级从高到底依次为:
    •  括号包围的地方
    • 后缀操作符:
      • 括号()表示是一个函数
      • 方括号[]表示是一个数组
    • 前缀操作符:星号*表示类型是 指向....的指针
  3. 若const/volatile关键字的后面紧跟类型说明符(int,float),那么其作用于类型,在其他情况下,作用于其左边紧邻的指针星号。

不过,我觉得这个规则的不够通俗,看了《C++Annotation》中关于const的那一章节,也详细解释了下这个规则,高效,庖丁解牛般分析:

// 例子
char* const *(*next)();
  1. 从变量名开始
  2. 往右看,直到声明结束或碰到 ')'
  3. 回到上一次开始的地方,往左看,直到直到碰到 ‘(’ 或到声明开始的地方。
  4. 若碰到(, 从2开始,整个被()包含的地方为一个成分,根据语境解释其含义
  5. 到开头结束

通俗的乒乓解释,就是从变量名开始,碰到)或结束往左读,碰到(往右读。

遇到*()[]解释之

其中 解释是函数的话,需解释其形参表返回值

       解释为数组的话,需解释数组元素是什么

       解释为指针时,需解释其指向什么类型

举例:

1 next                          名叫next,                     开始往右读,碰到了),折向往左
2 (*next)                       遇到* ,解释是一个指针           继续往左读碰到(, 折向往右
3 (*next)()                     遇到(),解释指向一个函数,该函数没有形参        到末尾,折向往左
4 *(*next)() 遇到*,解释函数返回一个指针,关于函数的形参表和返回值解释完毕 , 继续往左 5 char* const *(*next)() 先碰到const,再是*, 最后是char, 解释函数的返回值(指针)指向一个char型指针常量,即指针不可赋新值,所指的值可以改变。到开头,结束。

以上的例子是函数的声明举例。

对其他的声明也是一样的,比如之前文章中  int *ap[], 我们也是这样解释的:

  • 从 ap 开始往右读,碰到了[], 说明是一个数组
  • 然后到末尾,折向向左,碰到*,说明数组的元素是指针,
  • 最后碰到int,说明该元素的指针类型为 int*

常规的 const int * p; 也可以这么来:

  • 从 p 开始,结尾折向,往左走。
  • 先碰到*, 说明是一个指针
  • 再碰到 int,const, const修饰的是int, 则说明该指针指向一个const int东西

同理,对于 int* const p: 我们在往左看时,先看到了const,再*, 说明const修饰的是*。

来个复杂的挑战下吧:

char* const* (* const  (*(*ip)() ) [] ) []
(*ip) ip是一个指针
(*ip)() ip是一个函数指针,该函数无形参
*(*ip)() 函数返回一个指针
(...)[]  该指针指向一个数组
*const (...)[] 数组元素为 常量指针
(*const (...)[]) []  该常量指针指向一个数组
char* const* {...}该数组内元素为指向char型常量指针的指针
合起来就是:ip是一个函数指针(无形参), 返回一个指向数组的指针,该数组内元素为常量指针,其指向一个元素为char型常量指针的数组。 

不过,一般不会有这种声明来恶心我们的。。我们基本上知道怎么打乒乓就行了。


typedef

剖析完了声明,一般来说还要说下 typedef ,这可是C的一大神器呀。

首先,我们要明确的是,typedef是为类型创建别名,而不是创造新的类型。

讲typedef时,又必须和 #define做下区分, define仅是简单的宏扩展

主要来说,有以下的区别:

  • 首先,可以对其他类型说明符采用宏类型名进行扩展,但对typedef所定义则不行
#define INT int
typedef int tInt
unsigned INT ci;  // 正确,类型为unsigned int
unsigned tInt tci;  // 非法,错误
  • 其次,连续变量声明中,typedef能够保证所有定义类型一致,而#define不能保证
#define pD int*
typedef int* pT
pD a,b;   // a类型为 int*, b类型为int
pT c,d;   // c,d类型均为int*

和typedef做好朋友

void (*signal(int sig, void(*func)(int))) (int);

我们来分析下这个是什么东西:

    signal(..) : signal 是一个函数,有复杂的形参表

    *signal(...) : 返回值是一个指针

    void {*}(int) : 该指针指向一个函数,该函数的形参为int, 这个函数的返回值是void

我们看看signal的形参表: 两个参数,一个参数是 int; 另外个参数 void(*func)(int) 是一个函数指针,该函数有一个int形参,返回void;

对比下,我们可以分析出, signal的返回值和 func的定义一样, 都是 void(*)(int), 但若是采用这种写法的话,不好看懂,这时候,我们的好朋友typedef就出现了:

typedef void(*ptr_to_func)(int);
ptr_to_func signal(int, ptr_to_func){.....}

另外,typedef也经常和struct配合使用。

typedef struct my_struct{....} NewName;

这样,NewName 就等同于 struct my_struct , 少打了struct这几个字符


扩展说下 声明和定义的区别:

C语言中,对象有且仅有一个定义,而声明却可以有多个extern 声明。

定义:只能出现在一个地方,确定同时分配内存,它是特殊的声明

声明:只是描述其他地方创建对象的属性。有extern前缀,作用于变量

具体的一些扩展区别,见后续博文,关于指针和数组的阐释

END

要点: 怎么打乒乓?typedef是个别名好朋友

转载于:https://www.cnblogs.com/IntellX/archive/2013/05/17/3083701.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/461062.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Photoshop绘制植物大战僵尸中的食人花

本人意见:我打算使用本文中的食人花形象作为我的一次讲课中的主要参考。我想说明的是,通过FLASH和PS都能够轻松地绘制出如本文所描述的食人花卡通效果。独立游戏开发者如果安排好时间,完全可以通过1-2个月掌握FLASH和PS绘制(想精通…

ios手机 php无法上传文件,【已解决】uni.uploadFile 苹果ios图片上传不成功

安卓正常上传,苹果里我到相册里选了一张20k的图片,服务器都得不到数据,服务器端是PHP,$_FILES居然是空的,下面是简单的代码openPic(){var that this;uni.chooseImage({count: 1,sourceType: [album, camera],sizeType…

uboot源码——uboot启动内核过程总结

总结的思维导图,其下载地址:Uboot启动.mmap_免费高速下载|百度网盘-分享无限制 第一阶段:汇编阶段,即start.S文件的工作。 第二阶段:C代码阶段,即start_armboot函数的工作。 值得一提的是,star…

虚拟化运行[OpenStack] VMWare产品介绍

最近使用开发的过程中出现了一个小问题,顺便记录一下原因和方法--虚拟化运行 世界上最早研制虚拟化软件的厂商之一。目前是是寰球桌面到数据中心虚拟化解决方案的引导厂商。中文名“威睿”,纽约证券交易所代码:VMW。总部设在加利福尼亚州的帕…

oracle模拟重叠事务,ORACLE的事务读一致性与语句读一致性

SET TRANSACTION READ ONLY来实现事物级别的一致性。一个事物所有语句读到的数据都是一致的。我们开始试验一,模拟语句级别读一致性。第一个session使用显示打开一个游标模拟数据读,同时在游标读数据的过程中,启动另外一个session更改数据&am…

内核源码——kernel启动过程的思维导图

参考博客 内核源码——汇编阶段的head.S文件_天糊土的博客-CSDN博客___head汇编 内核源码——C语言阶段的start_kernel函数_天糊土的博客-CSDN博客_start_kernel 思维导图 kernel启动过程的思维导图下载地址:内核启动过程.mmap

能力=知识+技能+经验

知识少则眼光不够深远;技能不精则难以服众经验太少则容易应变不灵。那么如果想让老板提升你,最“靠谱”的方法就是用这几方面检验自己,寻找机会进行自我提升,全面而优质的能力自然会让HR对你另眼相看。 然而这种理想的状态并非短期…

oracle 01013 02063,Oracle11g dblink用户密码大写限制-ORA-02063: preceding line from FOR244_DBLINK...

[oracleKY22 ~]$ sqlplus admin/admin4567SQL*Plus: Release 10.2.0.1.0 - Production on D???t 6?? 12 13:52:50 2012Copyright (c) 1982, 2005, Oracle. All rights reserved.Connected to:Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - 64bit Product…

oracle的等保,3.Oracle 检查(部分)

这是3级等保中oracle的检查方法,剩下的检查项可通过询问的方式进行检查。1.身份鉴别a.1 查看数据库用户select username,account_status from dba_users;b.1 检查用户的profileselect username,account_status,profile from dba_users;b.2 检查密码策略select profi…

Activiti配置实例以及Spring集成配置

public class TestDB {public static void main(String[] args) {//1。 创建Activiti配置对象的实例ProcessEngineConfiguration configuration ProcessEngineConfiguration.createStandaloneProcessEngineConfiguration();//2. 设置数据库连接信息// 设置数据库地址configura…

Linux字符设备驱动剖析

以下内容整理于Linux字符设备驱动剖析,如有侵权请告知删除 。 一、应用层的程序 应用程序一般都是open打开设备文件,read、write、ioctl设备文件,最后close设备文件退出。 int main(int argc ,char *argv[]) { unsigned char val[1] 1; …

php处理form多文件上传,ajax利用FormData、FileReader实现多文件上传php获取

前台代码(注意,不需要用到form标签):a. html部分:b. js部分:c. 完整代码:function loadDoc(file,data,asynctrue){if(window.XMLHttpRequest){ // code for IE7, Firefox, Chrome, Opera, Safarixmlhttpnew XMLHttpReq…

getattr

getattr(object, name[, default]) -> value Get a named attribute from an object; getattr(x, y) is equivalent to x.y. When a default argument is given, it is returned when the attribute doesnt exist; without it, an exception is raised in that case转载于:h…

rpmbuild FILE

2、一些rpm相关信息rpm软件包系统的标准分组:/usr/share/doc/rpm-4.3.3/GROUPS各种宏定义: /usr/lib/rpm/macros已经安装的rpm包数据库: /var/lib/rpm如果要避免生成debuginfo包:这个是默认会生成的rpm包。则可以使用下面的命令&a…

Linux设备文件的创建和mdev

以下内容源于微信公众号嵌入式企鹅圈,有格式内容上的修改,如有侵权请告知删除。 本文将从代码级去理解Linux设备类和设备文件的创建过程。 一、设备类相关知识 设备类是虚拟的,并没有直接对应的物理实物,只是为了更好地管理同一类…

linux查看执行过的命令行,在Linux命令终端中查看和编辑曾执行过的命令 – LINUX笔记 – CFEI.NET...

今天我们来讲讲linux的知识,积累的这些知识就是我们以后的财富,各位加油.因为水平有限,难免有疏忽或者不准确的地方,希望大家能够直接指出来,我会及时改正。一切为了知识的分享。history 命令可以用来显示曾执行过的命令,也可以根…

烂泥:【解决】word复制windows live writer没有图片

本文由秀依林枫提供友情赞助,首发于烂泥行天下。 在使用windows live writer发表博客,博客先是在是word2013中进行编辑,编辑完毕后我会复制到windows live writer中,然后发表出去。 使用了几年都没有问题,就是最近这个…

平台设备与平台驱动的注册

以下内容源于网络资源的学习与整理,如有侵权请告知删除。 参考资料 linux中 probe函数何时调用 platform总线的probe函数调用_Linux编程_Linux公社-Linux系统门户网站 Linux设备驱动模型之platform(平台)总线详解 Linux设备驱动模型2——总线式设备驱动组织方式_天…

JDK源码 - BitSet的实现

java.util.BitSet是个很有趣的类&#xff0c;了解其内部实现对正确的使用非常重要。 对象构造&#xff1a; Java代码 private final static int ADDRESS_BITS_PER_WORD 6; private final static int BITS_PER_WORD 1 << ADDRESS_BITS_PER_WORD; private long[] wor…

细粒度权限控制 linux,利用docker插件实现细粒度权限控制

前言我们在实际的docker运行环境下&#xff0c;大都会遇到多用户的情况&#xff0c;为了安全起见&#xff0c;有些用户我们不想给予其全面的docker控制权限&#xff0c;比如不想某些用户执行docker stop 以及docker rm 等危险指令&#xff0c;当然我们可以从系统账号权限来控制…