【发布时间】:2021-12-15 02:47:21
【问题描述】:
我正在解决 CS:APP 课程中的一个实验练习作为自学。
在 CS:APP 课程中,最大正数可以用二进制补码中的 4 个字节表示,标记为Tmax(等于0x7fffffff)。
同样,最大负数标记为Tmin(等于0x80000000)。
练习的目标是实现一个isTmax() 函数,当给定Tmax 时,它应该返回1,否则它应该返回0。这只能使用一组受限的运算符来完成,它们是:@987654327 @,最大运算符数为 10。
您可以在下面看到我对isTmax() 函数的实现,cmets 解释了它应该如何工作。
#include <stdio.h>
int isTmax(int x)
{
/* Ok, lets assume that x really is tMax.
* This means that if we add 1 to it we get tMin, lets call it
* possible_tmin. We can produce an actual tMin with left shift.
* We can now xor both tmins, lets call the result check.
* If inputs to xor are identical then the check will be equal to
* 0x00000000, if they are not identical then the result will be some
* value different from 0x00000000.
* As a final step we logicaly negate check to get the requested behaviour.
* */
int possible_tmin = x + 1;
int tmin = 1 << 31;
int check = possible_tmin ^ tmin;
int negated_check = !check;
printf("input =\t\t 0x%08x\n", x);
printf("possible_tmin =\t 0x%08x\n", possible_tmin);
printf("tmin =\t\t 0x%08x\n", tmin);
printf("check =\t\t 0x%08x\n", check);
printf("negated_check =\t 0x%08x\n", negated_check);
return negated_check;
}
int main()
{
printf("output: %i", isTmax(0x7fffffff));
return 0;
}
我面临的问题是在编译程序时是否设置了优化标志会得到不同的输出。我正在使用gcc 11.1.0。
没有优化我得到这个输出,这对于给定的输入是正确的:
$ gcc main.c -lm -m32 -Wall && ./a.out
input = 0x7fffffff
possible_tmin = 0x80000000
tmin = 0x80000000
check = 0x00000000
negated_check = 0x00000001
output: 1
启用优化后,我得到了这个输出,这是不正确的。
gcc main.c -lm -m32 -Wall -O1 && ./a.out
input = 0x7fffffff
possible_tmin = 0x80000000
tmin = 0x80000000
check = 0x00000000
negated_check = 0x00000000
output: 0
由于某种原因,启用优化时逻辑否定未应用于check 变量。
在任何其他优化级别(-O2、-O3、-Os)下问题仍然存在。
即使我将表达式写成单行 return !((x + 1) ^ (1 << 31)); 也没有任何变化。
如果我将 check 声明为 volatile,我可以“强制”正确的行为。
我正在使用与练习附带的自动检查器相同的优化级别,如果我将其关闭,我的代码将通过所有检查。
谁能解释为什么会发生这种情况?为什么逻辑否定不会发生?
编辑:我添加了一个部分,其中包含与我忘记包含在原始帖子中的练习相关的额外指南和限制。具体来说,我不允许使用任何其他数据类型来代替int。我不确定这是否还包括文字后缀U。
Replace the "return" statement in each function with one
or more lines of C code that implements the function. Your code
must conform to the following style:
int Funct(arg1, arg2, ...) {
/* brief description of how your implementation works */
int var1 = Expr1;
...
int varM = ExprM;
varJ = ExprJ;
...
varN = ExprN;
return ExprR;
}
Each "Expr" is an expression using ONLY the following:
1. Integer constants 0 through 255 (0xFF), inclusive. You are
not allowed to use big constants such as 0xffffffff.
2. Function arguments and local variables (no global variables).
3. Unary integer operations ! ~
4. Binary integer operations & ^ | + << >>
Some of the problems restrict the set of allowed operators even further.
Each "Expr" may consist of multiple operators. You are not restricted to
one operator per line.
You are expressly forbidden to:
1. Use any control constructs such as if, do, while, for, switch, etc.
2. Define or use any macros.
3. Define any additional functions in this file.
4. Call any functions.
5. Use any other operations, such as &&, ||, -, or ?:
6. Use any form of casting.
7. Use any data type other than int. This implies that you
cannot use arrays, structs, or unions.
You may assume that your machine:
1. Uses 2s complement, 32-bit representations of integers.
2. Performs right shifts arithmetically.
3. Has unpredictable behavior when shifting an integer by more
than the word size.
【问题讨论】:
-
签名溢出会导致未定义的行为。因此,如果您将 x=tmax 传递给您已经在第一条语句中导致 ub 的函数。
标签: c optimization