题目:实现如下功能,接收一个yyyymmdd形式的整数(例如20070412),确定相应的月、日和年的数值,并将年、月、日进行输出,要求实现异常处理。

分析:首先是如何将整数分离,这个是好办的,用一个数组接收,用指针操作就好。那么怎么实现异常处理呢?显然主要是控制输入的位数(用for实现)、控制月份和日期不越界。为了简单起见,默认每个月最大天数为31天。可以分别对月份和日期进行讨论,用if....else if....else...实现。但是最近在关注C语言的异常处理,想想何不用jmpset/longjmp来实现?于是动手写,最开始的代码如下:

 

 
  
  1. #include <STDIO.H> 
  2. #include <STDLIB.H> 
  3. #include "date.h" 
  4.  
  5. jmp_buf buf; 
  6.  
  7. void _error_month() 
  8.     longjmp(buf,1); 
  9.  
  10. void _error_day() 
  11.     longjmp(buf,2); 
  12.  
  13. void main() 
  14. {        
  15.     int _da[8]; 
  16.     const int* p = _da
  17.     _date mdate;     
  18.     for(int i = 0;i < 8;i ++) 
  19.     { 
  20.         printf("Please enter the "); 
  21.         printf("%d",i+1); 
  22.         printf(" number!\n"); 
  23.         scanf("%d",&_da[i]); 
  24.     } 
  25.     mdate.year = *p * 1000 + *(p+1) * 100 + *(p+2) * 10 + *(p+3); 
  26.     mdate.month = *(p+4) * 10 + *(p+5); 
  27.     mdate.day = *(p+6) * 10 + *(p+7); 
  28.     int v = setjmp(buf); 
  29.      
  30.     if (mdate.month <= 0 || mdate.month >= 13) 
  31.     { 
  32.         _error_month(); 
  33.     }    
  34.     else if(mdate.day <= 0 || mdate.day >= 31) 
  35.     { 
  36.         _error_day(); 
  37.     } 
  38.     if(v == 1) 
  39.     { 
  40.         printf("You enter a wrong month,Please shut down !!\n"); 
  41.         return; 
  42.     } 
  43.     else if(v == 2) 
  44.     { 
  45.         printf("You enter a wrong day,Please shut down !!\n"); 
  46.         return; 
  47.     }    
  48.     else 
  49.     { 
  50.         printf("The year is:"); 
  51.         printf("%d\n",mdate.year); 
  52.         printf("The month is:"); 
  53.         printf("%d\n",mdate.month); 
  54.         printf("The day is:"); 
  55.         printf("%d\n",mdate.day); 
  56.         return; 
  57.     } 

一运行,发现竟然进入了死循环。百思不得其解。于是翻开KR的《C程序设计语言(第2 . 新版)》第232页给出的关于标准库<setjmp.h>的说明。如下一段:

 
  
非局部跳转<setjmp.h>  头文件<setjmp.h>中的说明提供了一种避免通常的函数调用和返回顺序的途径,特别的,它允许立即从一个多层嵌套的函数调用中返回。  8.1 setjmp  #include <setjmp.h> int setjmp(jmp_buf env);  setjmp()宏把当前状态信息保存到env中,供以后longjmp()恢复状态信息时使用。如果是直接调用setjmp(),那么返回值为0;如果是由于调用longjmp()而调用setjmp(),那么返回值非0。setjmp()只能在某些特定情况下调用,如在if语句、 switch语句及循环语句的条件测试部分以及一些简单的关系表达式中。  8.2 longjmp  #include <setjmp.h> void longjmp(jmp_buf env, int val);  longjmp()用于恢复由最近一次调用setjmp()时保存到env的状态信息。当它执行完时,程序就象setjmp()刚刚执行完并返回非0值val那样继续执行。包含setjmp()宏调用的函数一定不能已经终止。所有可访问的对象的值都与调用longjmp()时相同,唯一的例外是,那些调用setjmp()宏的函数中的非volatile自动变量如果在调用setjmp()后有了改变,那么就变成未定义的。
其中尤其有一段话引起了我的注意: 
 setjmp()只能在特定情况下调用,比如if语句、switch语句以及循环语句的条件测试部分以及一些简单关系表达式中。
setjmp只调用一次,返回值为0;longjmp可以调用多次,返回值可以任意设定。每当调用一次longjmp,函数就跳到setjmp的地方,重新开始执行。我知道死循环的由来了!在对error_month或者error_day的调用中,函数跳回setjmp,结果往下执行时,又一次遇到了error_month,又跳回,又执行,又跳回.....如此循环往复,子子孙孙无穷尽也。呜呼哀哉! 于是解决问题的关键在于,改变setjmp的值后,表明已经出现异常,无需再进行判断了!将代码改成如下后,测试成功:
 
  
  1. #include <STDIO.H> 
  2. #include <STDLIB.H> 
  3. #include "date.h" 
  4.  
  5. jmp_buf buf; 
  6.  
  7. void _error_month() 
  8.     longjmp(buf,1); 
  9.  
  10. void _error_day() 
  11.     longjmp(buf,2); 
  12.  
  13. void main() 
  14.          
  15.     int _da[8]; 
  16.     const int* p = _da
  17.     _date mdate; 
  18.      
  19.     for(int i = 0;i < 8;i ++) 
  20.     { 
  21.         printf("Please enter the "); 
  22.         printf("%d",i+1); 
  23.         printf(" number!\n"); 
  24.         scanf("%d",&_da[i]); 
  25.     } 
  26.  
  27.     mdate.year = *p * 1000 + *(p+1) * 100 + *(p+2) * 10 + *(p+3); 
  28.     mdate.month = *(p+4) * 10 + *(p+5); 
  29.     mdate.day = *(p+6) * 10 + *(p+7); 
  30.  
  31.     int v = setjmp(buf); 
  32.  
  33.     if(v == 1) 
  34.     { 
  35.         printf("You enter a wrong month,Please shut down !!\n"); 
  36.         return; 
  37.     } 
  38.  
  39.     else if(v == 2) 
  40.     { 
  41.         printf("You enter a wrong day,Please shut down !!\n"); 
  42.         return; 
  43.     } 
  44.      
  45.     else 
  46.     { 
  47.  
  48.         if (mdate.month <= 0 || mdate.month >= 13) 
  49.         { 
  50.             _error_month(); 
  51.         }    
  52.         else if(mdate.day <= 0 || mdate.day >= 31) 
  53.         { 
  54.             _error_day(); 
  55.         } 
  56.         printf("The year is:"); 
  57.         printf("%d\n",mdate.year); 
  58.         printf("The month is:"); 
  59.         printf("%d\n",mdate.month); 
  60.         printf("The day is:"); 
  61.         printf("%d\n",mdate.day); 
  62.         return; 
  63.     } 
  64.      

特地记下这个例子,以后再用到setjmp时,可以回忆一下这个例子,思考一下问题可能出在哪里。