常数的好处

时间:2010-03-26 15:50:06

标签: programming-languages constants

我理解关于常量的一个重要事项是你不必经历并更新代码,在那里使用该常量。多数民众赞成,但是我们说你没有明确地宣布它为常数。采用HAPPENS实际上不会被改变并使其成为常数的变量有什么好处,这会节省处理和/或代码大小等等吗?

基本上我有一个程序,编译器说一个特定的变量没有改变,因此可以声明为常量,我只是想知道添加常量限定符对它有什么好处,如果它没有区别然后做出这种改变没有增加任何价值,因此没有浪费时间(同样的情况发生在多个地方)回去并“修复”所有这些变量。

14 个答案:

答案 0 :(得分:22)

如果将变量声明为常量,那么优化器通常可以通过“常量折叠”消除它,从而加速程序并节省空间。举个例子,考虑一下:

var int a = 5;
const int b = 7;
...
c = process(a*b);

编译器最终会创建一个指令,将a乘以7,并将其传递给'process',将结果存储在c中。但在这种情况下:

const int a = 5;
const int b = 7;
...
c = process(a*b);

编译器将简单地传递35进行处理,甚至不编码乘法。此外,如果编译器知道该进程没有副作用(即,是一个简单的计算),那么它甚至不会调用进程。它只是将c设置为进程(35)的返回值,为您节省函数调用。

答案 1 :(得分:21)

如果您将某些内容声明为常量,然后意外地尝试在代码中修改其值,则编译器会告诉您有关错误的信息。这种形式的静态类型检查实际上是使用常量的主要原因。

答案 2 :(得分:6)

这很大程度上取决于你的优化器有多好。

一个好的优化器会在编译期间用文字值替换const引用。这节省了处理器周期,因为生成的机器代码使用立即值而不必从内存加载值。

某些优化器会识别出一个值在声明后不会被修改,并将其转换为常量。不要依赖这种行为。

此外,只要有可能,您的代码应该强制执行您在开发过程中所做的假设。如果永远不应该更改“变量”,将其声明为常量将有助于确保您自己或后来出现的任何其他开发人员都无意中修改“常量”。

答案 3 :(得分:4)

将变量标记为常量会将您的意图声明为程序员,只要在执行代码期间访问它,它就是一致的值。将它视为一种文档形式,它也会使编译器产生你的设计。

答案 4 :(得分:3)

正如您所指出的,将未变化的变量更改为显式常量可能无法获得任何直接好处。但是,在分析程序时,所做的一项检查是查看变量的声明点到变量的参考点。因此,诸如在声明之前访问的变量,或者在引用之前设置和重置等的指标可以指示可能的错误。

通过明确地将常量声明为常量,编译器和分析工具知道您不打算随时重置变量。

对于使用您的代码的其他开发人员也是如此。他们可能会无意中设置变量,您可能不会意识到这一点。声明常量将避免这些类型的错误。

答案 5 :(得分:3)

编译器可能会减少代码大小..例如,您将找到的包System(在x86计算机上)

type Bit_Order is (High_Order_First, Low_Order_First);
Default_Bit_Order : constant Bit_Order := Low_Order_First;

所以给定

case System.Default_Bit_Order is
   when System.High_Order_First =>
      --  Big-endian processing
   when System.Low_Order_First =>
      --  Little-endian processing
end case;

当您的代码保持其可移植性时,编译器可以完全消除“错误”分支。使用GNAT,您需要使用非默认优化来实现:-O2我认为。

两个分支都必须是可编译的 - 这是一个优化,而不是#ifdef处理。

答案 6 :(得分:2)

一个好处是,如果它确实是一个恒定的值,你不会意外地改变它。常数可以防止你以后搞砸东西。作为一个变量,任何人都可以在更新代码后随时更改它。

应该清楚哪些值永远不会改变,哪些值可以改变。常数强化了你的意图。


我年轻时的真实世界的例子:

我们正在处理的系统上没有内置的PI值,因此有人创建了一个名为PI的VARIABLE,并且在某个地方,有人(我)将该值修改为3.14519(或其中的某些内容)我写的一个程序......不是pi 3.14159的近似值)。它已经在代码的其他地方设置,从那时起,各种计算都没有。如果PI是一个常量(当然后来改变了),我就不可能犯这个错误......至少不容易。

