第三章------声明
一.声明的构成
1.声明器就是由标识符以及与它组合在一起的任何指针、函数括号、数组下标等组成。
2.结构:一种把一些数据项组合在一起的数据结构
(1)
struct tag{
short dd;
int aa;
}my_tag;
//tag--->结构类型名
//my_tag--->结构变量
struct tag my1_tag;
my_tag my2_tag;
//以上两种都等价
(2) 允许存在位段。无名字段以及字对齐所需的填充字段。(类型为int,unsigned int,signed int 或加上限定符)
struct pid{
unsigned int inactive : 1; //一个位的填充
unsigned int : 0; //填充到下一个边界
}
3.联合:可称变体记录,外表与结构相似,所有成员都是从偏移位置零开始存储,每个成员重叠在一起,在某一时刻只有一个成员真正存储于该地址。
tips:
(1)联合一般用于节省空间,有些数据项不可能同时出现,但同时存储则浪费内存;
(2)可以把两个互相排斥的字段存储于一个联合中来节省空间。
(3)联合可以把同一个数据解释成两种不同的东西.
union bits32{
int whole; //一个32位的值
struct {char c0,c1,c2,c3;}byte; //4个8位的字节
}
4.枚举:把一串名字与一整串整型值联系在一起
enum sizes{small = 7 ,mdedium,big=10,large};
tips:
(1)一般情况从0开始,赋值的标识符,后一个值比前一个大1
(2)与#define相比,编译时枚举名字一直在调试器中可见,而宏定义则编译就丢弃
二.优先级规则
步骤 | 解释 |
---|---|
A | 首先,声明从标识符名字读取,并注意到它直接被括号括住 |
B | 优先级从高到低是 |
B1 | 声明中被括号的那部分 |
B2 | 后缀操作符:括号()表示这个是一个函数 , 挎号[ ]表示这个是一个数组 |
B3 | 前缀操作符:* 表示指向…的数组 |
C | 如果const 或volatile 关键字的后面紧跟类型说明符(如 int等),那么将它用于类型说明符。其它情况下,const或volatile 关键字作用于它左边紧邻的指针星号 |
eg:
(1)
char *const*(*next)();
//A,先看变量next,翻译成next是...
//B1,看它是被扩号括住,观察里面有*,翻译成next是一个指向...的指针
//B2,后面是一个括号,说明next是一个函数指针,指向一个返回...的函数
//B3,前缀是一个*,表示得出指针所指的内容
//C,char * const是一个整体,解释为:指向字符的常量指针
//next是一个指针,它指向一个函数,函数返回另一个指针,
//这个指针指向类型为char的常量指针
(2)
char *(*str[10])(int **p);
//A,找到str变量名,str是...
//B1,被括号括住,后缀有[],所以str是一个元素数量为10的数组
//B3,前缀是一个*,所以str是一个数组,元素类型为指针,数量为10的数组
//B1,(*str[10])(),后面是一个括号,说明是str是一个数组,元素类型是函数指针
//且接受一个指向整型指针的指针
//B3,char *()() 解释为该函数返回一个指向字符型的指针
//str是一个数组,元素类型是一个函数指针,接受一个指向整型指针的指针,
//并且返回一个指向字符型的指针
三.typedef
1.typedef:为一种类型引入新的名字,而不是为变量分配空间!
tips:不要在一个typedef放入多个声明类型,并且也不要把它放在声明的中间
2.typedef 与#define 的区别
(1)可以用其他类型说明符对宏类型名进行拓展,而typedef所定义的类型名就不能
#define peach int
typedef int apple
unsigned peach i; //正确
unsigned apple b; //error!!
(2)在连续几个变量的声明中,typedef定义的类型可以保证声明中所有的变量均为同一种类型,而用#define则无法保证
#define int_ptr int*
int_ptr chalk , cheese;
//上行经宏拓展后变成
int * chalk , cheese;
//前者为int * ,后者为int
**********************************
**********************************
typedef int * int_ptr
int_ptr chalk , chesse;
//两者都是 int *
3.typedef的使用提示
(1)不要为了方便起见对结构使用typedef
(2)它应该用在数组、结构、指针以及函数的组合类型
(3)可移植类型
(4)也可以为后面的强制类型转换提供一个简单的名字
四.程序:将C语言的声明翻译成通俗语言
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#define MAXTOKENS 100
#define MAXTOKENLEN 64
//把C语言声明翻译成通俗语言
//数据结构是堆栈,从左向右读取,把各个标记依次压入堆栈,直到遇到标识符。然后继续向右读入一个标记,
//也就是标识符右边的标记,接着,再把标识符左边的标记从堆栈中弹出
enum type_tag {IDENTIFIER , QUALIFIER,TYPE};
//identifier 取标记
//type 类型
//qualifier 限定符
struct token{
char type;
char string[MAXTOKENLEN];
};
m
int top = -1;
struct token stack[MAXTOKENS];
struct token this;
#define pop stack[top--]
#define push(s) stack[++top] = s
//推断 标识符的类型
enum type_tag classify_string(void)
{
char *s = this.string;
if(!strcmp(s,"const")){
strcpy(s,"read-only");
return QUALIFIER;
}
if(!strcmp(s,"volatile")) return QUALIFIER;
if(!strcmp(s,"void")) return TYPE;
if(!strcmp(s,"char")) return TYPE;
if(!strcmp(s,"signed")) return TYPE;
if(!strcmp(s,"unsigned")) return TYPE;
if(!strcmp(s,"short")) return TYPE;
if(!strcmp(s,"int")) return TYPE;
if(!strcmp(s,"long")) return TYPE;
if(!strcmp(s,"float")) return TYPE;
if(!strcmp(s,"double")) return TYPE;
if(!strcmp(s,"struct")) return TYPE;
if(!strcmp(s,"union")) return TYPE;
if(!strcmp(s,"enum")) return TYPE;
return IDENTIFIER;
}
//读取下一个标记到“this”,取标记
void gettoken(void)
{
char *p = this.string;
//省略空白字符
while((*p = getchar()) == ' ');
if(isalnum(*p)){
//读入的标识符以A-Z,0-9开头
while(isalnum(*++p = getchar()));
ungetc(*p, stdin); // int ungetc(int char, FILE *stream) 把字符 char(一个无符号字符)
//推入到指定的流 stream 中,以便它是下一个被读取到的字符。
*p = '\0';
this.type = classify_string();
return;
}
if(*p == '*'){
strcpy(this.string , "pointer to");
this.type = *p;
return;
}
this.string[1] = '\0';
this.type = *p;
return;
}
//理解所有分析过程的代码段
//先调用gettoken,把标记压入堆栈,直到遇见第一个标识符
//printf“该标识符是....”,this.string
//再继续调用gettoken
read_to_first_identifer()
{
gettoken();
while(this.type != IDENTIFIER){
push(this);
gettoken();
}
printf("%s is ",this.string); //标识符是什么...
gettoken();
}
deal_with_arrays(){
while(this.type == '['){
printf("array ");
gettoken(); //数字或者]
if(isdigit(this.string[0])){
printf("0..%d ",atoi(this.string)-1);
gettoken();//读取]
}
gettoken(); //读取完]后再做一个标记
printf("of ");
}
}
deal_with_function_args()
{
while(this.type != ')'){
gettoken();
}
gettoken();
printf("function returning ");
}
deal_with_pointers()
{
while(stack[top].type == '*'){
printf("%s ",pop.string);
}
}
deal_with_declarator()
{
//处理标识符之后可能存在的数组或函数
switch(this.type){
case '[' : deal_with_arrays();
break;
case '(' : deal_with_function_args();
}
deal_with_pointers();
//处理在读入到标识符之前压入堆栈中的符号
while(top >= 0){
if(stack[top].type == '('){
pop;
gettoken();//读取')'之后的符号
deal_with_declarator();
}else{
printf("%s ",pop.string);
}
}
}
main()
{
//将标记压入堆栈,直至遇见标识符
read_to_first_identifer();
deal_with_declarator();
printf("\n");
return 0;
}