📜  静态和动态作用域

📅  最后修改于: 2021-06-28 07:22:19             🧑  作者: Mango

变量x的范围是程序的区域,其中x的使用指的是变量的声明。进行范围界定的基本原因之一是使程序不同部分中的变量彼此不同。由于仅有少量的简短变量名,并且程序员共享有关变量命名的习惯(例如,i用于数组索引),因此在任何大小适中的程序中,相同的变量名都将在多个不同的范围中使用。

范围界定通常分为两类:
1.静态作用域
2.动态范围

静态作用域:
静态作用域也称为词汇作用域。在这种范围内,变量始终指的是其顶层环境。这是程序文本的属性,与运行时调用堆栈无关。静态作用域还使编写模块化代码变得更加容易,因为程序员仅通过查看代码即可确定范围。相反,动态范围要求程序员预见所有可能的动态上下文。

在大多数编程语言(包括C,C++和Java,变量始终是静态(或词法)范围的,即,变量的绑定可以由程序文本确定,并且独立于运行时函数调用堆栈。

例如,以下程序的输出为10,即f()返回的值不依赖于谁在调用它(就像g()调用它并且具有值为20的斧头)。 f()始终返回全局变量x的值。

// A C program to demonstrate static scoping.
#include
int x = 10;
  
// Called by g()
int f()
{
   return x;
}
  
// g() has its own variable
// named as x and calls f()
int g()
{
   int x = 20;
   return f();
}
  
int main()
{
  printf("%d", g());
  printf("\n");
  return 0;
}

输出 :

10

总而言之,编译器首先在当前块中进行搜索,然后在全局变量中进行搜索,然后在依次较小的范围内进行搜索。

动态范围:
对于动态范围,全局标识符是指与最新环境关联的标识符,在现代语言中并不常见。用技术术语来说,这意味着每个标识符都具有绑定的全局堆栈,并且在最近的绑定中搜索标识符的出现。

简单来说,在动态范围界定中,编译器首先搜索当前块,然后依次搜索所有调用函数。

// Since dynamic scoping is very uncommon in 
// the familiar languages, we consider the 
// following pseudo code as our example. It
// prints 20 in a language that uses dynamic
// scoping.   
  
int x = 10;
  
// Called by g()
int f()
{
   return x;
}
  
// g() has its own variable
// named as x and calls f()
int g()
{
   int x = 20;
   return f();
}
  
main()
{
  printf(g());
}

以使用动态范围界定的一种语言输出:

20

静态与动态范围
在大多数编程语言中,静态作用域是主要的。这仅仅是因为在静态作用域中,仅通过查看代码就可以很容易地推理和理解。通过查看编辑器中的文本,我们可以看到范围内的变量。

动态范围并不在乎代码的编写方式,而在乎代码的执行方式。每次执行新函数,都会将新作用域推入堆栈。

Perl支持动态和静态作用域。 Perl的关键字“ my”定义了一个静态范围内的局部变量,而关键字“ local”则定义了动态范围内的局部变量。

# A perl code to demonstrate dynamic scoping
$x = 10;
sub f 
{ 
   return $x; 
}
sub g 
{ 
   # Since local is used, x uses
   # dynamic scoping. 
   local $x = 20; 
  
   return f(); 
}
print g()."\n";

输出 :

20