答案 7 :(得分:2)

除了已经说过的内容之外,将变量声明为常量可为优化器提供更多自由。它可以消除读取它的值,它可以消除创建临时副本的需要,它可以消除变量本身(对于数字常量尤其如此)。

常量的另一个大用例是常量对象。拥有一个常量对象(或者放弃对函数中常量对象的引用),可以确保不修改对象,并且只允许调用该对象的方法(称为const方法)。然而,对于C ++来说,这是正确的,我不确定Ada中的相同概念是否也有效。

答案 8 :(得分:2)

另一种可能性,取决于语言,编译器可能能够在多线程环境中进行一些优化,否则这些优化是不可能的。就像你说:

int b=x*f;
int c=y*f;

在多线程环境中,如果f是变量,则编译器可能必须在第二次操作之前生成f的重新加载。如果f是常量并且编译器知道它仍然在寄存器中,则不需要重新加载。

答案 9 :(得分:2)

当然对于像C和Ada这样的语言,编译器会将常量中的值直接放入汇编指令中,这意味着除了运行程序所需的内容之外,不需要交换寄存器或从内存中读取数据。 。这意味着两件事:主要的一个是速度(在许多应用程序中可能不那么明显,除非它们是嵌入式的),第二个是内存使用(程序的最终二进制大小和运行时内存占用量)。

这种行为将归结为语言和编译器,因为语言将决定您(程序员)所做的任何假设,从而限制语言的效率;和编译器,因为它的行为可以像处理任何其他变量一样处理你的常量,以预处理代码并尽可能地优化二进制速度和足迹。

其次,正如itsmatt和jball所说,它使您能够在逻辑上将项目视为常量配置项而不是“变量”;特别是在更高级的编程语言和解释语言中。

答案 10 :(得分:2)

这不是Ada特定的问题。

常量对变量的一般好处:

  • 防止代码错误“不断”被意外修改。
  • 通知编译器它可以假设优化时该值不会改变。

Ada特定的好处:

  • 对于数字常量,您不必指定类型(命名数字)。如果以这种方式声明它,它将在任何表达式中用于那种数字。如果使用变量,则只能在该变量的精确类型的表达式中使用。

示例:

Seconds_Per_Minute : constant := 60;
Secs_Per_Min       : Integer  := 60;

type Seconds_Offset is 0 .. Integer'last; --'

Min1, Secs1 : Integer;
Min2, Secs2 : Seconds_Offset;

...
--// Using the named number, both of these compile no problem.
Secs1 := Min1 * Seconds_Per_Minute;
Secs2 :=  Min2 * Seconds_Per_Minute;

--// The second line here gives an error, since Integer is a 
--// different type than Seconds_Offset.
Secs1 := Min1 * Secs_Per_Min;
Secs2 := Min2 * Secs_Per_Min;

答案 11 :(得分:2)

还有另一个好处,堆栈和数据段大小。

考虑:

function Recurser(i : Integer) return Integer is
  ia : array(0..9) of Integer
          := (1, 2, 3, 4, 5, 6, 7, 8, 9, 1000);
  r : Integer;
begin
   if i = 0 then return 0; end if;
   r := ia(i mod 10);
   return r + Recurser(i - 1);
end;

每次该函数递归时,在堆栈上创建一个320字节的结构。但是由于a的值没有改变,所以堆栈正在增加以保持一个恒定的变量。这在具有小堆栈的嵌入式平台上非常重要。

包级别变量也会增加数据段的大小。提高你的记忆要求。

答案 12 :(得分:1)

常量通常存储在只读存储器中,这可以防止在执行期间更改它们,并且访问它们可能更快,或者至少与访问RAM一样快。

答案 13 :(得分:0)

常量是不可变的值,它们在编译时是已知的,并且在程序的生命周期内不会更改。

常量与变量的不同之处在于,一旦将值赋值给常量,它就不会随后被更改。

在运行时,您可以确定常量中定义的值不会更改,因此您的程序不会中断。