学习笔记分享

分享与展示个人专业知识学习笔记

0%

C语言链接属性

什么是链接属性

我们编译C程序时通常不止一个源文件,不同源文件中可能有相同的标识符,链接就决定了不同源文件中标识符的关系,即相同标识符是否指向同一个实体。

更通俗地说,就是在两个不同文件中的变量、函数声明是否指向同一个实体。比如:a、b文件同时声明了变量c,链接属性就指定了这两处变量c是否是同一个c。

链接属性指明了这一关系。通过改变标识符链接属性,你能决定在a文件中的标识符要不要指向b文件中相同标识符的声明。

链接属性的分类

链接属性有三种:

  • external - 外部链接
  • internal - 内部链接
  • none - 无链接

对于external属性的标识符,不同文件中出现的多个同名称标识符指向同一个实体。在C语言中,用extern关键字在声明中指定以引用其他文件中定义的相同标识符

对于internal属性的标识符,仅在当前文件内该标识符指向同一个实体。在C语言中,用static关键字在声明中指定让标识符变为该文件私有(只有对原本缺省的链接属性为external的标识符,才能用static关键字改变其链接属性为internal)。

对于none属性的标识符,在每个声明位置都是一个新的实体。C语言中,没有对应的关键字,由上下文确定。

默认的链接属性

标识符的默认的链接属性与其出现的位置有关。

  • 程序的全局变量、所有函数默认的链接属性为external。

  • 其余标识符的默认链接属性为none。

在以下例子中,b、c、f的链接属性就是external:

1
2
3
4
5
6
7
typedef char *a;
int b;
int c(int d)
{
int e;
int f(int g);
}

具体用法

extern

在a文件中想要使用b文件中定义的external属性标识符,可使用extern关键字在a文件中声明。

即使该标识符所在上下文下默认链接属性为external,也建议使用extern关键字显式说明,有利于增加程序可读性

static

在a文件定义了一个全局标识符,但不想被其他文件访问,可以对该标识符加上static关键字。

在a、b文件中定义了同样的标识符,通过static关键字可以避免多重定义问题。

同一个标识符在链接中只能存在一个,那么通过内部链接的方式可以隔绝同名外部链接,且限定了外部编译单元不能访问该文件全局标识符。

再次提醒:只有对原本缺省的链接属性为external的标识符,才能用static关键字改变其链接属性为internal

一些细节

  1. 对于external属性的标识符,你可以在多个不同源文件中声明,但是你只能在一处初始化。否则就会出现重复定义的问题:multiple definition of 'a';
  2. extern关键字声明的标识符用于访问其他文件中定义的同名的标识符,因此无法进行初始化。如果你对extern声明的变量进行初始化就会生成警告:warning: ‘a’ initialized and declared ‘extern’
  3. 如果在其他文件中不存在相应的标识符定义,却在当前文件中使用了extern声明,会报错:undefined reference to 'a',原理同2。
  4. external属性的标识符总是静态存储类型。
  5. static关键字还有改变存储类型的作用,因此,其作用与上下文环境有关,只有对于默认链接属性为external的标识符,才有改变链接属性的作用。
  6. C++中,const变量隐含的具有internal属性,C中并不具有这一性质。

实例分析

external链接重复定义

a.c:

1
2
3
4
5
6
7
8
9
#include <stdio.h>

int a = 3;

int main(void)
{
printf("a = %d\n", a);
return 0;
}

b.c:

1
int a = 1;

编译运行:

1
2
3
$ gcc a.c b.c
/usr/bin/ld: /tmp/ccoNPRr3.o:(.data+0x0): multiple definition of `a'; /tmp/cc8qHzR5.o:(.data+0x0): first defined here
collect2: error: ld returned 1 exit status

a.c和b.c中的变量a在上下文中缺省为外部链接属性,两处都进行了声明,因此报错重复定义。

internal和none

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
static int i; // definition
// static storage
// internal linkage

void f(void)
{
extern int i; // declaration
// refers to the static i at file scope
// note that even though the specifier is extern
// its linkage is intern (this is legal in both C/C++)
{
int i; // definition
// automatic storage
// no linkage
}
}
  1. 作用域不同。这个跟链接属性无关,不是由链接属性决定的,作用域只与其上下文位置有关。但不难看出,internal属性标识符出现的位置都是在文件作用域,而none往往在代码块作用域。
  2. 参不参与链接。这段代码很好地说明了internal属性的变量在文件内部参与链接,因为你能够在内部通过extern声明显示得到。而没有链接属性的变量无法通过这一方式得到。

internal链接

a.c:

1
2
3
4
5
6
7
8
9
10
#include <stdio.h>

static int a = 3;

int main(void)
{
extern int a;
printf("a = %d\n", a);
return 0;
}

b.c:

1
int a = 1;

编译运行:

1
2
3
$ gcc a.c b.c
$ ./a.out
a = 3

参考

创作不易,支持一下吧!