设计理念:

C语言的一个设计理念就是声明变量和使用变量的形式应该是一致的

优点:声明变量和使用变量时的运算符优先级是相同的

缺点:运算符的优先级是C语言过度解析的部分之一

术语:

变量声明中使用到的符号的术语:(并不是所有的组合是合法的)

数量 名称 举例
0或更多 指针(pointer) *
一个 说明符(declarator)

identifier

identifier[size]

identifier(args)

(declarator)

0个或一个 初始化器(initializer) = initial_value
至少一个类型限定符

类型说明符

存储类型

类型修饰符

void char short

extern static register

const volatile

0个或一个 更多的说明符 ,declarator
一个 分号 ;

关于struct和union:

  • 类型说明符是:struct {stuff...}
  • 声明形式:struct {stuff...} s;
  • tag:为了简写,可以在struct后面加上结构体tag:struct struct_tag {stuff...},这样就声明了struct_tag代表具体的类型集合{stuff...},之后的声明就可以使用struct struct_tag s;

关于参数传递的两点说明:

  1. 某些书中会说"参数传递到调用函数的时候是从右到左压到栈中",这种说法是不对的。参数传递的时候会尽可能使用寄存器,所以一个整数和一个只含有一个整数的结构体的传递方法是完全不同的,一个整数可能通过寄存器传递,结构体会通过栈传递
  2. 通常一个数组是不能直接通过赋值来传递整个数组的,或者被一个函数返回,但是通过把数组作为结构体的唯一一个成员就可以实现

虽然union具有和结构体类似的结构,但是它们有完全不同的存储方式

  • 结构体将每个成员存储到其前一个成员后面
  • union的所有成员都存储在相同的起始地址,所以不同的成员是相互覆盖的,同时只能有一个成员可以被存储

union的一个明显的问题就是存储了一种类型但是用另一种类型取的类型安全问题,Ada语言主张在记录中存储说明字段来解决这个问题,但是C语言依赖于程序员能够记住存了什么而不采取任何措施

union有两个用途:

  1. 节约空间
  2. 所有的成员有相同的存储空间大小的时候,就可以用不同的方式解析相同的二进制数据而不用显式的进行类型转换

声明语句的解析:

C语言可以有非常复杂的声明语句而让人无法轻易的搞清楚到底定义了什么东西

有两种解析方式:

方式一:优先级法则

  1. 声明的解析从名称开始,然后按照优先级规则继续执行
  2. 优先级从高到低:
    1. 将声明的各个部分组合在一起的括号
    2. 后缀操作符:指明一个函数的"()"和指明数组的"[]"
    3. 前缀操作符:指明是"指向..."的星号
  3. 如果"const"或"volatile"关键字和一个类型说明符相邻,就应用到这个类型说明符;否则,如果应用到左边紧邻的"*"

方式二:状态机规则

  1. 从最左侧的标识符开始,"identifier是" "identifier is"
  2. 如果右侧是"[]"就获取,"一个...的数组" "array of"
  3. 如果右侧是"()"就获取,"参数为...返回值为...的函数" "function returning"
  4. 如果左侧是"("就获取整个括号中的内容,这个括号包含的是已经处理过的声明,回到步骤2
  5. 如果左侧是"const""volatile""*"就获取,持续读取左侧的符号直到不再是这三个之中的,之后返回步骤4
    1. "const":"只读的" "read only"
    2. "volatile":"volatile" "volatile"
    3. "*":"指向..." "pointer to"
  6. 余下的就是基本数据类型

