user-0 提问: 求教一个关于C语言数据溢出的检验程序问题和setjmp函数，有代码

回复:
user: user-1
content: 引用 13 楼 znzp124900171 的回复:[quote=引用 12 楼 ALNG 的回复:] 内积的问题我似乎直接已经帮你改了，参我的修改代码。直接把每个相乘的一个运算数升级为double, 这样都成了double的乘和加，考虑到输入范围，溢出已经不可能了。  我的题目中要求要考虑到一些自定义范围，因为对于一些参数，比如滤波器参数等等，其处理的数据能力是有限的，所以当C语言在转换数据的过程当中，不可避免的会出现一些编译器无法发现的数据丢失，扩大数据的范围肯定能避免溢出，但是有时候数据的范围比较小，比如int，大部分数模转换器的数据类型都是int，那么在处理它们的过程中，溢出就会严重影响精度和结果，是这个意思。[/quote]  嗯，我知道你的意思了。如果更大的数据类型不存在或不方便，那只有依赖具体的检测。举个例子，假如你已经使用了C内建支持的最大的整数类型并不想使用自定义的大整数运算，而要计算整型的内积(不升级为double), 可以处理如下 typedef struct _Point{     long long x,y,z; }Point;  long long InnerProduct(const Point* p1, const Point* p2) {     long long _x, _y, _z, rslt;      _x=p1->x*p2->x;      if(p1->x && _x/p1->x!=p2->x)         longjmp(jump_buffer, 3);      _y=p1->y*p2->y;     if(p1->y && _y/p1->y!=p2->y)         longjmp(jump_buffer, 3);      _z=p1->z*pz->z;     if(p1->z && _z/p1->z!=p2->z)         longjmp(jump_buffer, 3);      rslt=_x+_y;     if( ((_x<0)^(_y<0)==0) && //both negative or both non-negative            (_x<0 && rslt>_x || // two negative number should add to a smaller rslt without overflow        _x>0 && rslt<_x) // 2 positive number should add to a bigger rslt without overflow     )         longjmp(jump_buffer, 3);           _x=rslt;     rslt+=_z;        if( ((_x<0)^(_z<0)==0) && //both negative or both non-negative            (_x<0 && rslt>_x || // two negative number should add to a smaller rslt without overflow        _x>0 && rslt<_x) // 2 positive number should add to a bigger rslt without overflow     )         longjmp(jump_buffer, 3);      // all overflow cases have been taken care of     return rslt; }

user: user-0
content: 引用 12 楼 ALNG 的回复:内积的问题我似乎直接已经帮你改了，参我的修改代码。直接把每个相乘的一个运算数升级为double, 这样都成了double的乘和加，考虑到输入范围，溢出已经不可能了。  我的题目中要求要考虑到一些自定义范围，因为对于一些参数，比如滤波器参数等等，其处理的数据能力是有限的，所以当C语言在转换数据的过程当中，不可避免的会出现一些编译器无法发现的数据丢失，扩大数据的范围肯定能避免溢出，但是有时候数据的范围比较小，比如int，大部分数模转换器的数据类型都是int，那么在处理它们的过程中，溢出就会严重影响精度和结果，是这个意思。

user: user-1
content: 内积的问题我似乎直接已经帮你改了，参我的修改代码。直接把每个相乘的一个运算数升级为double, 这样都成了double的乘和加，考虑到输入范围，溢出已经不可能了。

user: user-1
content: 对了，一个小建议。 sizeof(Point)=6， 传值调用在32位平台上可能稍微昂贵一些，可以考虑吧Add, Substra, Multiply的原型设计成类似  void Add(const Point* p1, const Point* p2, Point* rslt);  当然，不改问题也不大。

user: user-0
content: 引用 9 楼 ALNG 的回复:太谢谢你了，例子十分清楚，容我追加一个问题，就是乘法的溢出，代码中我没有处理乘法部分是因为我还没弄明白如何处理他们？你有好的建议吗？尤其是对于带符号的向量，内积中可能会有一部分的乘法结果溢出，但是有负值的话，最后求得的内积的和还是有可能在范围内，导致加减法溢出的检验方法不适用。  =============== 一样啊。用一个更大的类型，比如int, 如果计算的结果超出了short的范围，则溢出。代码几乎一模一样，两overflow函数都是重用的。 void Multiply(Point p1, Point p2, Point* rslt) {    int x=(int)p1.x*p2.x;    int y=(int)p1.y*p2.y;    int z=(int)p1.z*p2.z;     if(overflow(x) || overflow(y) || overflow(z))        longjmp(jump_buffer, 3); // suppose we decide to differentiate MULTIPLY overflow    rslt->x=(short)x;    rslt->y=(short)y;    rslt->z=(short)z; }  随手敲的，不保证能够运行。意思在那里了。  十分感谢，问题解决，结贴

user: user-1
content: 太谢谢你了，例子十分清楚，容我追加一个问题，就是乘法的溢出，代码中我没有处理乘法部分是因为我还没弄明白如何处理他们？你有好的建议吗？尤其是对于带符号的向量，内积中可能会有一部分的乘法结果溢出，但是有负值的话，最后求得的内积的和还是有可能在范围内，导致加减法溢出的检验方法不适用。  =============== 一样啊。用一个更大的类型，比如int, 如果计算的结果超出了short的范围，则溢出。代码几乎一模一样，两overflow函数都是重用的。 void Multiply(Point p1, Point p2, Point* rslt) {    int x=(int)p1.x*p2.x;    int y=(int)p1.y*p2.y;    int z=(int)p1.z*p2.z;     if(overflow(x) || overflow(y) || overflow(z))        longjmp(jump_buffer, 3); // suppose we decide to differentiate MULTIPLY overflow    rslt->x=(short)x;    rslt->y=(short)y;    rslt->z=(short)z; }  随手敲的，不保证能够运行。意思在那里了。

user: user-1
content: switch(setjmp(jump_buffer))     {     case 0: { 天，case 0: 后面的那个 { 多余，要去掉才能正常编译。

user: user-0
content: 引用 6 楼 ALNG 的回复:一个是VC++ 12 32位的编译器，一个是g++ 4.8.2 64位的编译器.  对了上面那个while(getchar()!='\n');可以去掉，虽然无关紧要。  longjump应该放在Add, Substract和其他实现某个可能导致溢出或其他错误的函数代码中，这样的实现才有使用价值。本质上实现了类似C++ try{}catch(Exception1&){}catch(Exception2&){}的功能。这里不同的exception用一个非0的整型值来表示，再在我的示例中switch的不同分支来处理。为了展示longjmp的第二个参数的意义，我特意区分了加法溢出和减法溢出，实际应用中这个区分并无意义。  太谢谢你了，例子十分清楚，容我追加一个问题，就是乘法的溢出，代码中我没有处理乘法部分是因为我还没弄明白如何处理他们？你有好的建议吗？尤其是对于带符号的向量，内积中可能会有一部分的乘法结果溢出，但是有负值的话，最后求得的内积的和还是有可能在范围内，导致加减法溢出的检验方法不适用。

user: user-1
content: 一个是VC++ 12 32位的编译器，一个是g++ 4.8.2 64位的编译器.  对了上面那个while(getchar()!='\n');可以去掉，虽然无关紧要。  longjump应该放在Add, Substract和其他实现某个可能导致溢出或其他错误的函数代码中，这样的实现才有使用价值。本质上实现了类似C++ try{}catch(Exception1&){}catch(Exception2&){}的功能。这里不同的exception用一个非0的整型值来表示，再在我的示例中switch的不同分支来处理。为了展示longjmp的第二个参数的意义，我特意区分了加法溢出和减法溢出，实际应用中这个区分并无意义。

user: user-1
content: 我把你的代码稍微改了一下，希望有助于你理解setjmp和longjmp： // //  main.c //  Bachelorarbeit // //  Created by Nan on 1/1/14. //  Copyright (c) 2014 Nan. All rights reserved. //   #include <stdio.h> #include <setjmp.h>   jmp_buf jump_buffer;   typedef struct Vektor{     short int x,y,z; }Point,*pPoint;   void Input(pPoint p) {     printf("please enter the value of x :");     scanf("%hd",&(p->x));     printf("please enter the value of y :");     scanf("%hd",&(p->y));     printf("please enter the value of z :");     scanf("%hd",&(p->z)); }     //Skalarprodukt double Inner(Point p1,Point p2) {     return (double)p1.x*p2.x+(double)p1.y*p2.y+(double)p1.z*p2.z; }   static int overflow(int x) { 	return x>32767 || x<-32768; }   //Addition void Add(Point p1,Point p2,Point *p3) { 	int x=(int)p1.x+p2.x; 	int y=(int)p1.y+p2.y; 	int z=(int)p1.z+p2.z; 	if(overflow(x) || overflow(y) || overflow(z)) 		longjmp(jump_buffer, 1);     p3->x=(short)x;     p3->y=(short)y;     p3->z=(short)z;      }   //Subtraktion void Subtra(Point p1,Point p2,Point *p4) { 	int x=(int)p1.x-p2.x; 	int y=(int)p1.y-p2.y; 	int z=(int)p1.z-p2.z; 	if(overflow(x) || overflow(y) || overflow(z)) 		longjmp(jump_buffer, 2);      p4->x=(short)x;     p4->y=(short)y;     p4->z=(short)z; }   void show_point(Point p) {     printf("(%.3d,%.3d,%.3d)", p.x, p.y, p.z); }   int main() {       Point p1, p2, p3,p4;           switch(setjmp(jump_buffer))     {     case 0: {         printf("\n*******input p1*******\n"), Input(&p1);         printf("\n*******input p2*******\n"), Input(&p2);         Add(p1, p2, &p3);         Subtra(p1, p2, &p4);         puts("\n=======result=======");         printf("\ninner: %.3lf", Inner(p1, p2));         printf("\nadd:"), show_point(p3);         printf("\nmin:"), show_point(p4);       	break;     case 1:     	printf("Addition overflow!\n");     	break;     case 2:     	printf("Substraction overflow!\n");     	break;     } //    while(getchar()!='\n');      puts("\npress enter to exit.\n");     getchar();     return 0; }

user: user-0
content: 引用 2 楼 ALNG 的回复:g++ 4.8 64bit下测试也通过了。  你用的编译器？  不好意思，忘了引用你的回复

user: user-0
content: 我试了两个，win下是DEV C++，然后mac下用的xcode，最后跳转后都是不停的重复printf的内容，之前我在else语句中加了longjmp(jump_buf,0),意思是想如果出现溢出，就在报错后再跳回去，重新输入point的值，之前测试了一下成功了，但是不知道怎么着就不行了，我把代码贴出来   // //  main.c //  Bachelorarbeit // //  Created by Nan on 1/1/14. //  Copyright (c) 2014 Nan. All rights reserved. //  #include <stdio.h> #include <setjmp.h>  jmp_buf jump_buffer;  typedef struct Vektor{     short int x,y,z; }Point,*pPoint;  void Input(pPoint p) {     printf("please enter the value of x :");     scanf("%hd",&(p->x));     printf("please enter the value of y :");     scanf("%hd",&(p->y));     printf("please enter the value of z :");     scanf("%hd",&(p->z)); }   //Skalarprodukt double Inner(Point p1,Point p2) {     return p1.x*p2.x+p1.y*p2.y+p1.z*p2.z; }  //Addition void Add(Point p1,Point p2,Point *p3) {     p3->x=p1.x+p2.x;     p3->y=p1.y+p2.y;     p3->z=p1.z+p2.z; }  //Subtraktion void Subtra(Point p1,Point p2,Point *p4) {     p4->x=p1.x-p2.x;     p4->y=p1.y-p2.y;     p4->z=p1.z-p2.z; }  void show_point(Point p) {     printf("(%.3d,%.3d,%.3d)", p.x, p.y, p.z); }  int main() {      Point p1, p2, p3, p4;          if (setjmp(jump_buffer)==0) {         printf("\n*******input p1*******\n"), Input(&p1);         printf("\n*******input p2*******\n"), Input(&p2);         Add(p1, p2, &p3);         Subtra(p1, p2, &p4);         if(p3.x!=(p1.x+p2.x)){             longjmp(jump_buffer, 1);         }         puts("\n=======result=======");         printf("\ninner: %.3lf", Inner(p1, p2));         printf("\nadd:"), show_point(p3);         printf("\nmin:"), show_point(p4);      }     else{         printf("\n*******the result is overflow,pleas check your input and enter the value again*******\n");          longjmp(jump_buff,0);     }          while(getchar()!='\n');     puts("\npress enter to exit.\n");     getchar();     return 0; }

user: user-1
content: g++ 4.8 64bit下测试也通过了。  你用的编译器？

user: user-1
content: 我用VS 2013 express的编译器运行无误, 参截图: