C语言关系运算符异常:Simpletron减法越界判断错误排查
这是《Programming in C》一书中的Simpletron模拟器实现代码。在execute函数的SUBTRACT算术操作模块中,通过表达式*accumulator - memory[*operand] < -9999 || *accumulator - memory[*operand] > 9999判断减法计算结果是否超出[-9999, 9999]的内存范围。测试发现,当结果为正数时程序运行正常,但结果为负数时,本该返回false的判断却返回true,导致程序异常终止。
问题代码
#include <stdio.h> #include <stdlib.h> // Input/Output Operations: #define READ 10 #define WRITE 11 // Load/Store Operations: #define LOAD 20 #define STORE 21 // Arithmetic Operations: #define ADD 30 #define SUBTRACT 31 #define DIVIDE 32 #define MULTIPLY 33 //Transfer-of-control Operations: #define BRANCH 40 #define BRANCHNEG 41 #define BRANCHZERO 42 #define HALT 43 void main_message(); void load(int *memory,int *instructionCounter); void execute(int *instructionCounter, int memory[], int *instructionRegister, int *operationCode, int *operand, unsigned int *accumulator); void dump(int *memory); int main() { main_message(); //Special Registers int memory[101] = {+0000}; unsigned int accumulator = 0000; int instructionCounter = 00; unsigned int instructionRegister = 0000; int operationCode = 00; int operand = 00; load(memory,&instructionCounter); execute(&instructionCounter, memory, &instructionRegister, &operationCode, &operand, &accumulator); computer_dump(memory); return 0; } void main_message() { puts("*** Welcome to Simpletron! ***"); puts("*** Please enter your program one instruction ***"); puts("*** (or data word) at a time. I will type the ***"); puts("*** location number and a question mark (?). ***"); puts("*** You then type the word for that location. ***"); puts("*** Type the sentinel -99999 to stop entering ***"); puts("*** your program. ***"); puts(""); puts(""); } void computer_dump(int *memory) { //Special Computer Registers puts("REGISTERS:"); printf("accumulator %+05d\n",0); printf("instructionCounter %02d\n",0); printf("instructionRegister %+05d\n",0); printf("operationCode %02d\n",0); printf("operand %02d\n",0); puts(""); //Printing Memory puts("MEMORY:"); for(int i = 0; i<10; i++) printf(" %d",i); puts(""); for(int i = 0; i<91; i+=10) { printf("%d ",i); for(int j = 0; j < 10; j ++) { printf("%+.04d ",*(memory + (i+j))); } puts(""); } } void load(int *memory,int *instructionCounter) { //Goal load data into memory array using pointers while(1) { printf("%02d ? ", *instructionCounter); scanf("%d", (memory + *instructionCounter)); if(*(memory + *instructionCounter) == -99999) // Checking if termination value has been entered { *(memory + *instructionCounter) = 00; break; } if(*(memory + *instructionCounter) < -9999 || *(memory + *instructionCounter) > 9999) // Checking if word is in range { *(memory + *instructionCounter) = 00; puts("Please type a word between the ranges of -9999 and +9999"); continue; } *instructionCounter+=1; }; puts("*** Program loading completed ***"); puts("*** Program execution begins ***"); puts(""); puts(""); } void execute(int *instructionCounter, int memory[], int *instructionRegister, int *operationCode, int *operand, unsigned int *accumulator) { puts("Performing the execution phase!"); for (int i = 0; i <*instructionCounter; i++) { *instructionRegister = memory[i]; *operationCode = *instructionRegister / 100; *operand = *instructionRegister % 100; // operation instructions if(*operationCode == READ) // Input/Output operations { printf("? "); scanf("%d", &memory[*operand]); } else if(*operationCode == WRITE) { printf("%d",memory[*operand]); } else if(*operationCode == LOAD) // Load/Store Operations { *accumulator = memory[*operand]; } else if(*operationCode == STORE) { memory[*operand] = *accumulator; } else if(*operationCode == ADD) // Arithmetic Operations { if (*accumulator + memory[*operand] < -9999 || *accumulator + memory[*operand] > 9999 ) { puts("*** Operation resulted in a value outside the bounds of memory ***"); puts("*** Simpletron execution abnormally terminated ***"); break; } *accumulator+= memory[*operand]; printf("Accumulator after addition operation performed: %d\n",*accumulator); } else if(*operationCode == SUBTRACT) { if (*accumulator - memory[*operand] < -9999 || (*accumulator - memory[*operand]) > 9999 ) { puts("*** Operation resulted in a value outside the bounds of memory ***"); puts("*** Simpletron execution abnormally terminated ***"); break; } *accumulator -= memory[*operand]; } else if(*operationCode == DIVIDE) { if (memory[*operand] == 0) // Checking to see if number is undefined { puts("*** Attempt to divide by Zero ***"); puts("*** Simpletron execution abnormally terminated ***"); break; } *accumulator = *accumulator / memory[*operand]; } else if(*operationCode == MULTIPLY) { if (*accumulator * memory[*operand] < -9999 || *accumulator * memory[*operand] > 9999 ) { puts("*** Operation resulted in a value outside the bounds of memory ***"); puts("*** Simpletron execution abnormally terminated ***"); break; } *accumulator = *accumulator * memory[*operand]; } else if(*operationCode == BRANCH) // Transfer-Of-Control Operations { i = *operand; } else if(*operationCode == BRANCHNEG) { if(*accumulator < 0) i = *operand; } else if(*operationCode == BRANCHZERO) { if(*accumulator == 0) i = *operand; } else if(*operationCode == HALT) { puts("*** Simpletron execution terminated ***"); } } puts(""); puts(""); }
问题原因
核心错误在于accumulator被定义为无符号整数(unsigned int),而Simpletron的累加器需要支持正负数值运算。当无符号整数参与减法运算得到负数结果时,会触发无符号整数溢出回绕:比如0 - 1在32位系统中会被计算为4294967295(无符号的最大值)。
此时进行范围判断时,这个超大的无符号数会被隐式转换为有符号整数(或者按无符号规则比较),导致*accumulator - memory[*operand] > 9999的判断成立,错误触发异常终止逻辑。
另外,BRANCHNEG分支中的if(*accumulator < 0)判断也完全无效——无符号整数永远不可能小于0,这同样是类型定义错误导致的逻辑问题。
修复方案
修改
accumulator的类型为有符号整数(推荐):
将unsigned int accumulator = 0000;改为int accumulator = 0;,同时调整函数参数中的类型声明,确保所有相关变量都使用有符号类型。这是最彻底的修复,符合Simpletron累加器的设计需求。临时转换为有符号类型计算(临时方案):
如果暂时不想修改类型,可以在减法运算前将无符号值转换为有符号整数计算,再判断范围:else if(*operationCode == SUBTRACT) { int temp_result = (int)*accumulator - memory[*operand]; if (temp_result < -9999 || temp_result > 9999 ) { puts("*** Operation resulted in a value outside the bounds of memory ***"); puts("*** Simpletron execution abnormally terminated ***"); break; } *accumulator = (unsigned int)temp_result; }但这种方式需要额外处理类型转换的溢出风险,且无法解决
BRANCHNEG等其他分支的逻辑错误,因此不推荐。
注意:其他算术操作(ADD、MULTIPLY)以及BRANCHNEG分支也存在相同的无符号类型问题,需要同步修复。
内容的提问来源于stack exchange,提问作者Austin Castro

