链接性

标识符在不同翻译单元中是否指向同一个实体,发生在 链接期

作用域

标识符在源代码中的可见范围,发生在 编译期

块作用域

被 '{}' 包裹所形成的代码块,定义的变量只能在这个块内使用

比如if语句,while语句等等,这里需要注意的是 switch-case 语句,它整个就是一个块作用域,如果重复定义一个变量会报错,示例如下

  char c;
  ...
  switch (c) { // 整个switch-case是一个块作用域
    case 'a':
      int count = 1; // 第一次定义count
      break;
    case 'b':
      int count = 2; // 第二次定义count
      break;
  }

在这个示例中,每个case都是属于同一个作用域,所以count相当于在这个作用域内定义了多次,解决方法有两种,一个是在switch-case语句的外部定义,还有一种就是给case加上一个块作用域,独立于switch-case的这个大的作用域

// 法1
  int count; // 在外部定义
  switch (c) {
    case 'a': ...
    case 'b': ...
  }

  // 法2
  switch (c) {
    case 'a': {...} // 形成单独的作用域
    case 'b': {...}
  }

函数作用域

函数作用域是一个特殊的块作用域,它是函数实现的顶层块作用域,比如函数的参数就具有函数作用域

还有一个是,在函数内定义的标签(如 goto 标签)具有函数作用域,这意味着标签只在定义它的函数内有效,示例如下

  #include <stdio.h>

  void func() {
      goto label; // label 只能在 func 函数内使用
      label: 
      printf("This is a label.\n");
  }

  int main() {
      func();
      printf("%s", label); // 错误:label 在这里不可见
      return 0;
  }

文件作用域

在文件顶部定义的变量和函数在整个文件中可见,比如全局变量,可以在一个或多个源文件中使用

总结

什么时候提作用域/链接性

  • 分阶段

    • 编译期 -> 作用域

    • 链接期 -> 链接性

  • 分目的

    • 分析标识符的 可见性(适用范围) -> 作用域

    • 分析标识符的 实体同一性(标识符要有唯一对应的实体),比如一份函数原型,它对应的实体(即实现)只能唯一

static的作用

  • 限制作用域,将标识符限制在一个文件中使用,避免同名冲突,链接冲突(这些冲突是发生在多个文件中的,限制在一个文件内,这些冲突就没了)

特别提一点,我们这里只提及他对 作用域和链接性 的影响,而不是对 生命周期 的影响

作用域就块/函数/文件这三个吗

可能有其他的,但这三个最主要,他们的特殊情况可能成为新的 作用域名词

作用域/链接性/生命周期

这三个概念容易混淆,作用域强调的是 标识符的使用范围,链接性强调的是 标识符和实体的对应,生命周期强调的是 标识符的创建和销毁

举个例子

int add(int x)
{
  static int sum = 0;
  sum += x;
  return sum;
}

这里的sum变量,他的作用域是 块作用域,因为是顶层块作用域,所以也可以成为函数作用域,没有链接性(只在定义的文件中使用),生命周期则是 贯穿程序始终

 posted on 2024-10-01 20:08  Dylaris  阅读(120)  评论(2)    收藏  举报