C安全编程教学-声明和初始化-不要声明具有冲突链接类别的标识符_开发语言

注:本课程参考文献《C安全编码标准》

 欢迎关注我👆,收藏下次不迷路┗|`O′|┛ 嗷~~

目录

一.引言

二.不安全代码

三.修复方案

四.练习与答案

案例一

案例二

案例三


一.引言

    链接能够让一个标识符在不同的作用域中被声明,或者在同一作用域中被多次声明,所有这些声明都指向同一个对象或函数。标识符可以根据其链接属性被分为三类:外部链接、内部链接和无链接。这三种链接类型各自具有独特的特性。

    外部链接:在整个程序中,具有外部链接的标识符代表着相同的对象或函数,这意味着在程序的所有编译单元和库中,该标识符都指向同一个对象或函数。链接程序可以利用这个标识符进行链接。然而,如果在程序中对同一个具有外部链接的标识符进行第二次声明,链接程序将不再将其与原先的对象或函数关联。

    内部链接:具有内部链接的标识符指的是,在特定的翻译单元里,它们代表相同的对象或函数。由于链接程序并不掌握关于这些内部链接标识符的任何信息,所以这些标识符的作用范围仅限于该翻译单元内部。

    无链接:若标识符未建立链接,那么任何使用该标识符的后续声明都会创建新的对象,比如新的变量或新的类型。

链接按照如下方式确定:

    如果一个对象或者函数的文件作用域标识符包含存储类指示符static,该标识符具有内部链接。

    对于使用存储类指示符extern在某个作用域中声明的标识符,如果该标识符的前一次声明在此作用域中可见,且前一次声明指定内部或者外部链接,则后来声明的标识符链接与前一次声明相同。如果没有可见的前一次声明,或者前一次声明指定无链接,则该标识符具有外部链接。

    如果函数标识符的声明没有存储类指示符,那么它的链接确定方式和使用存储类指示符extern的情况相同。如果对象的标识符声明具有文件作用域且没有存储类指示符,则标识符具有外部链接。

    下列标识符没有链接:声明为对象或者函数之外的任何标识符;声明为函数参数的标识符;不使用存储类指示符extern的块作用域对象标识符。

    使用分类兼具内部和外部链接的标识符(在一个翻译单元中)是未定义行为。一个翻译单元包含源文件及其头文件,以及通过预处理指令#include包含的所有源文件。

    表为在一个翻译单元中声明两次的对象指定的链接方式。列方向表示第一次声明,行方向表示重新声明。

第二次声明

静态

无链接

外部

静态

内部

未定义

内部

第一次声明

无链接

未定义

无链接

外部

外部

未定义

未定义

外部

二.不安全代码

    在下列不安全代码示例中,i2和i5被定义为兼具内部和外部链接。这两个标识符未来的使用会造成未定义行为。

int i1=10;        /* Definition,external linkage */
static int i2=20; /* Definition,internal linkage */
extern int i3=30; /* Definition,external linkage */
int i4;           /* Tentative definition,external linkage */
static int i5;    /* Tentative definition,internal linkage */

int i1;           /* Valid tentative definition */
int i2;           /* Undefined,linkage disagreement with previous */
int i3;           /* Valid tentative definition */
int i4;           /* Valid tentative definition */
int i5;           /* Undefined,linkage disagreement with previous */

int main(void){
    /* ... */
    return 0;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.

三.修复方案

    下列兼容解决方案不包含冲突的定义:

int i1=10;        /* Definition,external linkage */
static int i2=20; /* Definition,internal linkage */
extern int i3=30; /* Definition,external linkage */
int i4;           /* Tentative definition,external linkage */
static int i5;    /* Tentative definition,internal linkage */

int main(void){
    /* ... */
    return 0;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.

四.练习与答案

案例一

问题描述

    在一个大型项目中,开发者在两个不同的源文件中声明了同一个全局变量,其中一个使用了static关键字,另一个则没有。这导致了链接时的未定义行为。

代码示例

// file1.c

static int globalVar = 42; // 内部链接
  • 1.

// file2.c

extern int globalVar; // 外部链接
  • 1.

问题

    上述代码中的globalVar变量在两个不同的文件中被声明,一个使用了static关键字,另一个使用了extern。这将导致什么类型的链接问题?

答案

    这将导致未定义行为,因为globalVar在同一个程序中被赋予了内部链接和外部链接。static关键字使得globalVarfile1.c中具有内部链接,而extern关键字在file2.c中声明了外部链接。链接器无法确定这两个声明是否指向同一个对象,因此行为未定义。

案例二

问题描述

    开发者在头文件中声明了一个全局变量,并在多个源文件中包含了这个头文件。然而,他们没有使用extern关键字,导致每个源文件都创建了一个该变量的独立副本。

代码示例

// header.h

int globalVar; // 无extern关键字
  • 1.

// file1.c

#include "header.h"
  • 1.

// file2.c

#include "header.h"
  • 1.

问题

    在上述代码中,globalVar变量会被如何处理?每个源文件都会有自己的globalVar副本吗?

答案

    是的,每个源文件都会有自己的globalVar副本。因为在头文件中声明globalVar时没有使用extern关键字,所以每个包含该头文件的源文件都会创建一个新的globalVar变量。正确的做法是在头文件中使用extern关键字来声明globalVar,并在一个源文件中定义它。

案例三

问题描述

    开发者在源文件中声明了一个静态变量,并在同一个源文件的另一个函数中尝试使用extern关键字来访问它。

代码示例

// file.c

static int myVar = 10; // 静态变量  
  
void func1() {  
    // ...  
}  
  
void func2() {  
    extern int myVar; // 尝试访问静态变量  
    // ...  
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.

问题

    在上述代码中,func2中的extern int myVar;声明能够成功访问func1之前声明的myVar变量吗?

答案

    不能。myVar在声明时使用了static关键字,因此它具有内部链接,其作用范围仅限于声明它的翻译单元内部。在func2中使用extern关键字尝试访问myVar是无效的,因为extern用于声明具有外部链接的变量。在这种情况下,func2中的myVar声明实际上是一个新的、未定义的外部变量声明,与func1中的myVar无关。

 非常感谢您花时间阅读我的博客,希望这些分享能为您带来启发和帮助。期待您的反馈与交流,让我们共同成长,再次感谢!

👇个人网站👇

 安城安的云世界

 

C安全编程教学-声明和初始化-不要声明具有冲突链接类别的标识符_前端_02