4

此代码用于设置组件,会产生编译器警告:

[DCC 警告] Unit1.pas(742): W1024 结合有符号和无符号类型
                            - 扩大了两个操作数
var
  iPrecision: cardinal;
  iRadius: cardinal;
  iActive: boolean;
  iInProximity: boolean;

iPrecision := Max(50, 100 - (3 + 2 * ord(iActive and iInProximity)) * iRadius);

这可以以某种方式进行类型转换以防止编译器警告吗?

4

3 回答 3

5

Arnaud 解释说ord()返回一个有符号值,这是警告的来源。Arnaud 和 Ken 都建议如何通过避免使用无符号操作数来消除警告。

我想提供另一种意见,并建议您改为选择使用带符号的操作数。假设您仅使用有符号操作数执行计算。考虑以下程序:

{$APPTYPE CONSOLE}

uses
  Math;

function CalcPrecision(Radius: cardinal; Active, InProximity: boolean): Cardinal;
begin
  Result := Max(50, 100-(3+2*Cardinal(Active and InProximity))*Radius);
end;

begin
  Writeln(CalcPrecision(1000, True, True));
  Readln;
end.

我相信你会希望这个程序的输出是 50。它不是。输出为 4294962396。

发生的情况是您100-X在无符号上下文中执行X>100. 当你这样做时,你有整数溢出,结果是一个非常大的正值。由于您使用的是无符号算术,因此您不能期望这是一个负值,因为无符号中没有负值。

当然,如果启用了溢出检查的编译器选项,您会遇到运行时错误。但即使这样也不是你想要的。获得所需答案的简单方法是使用有符号算术执行操作。

{$APPTYPE CONSOLE}

uses
  Math;

function CalcPrecision(Radius: Integer; Active, InProximity: boolean): Integer;
begin
  Result := Max(50, 100-(3+2*ord(Active and InProximity))*Radius);
end;

begin
  Writeln(CalcPrecision(1000, True, True));
  Readln;
end.

这个程序产生了 50 的期望输出。

如果由于某种原因,您需要将值存储回无符号变量,则在计算之外执行此操作。由于上述原因,使用有符号值执行计算很重要。

....
var
  Precision: Cardinal;
begin
  Precision := CalcPrecision(1000, True, True);
  Writeln(Precision);
  Readln;
end.

当然,您确实可以提出会溢出计算的输入值,即使它是使用有符号算术编写的。但在实践中你会发现这样的输入是极不可能的。另一方面,使用相当合理的输入数据执行无符号运算时,很容易使您的方程式出错。

于 2013-06-24T07:06:49.177 回答
5

在您的情况下,ord()返回一个integer,因此需要明确地类型转换为cardinal,通过更改ord()cardinal()

iPrecision := Max(50, 100 - (3 + 2 * cardinal(iActive and iInProximity)) * iRadius);

您将摆脱警告,您的代码将几乎相同。

于 2013-06-24T06:41:16.550 回答
2

您可以通过以下任一方式避免编译器警告

  • 将变量更改Cardinal为有符号类型,例如Integer
  • 使用这样的轻微解决方法:
变量
  ...
常量
  BoolValues: 基数的array[Boolean] = (0, 1);
开始
  ...
  iPrecision := Max(50, 100 - (3 + 2 * BoolValues[iActive and iInProximity]) * iRadius);
结尾;

于 2013-06-24T00:21:41.297 回答