举例:char *(*c[10])(int **p);

  1. 按照优先级规则解析:

    1. c是一个...数组---c[10]
    2. c是一个指向...的指针的数组---*c[10]
    3. c是一个指向参数为...的返回值为...函数的指针的数组---(*c[10])()
    4. c是一个指向参数为整数的指针的指针的返回值为...函数的指针的数组---(*c[10])(int **p)
    5. c是一个指向参数为整数的指针的指针的返回值为指向...的指针函数的指针的数组---*(*c[10])(int **p)
    6. c是一个指向参数为整数的指针的指针的返回值为指向char的指针函数的指针的数组---char *(*c[10])(int **p)
  2. 按照状态机规则解析:
    1. c是...---c---1->2
    2. c是一个...的数组---c[10]---2->3
    3. c是一个指向...的指针的数组---*c[10]---3,4,5->4
    4. c是一个指向...的指针的数组---(*c[10])---4->2
    5. c是一个指向参数为int的指针的指针返回值为...的函数的指针的数组---(*c[10])(int **p)---2,3->4
    6. c是一个指向参数为int的指针的指针返回值为...的函数的指针的数组---(*c[10])(int **p)---4->5
    7. c是一个指向参数为int的指针的指针返回值为指向...的指针的函数的指针的数组---*(*c[10])(int **p)---5->6
    8. c是一个指向参数为int的指针的指针返回值为指向char的指针的函数的指针的数组---*(*c[10])(int **p)---5->6

实现程序:

状态机可以实现为自动翻译程序:

https://github.com/biaoJM/translate-C-declaration-statement

typedef和#define:

1.宏定义的类型名和其他类型说明符一起执行定义,但是typedef只能使用它本身

#define peach int  
unsigned peach i; /* works fine */  
typedef int banana;  
unsigned banana i; /* Bzzzt! illegal */ 

2.typedef的类型会实施到每个说明符,但是宏定义不会

#define int_ptr int *  
int_ptr chalk, cheese;
// 结果为:
int * chalk, cheese; 

导致chalk是int的指针类型,而cheese是int类型

typedef char * char_ptr;  
char_ptr Bentley, Rolls_Royce;

Bentley和Rolls_Royce都是char指针类型

命名空间:

C语言的命名空间

  1. 标签名,所有的标签名的命名空间
  2. tags,对于所有的结构体、枚举类和联合体的tag具有的命名空间
  3. 成员名称,对每个结构体、枚举类或联合体都有自己的成员命名空间
  4. 其他,其他名称的命名空间

所以对声明:

typedef struct baz {int baz;} baz;

这样的定义是合法的:

struct baz variable_1;  /*这里baz是定义的类型名*/
baz variable_2; /*这里baz是tag*/

对于这样的定义:

struct foo {int foo;int foo2;} foo;

第一个foo是这个结构体的tag,第二个foo是一个结构体变量

sizeof(foo)的结果是变量foo的大小,所以如果声明时这样的:

struct foo {int foo;int foo2;} *foo;

sizeof(foo)返回的就是4而不是8,如果想要用tag获取结构体的大小:sizeof(struct foo)——tag只有和struct关键字一起才起作用

而如果这样定义:

typedef struct foo {int foo;int foo2;} foo;

那么就不能再用foo作为变量名,因为此时foo不再是tag而和变量有相同的命名空间

参考:

《expert C programming:deep C secrets》

Chapter 3. Unscrambling Declarations in C

