【C语言】static改变了什么?

Static是C语言中的一个关键字,当它应用于不同上下文环境时,它的语义是不同的,在初学C语言的时候,它会对我们造成很多的困扰,今天就来扒拉扒拉它。
首先,我们得明白static对我们造成困扰的根本原因,那就是static在不同于语境下的不同作用,从原理上来说,static作用体现在两个方面:

  1. 改变标识符链接属性
  2. 改变变量的存储类型

要说链接属性,不得不提到标识符的作用域,作用域决定了标识符的链接属性,作用域有四种:
块作用域:位于一个花括号之间的所有语句被称为块,定义在块中的变量具有块作用域,其可见范围为从定义之处到该块的花括号结束。有一个例外就是,函数声明中的形参,虽然不位于花括号之间,但是也具有块作用域。
文件作用域:在代码块之外声明的标识符都具有文件作用域。
函数原型作用域:用于函数原型中的形参名,其范围是从形参定义处到原型声明结束。这个作用域可以避免不同函数原型中出现相同形参名从而导致的冲突。
函数作用域:仅用于goto语句的标签,在一个函数中,goto后所跟的标签作用域在整个函数都可见,保证了一个函数内goto后的标签唯一性。
链接属性有三种:external(外部),internal(内部)和none(无)。
在一个源文件中,不同位置定义的标识符的作用域是不同的,也就决定了它们的链接属性不同:具有块作用域,函数作用域,函数原型作用域的标识符其链接属性都是none,而具有文件作用域的标识符链接属性是external,来看一段代码:

int b = 0; // b具有文件作用域,链接属性为external

void function(int a); // 在函数原型声明中,形参a具有函数原型作用域

void function(int a) // 该函数具有文件作用域,链接属性为external,此时形参a和函数内定义变量c具有块作用域
{
    int c = a;
    printf("%d\n", a);
add:                // 标识符add具有函数作用域
    c = c + 1;

    if (c < 10)
    {
        goto add;
    }
    printf("%d\n", c);
}

int main()
{

    function(b);

    goto add; // 标识符add具有函数作用域

add:
    printf("此处add标签并不会与function函数内发生冲突,但是不建议使用相同标签\n");
}

在上述源文件中,四种作用域都有体现,但是链接属性只有external和none,此时static的作用就来了,当static作用于函数定义或代码块之外的变量声明定义时,它可以修改标识符的链接属性,由external变为internal,但是标识符的存储类型和作用域并不会受到影响,此时被static声明的函数或变量只能在声明它们的源文件中访问。
上述只是改变了标识符的链接属性,改变变量存储类型的情况是在针对代码块内部的变量声明时,也正好捋一下变量的存储类型。
C语言内存分区图如下:
在这里插入图片描述

C语言的所有变量都存储在堆区,栈区,全局(静态)区,除开C语言本身的内存分区,C语言运行在硬件环境上,那么CPU的寄存器也可以作为存储C语言内存数据的地方,所以,C语言的存储类型有以下4类:

  1. 自动类型:函数中所有的非静态局部变量,存储在堆栈
  2. 静态类型:声明时有static关键字的变量,存储在全局(静态)区
  3. extern存储类型:全局变量,包含被外部引用的全局变量,存储在全局(静态)区。
  4. 寄存器变量:代码块内由register修饰的变量,会被放入CPU的寄存器中,访问效率比内存高(很少用到,CPU寄存器就那么几个寄存器,编译器会决定你用register修饰的变量是否被放到寄存器中)

在代码块内被声明的变量其存储属性为自动类型,也就是说它存储在堆栈中,但是当我们加上static修饰后,会把它的存储类型由自动变为静态,放到全局变量区(静态存储区),所以被static修饰的局部变量只会被初始化一次,根本原因在于它的存储区域不是堆栈,不会在函数结束时被抄家。
总结:
Static用于函数定义或全局变量时,会修改它们的链接属性,但不改变存储类型和作用域。
Static用于局部变量时,会修改存储类型,但是变量的链接属性和作用域不受影响。

