Way on c & c++ 小记 [二]

简单的数据类型?感觉不那么简单。

       C语言的数据类型我认为可以划分为3类,分别是空类型void、基本类型(字符型、整型、浮点型和枚举类型)和派生类型(数组、函数、指针、结构体和共用体)。

       标准C上规定int类型范围为-32768 ~32767,然而实际上往往并非如此,这取决于编译器的设计,比如下述代码:

#include <stdio.h>
 
int main(int argc, char*argv[]){
       printf("%d/n",sizeof(int));
       printf("%d/n",sizeof(float));
       printf("%d/n",sizeof(double));
       printf("%d/n",sizeof(long long));
       return 0;
}

       vc2008中的输出为:4 4 8 8

       这说明了在vc2008编译环境下,int类型占据了4个字节的内存空间。而看到int类型,很容易让人联想到自加运算(++),关于自加运算不妨看看a+++ba+++++ba+++(++b)有何区别。下面是测试的结果:

#include <stdio.h>
 
int main(int argc, char*argv[]){
       inta,b,c;
       a = b = c = 0;
       //c = a+++b;  //输出1,0,0
       //c = a+++++b;      //无法通过编译,提示++需要左值
       //c =a+++(++b);//输出1,1,1
       printf("%d,%d,%d/n",a,b,c);
       return 0;
}

       插入旁白:关于左值、右值,网上有人说是翻译错误,应理解为可寻址的和可读的,我觉得也颇有几分道理,但这里就引用编译器报错原文。

       第一种结果很容易理解,先执行c=a+b,之后a自加1;第三种结果也好理解,在赋值运算前++b先得到执行,所以c1,赋值之后a++得到执行。关键在第二种,为什么会编译出错?“++需要左值”是指哪个“++”?为什么是那个“++”?在此,不妨反问一句,如果让自己设计编译器,那么遇到a+++++b这样的语句应该怎样处理?先带着这个疑问继续探寻。

       编译器会对a+++++b这样的表达式进行出错处理,但却接受a+++b这样的表达式,从一定程度上说明了编译器“聪明”到可以处理a++,但却还不够“聪明”去处理++b。当然,这里指的++b是特定地、位于a+++++b这样表达式当中的限定场合的。为什么呢?这就属于词法分析的问题了。仍然回到上面那个问题,如果让自己设计编译器,遇到a+++b要怎么办呢?

       为了找出是哪个“++”需要左值,我们不妨将代码稍加改动为:

#include <stdio.h>
 
int main(int argc, char*argv[]){
       inta,b,c;
       a = b = c = 0;
       c = ((a++)++)+b;
       printf("%d,%d,%d/n",a,b,c);
       return 0;
}

       编译器仍然提示同样的错误,于是我们可以推出是第三个加号和第四个加号组成的“++”运算符需要左值,即a+++++b等效于((a++)++)+b

    想来现在的最新问题是,(a++)后面为什么不能接自加运算“++”?

    回到l-valuer-value上来,不管采用左值、右值一说,或者采用可寻址值和可读值一说,其本质都是对内存的读写操作(我无法确定这里是否有寄存器操作),也就是说源操作数和目的操作数的地址要能确定。这问题个人感觉不是很好回答,因为依旧涉及到了编译器的设计问题,在此仅谈谈个人的理解。

    ++a是先对a执行加1操作,然后返回对a的引用,自然就可以确定此时a的地址,所以(++a)++是可以编译通过的(于visual c++ 2008编译环境,经博友Promi测试,VC6和GCC并不能编译通过)。而a++是新建了一个临时变量并赋予其当前a的值,再对a进行增1运算,最后返回临时变量值,此时就无法再编译的时候获得该临时变量的地址,于是造成了((a++)++)+b无法编译通过。

由此可知,在++aa++不影响逻辑结构的情况下,我们应该倾向于使用++a。需要强调的是,我使用的是vc2008编译环境。

    接着回到编译器设计考虑的问题。在C语言编译器的词法分析中有“贪心法”一说,因为存在“++”运算,所以遇到“+”自然要再往前看是否还有“+”。或许应该换一种说法,在我的知识范围内,词法分析都有“贪心法”一说,即识别单词过程应该包含尽可能多的字符。这很好理解,比如C语言中要求变量名必须以下划线或者字母开头,之后可以下划线、字母和数字混合,那么当我们遇到_abc123def时,我们难道分析道_abc就停止了吗?自然不是的,我们要继续往前以识别出一个完整的单词。这是词法分析的知识范畴,那么编译器是如何识别出标识符的呢?关于这个问题,杭电ACM网站上有这么一道简单题,可以去测试一下自己的思路对不,http://acm.hdu.edu.cn/showproblem.php?pid=2024。再深入想想,如果是判断合法的数值类型应该怎么操作呢?这里的数值类型包括整形、浮点型和指数型。

       对于上述杭电2024题目,我第一次的思路是分为两种情况:第一个字母只能有下划线和字母,其它字母只能有下划线、字母或数字,于是产生了以下代码:

#include <stdio.h>
#include <ctype.h>
 
int main(int argc, char*argv[]){//hdu2024_1
       intn,flag;
       charid[100];
       scanf("%d",&n);
       getchar();//由于scanf会将'/n'放在缓冲区中,所以先处理掉它
       while(n--){
              flag = 0;
              gets(id);
              if(!(id[0]== '_' || isalpha(id[0])))    flag = 1;
              for(int i=1; id[i] != '/0';++i){
                     if(!(isalpha(id[i])|| isdigit(id[i]) || id[i] == '_')){
                            flag = 1;
                            break;
                     }
              }
              printf("%s/n",(flag == 1) ? "no" : "yes");
       }
       return 0;
}


       这种思路比较简单,适用于识别标识符,但如果是要识别数值呢(含整形、浮点和指数型)?这里给出另外一种识别标识符的思路,可以用来识别数值类型,即编译器设计时使用的状态转换图(或其改进后的自动机):

标识符状态转换图

       上述识别标识符转换图的含义为:开始状态为0,如果遇到字母或者下划线则转向状态1;在状态1下,如果遇到字母、数组或者下划线则仍转向状态1,遇到其它则表示识别出一个标识符了。由其可产生如下代码:

#include <stdio.h>
#include <ctype.h>
 
int main(int argc, char*argv[]){//hdu2024_2
       int n,flag, state;
       charid[100];
       scanf("%d",&n);
       getchar();//由于scanf会将'/n'放在缓冲区中,所以先处理掉它
       while(n--){
              flag = state = 0;
              gets(id);
              for(int i=0; id[i] != '/0';++i){
                     switch(state){
                            case 0:
                                   if(isalpha(id[i]) || id[i] == '_')   state = 1;
                                   else{      flag= 1;  break;    }
                                   break;
                            case 1:
                                   if(isalpha(id[i]) || isdigit(id[i]) || id[i] == '_')       state= 1;
                                   else{      flag= 1;  break;    }
                                   break;
                            case 2:   //这里的case2对于hdu2024这道题目不是必须的,但在词法分析中却是需要的
                                   flag = 1;  break;
                     }
              }
              printf("%s/n",(flag == 1) ? "no" : "yes");
       }
       return 0;
}


       数据类型果真不那么简单,上面那么多的“+”已经有点眼花缭乱了,但对于数据类型的知识范围而言不过是刚刚开了个头。现在给自己一道饭后甜点,char类型的单个字符为什么不能表示一个汉字字符呢?

2010-1-26

----------------------------------------cuttingline----------------------------------------

 

评论 30
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值