C语言声明语句的更多相关文章

  1. 如何读懂复杂的C语言声明

    本文已迁移至: http://www.danfengcao.info/c/c++/2014/02/25/howto-understand-complicated-declaration-of-c.ht ...

  2. 如何解析复杂的C语言声明

    C语言中有时会出现复杂的声明,比如   char * const * (*next) (); //这是个什么东东?   在讲复杂声明的分析方法前,先来个补充点.   C语言变量的声明始终贯彻两点 :  ...

  3. C语言声明解析方法

    1.C语言声明的单独语法成份     声明器是C语言声明的非常重要成份,他是所有声明的核心内容,简单的说:声明器就是标识符以及与它组合在一起的任何指针.函数括号.数组下表等,为了方便起见这里进行分类表 ...

  4. php入门 数据类型 运算符 语言结构语句 函数 类与面向对象

    php PHP-enabled web pages are treated just like regular HTML pages and you can create and edit them ...

  5. OpenGL ES着色器语言之语句和结构体(官方文档第六章)内建变量(官方文档第七、八章)

    OpenGL ES着色器语言之语句和结构体(官方文档第六章) OpenGL ES着色器语言的程序块基本构成如下: 语句和声明 函数定义 选择(if-else) 迭代(for, while, do-wh ...

  6. 娓娓道来c指针 (4)解析c的声明语句

    (4)解析c的声明语句 在继续探索c指针之前.有必要来解析下c语言中复杂的声明语法. 仅仅须要记住两则:一个原则,一个规则. 原则:先看标示符. 规则:运算符优先级是规则. 举例说明 1.最简单的 i ...

  7. const关键字:终于拥有真正的常量声明语句

    本文首发于个人网站:const关键字:终于拥有真正的常量声明语句 你好,今天大叔想和你唠扯唠扯 ES6 新增的关键字 -- const.在说 const 关键字之前,大叔先和你唠唠大叔自己对 cons ...

  8. javascript语句——表达式语句、块语句、空语句和声明语句

    × 目录 [1]表达式 [2]块语句 [3]空语句[4]声明 前面的话 如果表达式在javascript中是短语,那么语句(statement)就是javascript整句或命令.表达式计算出一个值, ...

  9. [SQL]SQL语言入门级教材_SQL语言基本语句介绍(四)

    SQL语言基本语句介绍 • 表的建立 关系数据库的主要特点之一就是用表的方式组织数据.表是SQL语言存放数据.查找数据以及更新数据的基本数据结构.在SQL语言中,表有严格的定义,它是一种二维表,对于这 ...

随机推荐

  1. Python3.7中的常用关键字

    本文是在学习Python中遇到的一些关键字,作为日常总结的笔记. Python中有保留字/关键字 保留字就是在Python中预先保留的标识符,这些标识符在Python程序中具有特定用途,不能被程序员作 ...

  2. [HDU1052]Tian Ji -- The Horse Racing(田忌赛马)

    题目大意:田忌赛马问题,给出田忌和齐威王的马的数量$n$和每匹马的速度$v$,求田忌最多赢齐威王多少钱(赢一局得200,输一局扣200,平局不得不扣). 思路:贪心. 1.若田忌最慢的马可以战胜齐王最 ...

  3. AsyncTask 简要介绍

    当Android的UI线程超过5s未响应时,系统会引发ANR(Application Not Responding)异常,所以一般不在UI线程中执行耗时任务.一般是在其他线程中处理耗时任务,然后及时更 ...

  4. java去除反复的字符串和移除不想要的字符串

    在java开发中碰到了有些字符串是反复的,假设在进行业务处理要所有遍历太对的数据就会反复,所以在进行业务处理前进行一个去重操作. watermark/2/text/aHR0cDovL2Jsb2cuY3 ...

  5. HDU 2686 Matrix(最大费用最大流+拆点)

    题目链接:pid=2686">http://acm.hdu.edu.cn/showproblem.php?pid=2686 和POJ3422一样 删掉K把汇点与源点的容量改为2(由于有 ...

  6. .Net MVC的学习(一)

    套种间作,也挺有意思的--近来学习感悟.DRP学习的同一时候,折腾了点曾经不曾学习可是却非常多次耳闻过的东西--Asp.Net中的MVC架构模式. 一.是什么? MVC,即(Model-View-Co ...

  7. zzulioj--1716--毒(模拟水题)

     1716: 毒 Time Limit: 2 Sec  Memory Limit: 128 MB Submit: 96  Solved: 43 SubmitStatusWeb Board Desc ...

  8. HD-ACM算法专攻系列(9)——大菲波数

    题目描述: 源码: 运用Java大数求解. import java.math.BigInteger; import java.util.*; public class Main { //主函数 pub ...

  9. 为什么不针对internal接口写单元测试?

    测试驱动的开发(TDD,Test Driven Development)的核心理念,是要使得重构(refactoring)更为有效,而不是创建更多的测试. 对一个有着长生命周期的项目来讲,在它的第一个 ...

  10. css3 字体、2D转换、3D转换

    学习篇之CSS3 字体.2D转换.3D转换 一.字体 @font-face 将字体文件存放到 web 服务器上,通过CSS3 @font-face规则中定义,它会在需要时被自动下载到用户的计算机上. ...