标题“51单片机通过MPU6050-DMP获取姿态角例程”解析 “51单片机通过MPU6050-DMP获取姿态角例程”是一个基于51系列单片机(一种常见的8位微控制器)的程序示例,用于读取MPU6050传感器的数据,并通过其内置的数字运动处理器(DMP)计算设备的姿态角(如倾斜角度、旋转角度等)。MPU6050是一款集成三轴加速度计和三轴陀螺仪的六自由度传感器,广泛应用于运动控制和姿态检测领域。该例程利用MPU6050的DMP功能,由DMP处理复杂的运动学算法,例如姿态融合,将加速度计和陀螺仪的数据进行整合,从而提供稳定且实时的姿态估计,减轻主控MCU的计算负担。最终,姿态角数据通过LCD1602显示屏以字符形式可视化展示,为用户提供直观的反馈。 从标签“51单片机 6050”可知,该项目主要涉及51单片机和MPU6050传感器这两个关键硬件组件。51单片机基于8051内核,因编程简单、成本低而被广泛应用;MPU6050作为惯性测量单元(IMU),可测量设备的线性和角速度。文件名“51-DMP-NET”可能表示这是一个与51单片机及DMP相关的网络资源或代码库,其中可能包含C语言等适合51单片机的编程语言的源代码、配置文件、用户手册、示例程序,以及可能的调试工具或IDE项目文件。 实现该项目需以下步骤:首先是硬件连接,将51单片机与MPU6050通过I2C接口正确连接,同时将LCD1602连接到51单片机的串行数据线和控制线上;接着是初始化设置,配置51单片机的I/O端口,初始化I2C通信协议,设置MPU6050的工作模式和数据输出速率;然后是DMP配置,启用MPU6050的DMP功能,加载预编译的DMP固件,并设置DMP输出数据的中断;之后是数据读取,通过中断服务程序从DMP接收姿态角数据,数据通常以四元数或欧拉角形式呈现;再接着是数据显示,将姿态角数据转换为可读的度数格
<think>好的,我现在需要回答用户关于C语言static关键字的用法。首先,我应该先整理自己已知的知识,并参考提供的引用资料来确保信息准确。 首先,我记得static在C语言中有两种主要用途:修饰变量和修饰函数。不过根据用户提供的引用,好像有三种用法?比如引用[2]提到三种,但可能第三种是修饰函数时的不同情况?或者可能有其他的用法?需要仔细看一下提供的引用内容。 引用[1]提到static有两种基本用法:修饰变量和函数。但引用[3]提到static函数的作用域限制,这可能属于修饰函数的用法。而引用[2]提到了三种,可能包括变量在局部和全局的不同情况?比如,当static修饰局部变量时,改变存储期;修饰全局变量时,改变链接属性;修饰函数时同样改变链接属性。这可能就是三种用法。 接下来,我需要按照用户的要求,结构清晰,分点说明。用户的问题是关于使用说明,所以应该详细说明每个用法的场景、效果以及例子。 首先,修饰局部变量。static局部变量会将变量的存储期从自动变为静态,即使函数结束,变量值仍然保留。例如在函数内部声明static int count = 0; 这样每次调用函数时,count的值不会重新初始化,而是保持上次调用后的值。这在需要统计函数调用次数时非常有用。 其次,修饰全局变量。通常全局变量具有外部链接属性,其他文件可以通过extern来访问。但用static修饰后,全局变量的链接属性变为内部,只能在当前文件内访问,避免命名冲突。例如在file.c中声明static int globalVar; 那么其他文件无法引用这个变量,增强了封装性。 第三,修饰函数。默认情况下,函数是extern的,可以被其他文件访问。但使用static修饰函数后,该函数只能在声明它的文件内使用,其他文件无法调用。这有助于隐藏实现细节,提高模块化。例如在工具函数中,如果不希望被外部使用,可以声明为static。 需要确保每个用法都有例子,并且符合用户给出的引用资料中的内容。例如引用[3]提到static函数的作用域仅在本文件,这和修饰函数的用法一致。引用[1]也提到static修饰变量和函数的基本用法,而引用[2]虽然提到三种,但可能将变量分为局部和全局两种情况,加上函数的情况,组成三种用法。 此外,还需要注意语法正确性,比如变量声明的位置和使用方式。例如,在函数内部使用static局部变量,或者在文件作用域使用static全局变量。同时,要指出使用static的好处,如提高安全性、可维护性,避免命名冲突等,如引用[1]和[3]所提到的。 可能还需要对比不使用static的情况,比如普通局部变量和static局部变量的区别,普通全局变量和static全局变量的区别,普通函数和static函数的区别。这样用户可以更清楚理解其作用。 最后,生成相关问题时要围绕static的不同用法、应用场景、优缺点等,确保问题具有针对性和启发性。</think>### C语言中`static`关键字的使用说明 `static`是C语言中的存储类说明符,主要作用包括**改变变量的存储期或作用域**,以及**限制函数或全局变量的可见性**。以下是具体用法及示例: --- #### 1. **修饰局部变量** 当`static`修饰局部变量时,变量的**存储期**从自动存储期(函数调用时分配,结束时释放)变为静态存储期(程序运行期间始终存在),但**作用域仍限于函数内部**。 **示例**: ```c void counter() { static int count = 0; // 静态局部变量 count++; printf("调用次数:%d\n", count); } ``` - **效果**:每次调用`counter()`时,`count`的值会保留上次调用的结果。 - **用途**:统计函数调用次数、保留状态信息等[^1][^3]。 --- #### 2. **修饰全局变量** 当`static`修饰全局变量时,变量的**链接属性**从外部链接(允许其他文件通过`extern`访问)变为内部链接(仅当前文件可见)。 **示例**: 在文件`file1.c`中: ```c static int internal_var = 10; // 静态全局变量,仅file1.c可见 ``` 在文件`file2.c`中无法通过`extern`引用`internal_var`。 - **用途**:避免多文件编程时的命名冲突,增强封装性[^1][^3]。 --- #### 3. **修饰函数** 当`static`修饰函数时,函数的**链接属性**从外部链接变为内部链接,即该函数仅能在定义它的文件中调用。 **示例**: 在文件`utils.c`中: ```c static void helper() { // 静态函数,仅utils.c可用 // 辅助逻辑 } ``` 其他文件无法调用`helper()`。 - **用途**:隐藏工具函数,提高模块化设计。 --- #### 核心优势 - **安全性**:限制变量/函数的作用域,防止误用。 - **可维护性**:明确变量/函数的可见范围,降低代码耦合。 - **内存管理**:静态局部变量避免重复初始化和释放,提升效率[^3]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值