规范

每个步骤结尾的 规范 一节都会对这个步骤中的新特性给出规范,方便大家查阅。

step12 语法规范

灰色部分表示相对上一节的修改。


program
    : (function | declaration)*

function
    : type Identifier '(' parameter_list ')' (compound_statement | ';')

type
    : 'int'
    | type '*'

parameter_list
    : (type Identifier (',' type Identifier)*)?

compound_statement
    : '{' block_item* '}'

block_item
    : statement
    | declaration

statement
    : 'return' expression ';'
    | expression? ';'
    | 'if' '(' expression ')' statement ('else' statement)?
    | compound_statement
    | 'for' '(' expression? ';' expression? ';' expression? ')' statement
    | 'for' '(' declaration expression? ';' expression? ')' statement
    | 'while' '(' expression ')' statement
    | 'do' statement 'while' '(' expression ')' ';'
    | 'break' ';'
    | 'continue' ';'

declaration
: type Identifier ('[' Integer ']')* ('=' expression)? ';'
expression_list : (expression (',' expression)*)? expression : assignment assignment : conditional | unary '=' expression conditional : logical_or | logical_or '?' expression ':' conditional logical_or : logical_and | logical_or '||' logical_and logical_and : equality | logical_and '&&' equality equality : relational | equality ('=='|'!=') relational relational : additive | relational ('<'|'>'|'<='|'>=') additive additive : multiplicative | additive ('+'|'-') multiplicative multiplicative : unary | multiplicative ('*'|'/'|'%') unary unary : postfix | ('-'|'~'|'!'|'&'|'*') unary | '(' type ')' unary postfix : primary | Identifier '(' expression_list ')'
| postfix '[' expression ']'
primary : Integer | '(' expression ')' | Identifier

step12 语义规范

12.1. 支持多维数组,但每一维长度只能是正整数常数,不能是零或负数。

所以也没有变长数组 int a[n]; 也没有不定长数组 int a[];

12.2. 对数组取地址是错误,也不会有指向数组的指针。

这是为了简化实验,否则需要引入 C 中一堆繁复的记号,像 int *a[10]int (*a)[10],对于实验意义不大。

12.3. 数组声明不能有初始值。局部变量数组初始值未定,全局变量初始值为零。

C 中可以写 int a[2]={1, 2} 但 MiniDecaf 不行。

12.4. 数组首地址对齐要求同元素的对齐要求(4 字节)。 数组的各个元素在内存中是连续的,并且多维的情况下排在前面的维度优先。

例如 int a[3][4][5] 占用了 60 个 int(240 字节)的连续内存,和 int b[60] 一样。 a[1][2][3] 的偏移量是 (1*20+2*5+3) * 4 字节,和 b[33] 一样。

12.5. 下标运算优先级高于一元运算符。

因此 -a[0]-(a[0])

12.6. 下标运算 a[b] 的操作数类型必须是:a 为指针或数组,b 为 int。

12.7. 下标运算越界是未定义行为。

12.8. 可以将数组的前几维单独提出,类型还是数组类型,可转换以后赋给一个指针。

例如 int a[2][2][2],那么 int *p = (int*) a[0]; int *q = (int*) a[1][1]; 是合法的。 但 int x=a[1]int *r=a[0][0][0] 是不合法的。

12.9. 左值表达式除了能通过 11.1 中三条规则得到,新增一条规则:

  • 通过下标运算,如果结果类型不是数组类型,那么结果表达式是左值。

    例如 int a[2][2],那么 a[1] 不是左值,但 a[1][1] 是左值,a[1][1]=2&a[1][1] 都是合法操作。

12.10. 数组类型的表达式仅能参与如下运算:类型转换、下标。

12.11. 函数形参不能被声明为数组,传参只能传指针。

C 允许 int foo(int a[]);int bar(int b[5]); 但我们不允许。

12.12. (更新 11.5)指针可参加加减运算,允许 int 加指针和指针加 int,以及指针减 int(不允许 int 减指针)。 运算结果的类型和指针操作数的类型相同。 实际运算时,int 操作数需要乘上指针基类型的大小。

例如 int **p(考虑到 sizeof(int*) == 4),那么 p-2 等价于 p+-2,其汇编类似 addi result, p, -8

12.13.(更新 11.5)允许两个指针相减,但两个指针类型必须相同。 ab 是同类型的指针,那么 a-b 的结果 c 是一个 int,且满足 a == b+c

12.14. 空指针参与任何指针算术都是未定义行为。

12.15. 指针运算越界,按照 11.8 是未定义行为。但有特例:允许越界一个元素。

例如如下代码是合法的

int a[10]; int *p=(int*) a;
for (int *q=p; q != p+10; q=1+q) *q=0;

哪怕其中 p+10 已经越界了。

results matching ""

    No results matching ""

    results matching ""

      No results matching ""