C语言基础:static关键字

news2024/11/28 14:53:13

本文结合工作经验,研究C语言中static关键字的用法。

文章目录

  • 1 static关键字概念
  • 2 用法与使用场景
    • 2.1 修饰全局变量
      • 2.1.1 代码示例
      • 2.1.2 使用场景
    • 2.2 修饰函数
      • 2.2.1 代码示例
      • 2.2.2 使用场景
    • 2.3 修饰局部变量
      • 2.3.1 代码示例
      • 2.4.2 使用场景
  • 3 总结

1 static关键字概念

static关键字在C语言中非常常用,博主工作中常见三种用法:修饰全局变量、修饰局部变量以及修饰函数。通过static关键字的修饰,可以使被修饰的变量或函数只在作用域中有效,类似一种私有化的机制。

2 用法与使用场景

2.1 修饰全局变量

2.1.1 代码示例

在函数外定义的变量称为全局变量。全局二字表示,在其他的c文件中也可以调用这个变量。

例如如下代码demo1.c,main.c:

//demo1.c

int demo_a;

void demo1(void)
{
	demo_a = 10;
}
//main.c

#include <stdio.h>

extern int demo_a;
extern void demo1(void);

int main()
{
    demo1();
    printf("demo_a = %d\n", demo_a);
}

首先,在demo1.c中定义了一个全局变量demo_a,以及一个函数demo1(void)用于将10赋值给全局变量。

然后,在main函数中通过extern关键字,外部声明全局变量demo_a和函数demo1(void)。在main函数内部先通过调用demo1()将demo_a赋值为10,再编译软件打印出来。

打印在控制台上的结果如下:

在这里插入图片描述
这个demo对熟悉C语言的人来说是小菜一碟。之所以能在其他c文件中调用到demo_a的值,是因为全局变量的作用域为整个工程的c文件中。

接着在这个基础上,如果加上关键字static修饰全局变量demo_a,如下图:

//demo1.c

static int demo_a;//加上关键字static修饰全局变量demo_a

void demo1(void)
{
	demo_a = 10;
}

再编译一下就会报错:
在这里插入图片描述
这里报错的意思是,在软件链接的过程中,搜索工程中所有的c文件都无法找到demo_a这个变量。因为static关键字将demo_a这个变量的作用域限定在demo1.c这个文件中才可以使用了,外部其他文件即使extern声明了也无法引用。

2.1.2 使用场景

博主工作经验中,这种用static修饰全局变量,是为了将该全局变量限定在自身的c文件中使用,防止其他c文件随意地篡改掉。例如上一节中的做法,外部文件是完全无法访问到demo_a的值的。

如果想要获取静态全局变量的数值该怎么办呢。可以通过调用函数返回值获取,把代码改成下图所示。

//main.c

#include <stdio.h>

extern int demo1(void);

int main()
{
    int demo_a = demo1();    
    printf("demo_a = %d\n", demo_a);
}

//demo1.c

static int demo_a;

int demo1(void)
{
	demo_a = 10;
	return demo_a;
}

将demo_a的数值通过demo1()函数返回给main函数,这样外部就可以获得该静态全局变量的值。注意一点,demo1.c和main.c文件中都出现了demo_a变量,但不是一回事,一个是静态全局变量,另一个是函数中的局部变量。也就是说,在main函数中将局部变量demo_a的值改成其他的值(例如20),是不会影响到demo1.c文件中的静态全局变量demo_a的。这样,也就使得外部文件只能读不能写,达到了既能给外界传递信息,又能防篡改的效果。

2.2 修饰函数

2.2.1 代码示例

将上一节的代码稍微改一下,如下:
例如如下代码demo1.c,main.c:

//demo1.c

int demo_a;

static void demo1(void)
{
	demo_a = 10;
}
//main.c

#include <stdio.h>

extern int demo_a;
extern void demo1(void);

int main()
{
    demo1();
    printf("demo_a = %d\n", demo_a);
}

将demo1.c中的demo1(void)函数定义之前加上static关键字,就成为了静态函数。这时候在main函数中通过extern外部声明demo1(void)函数,就无法调用了,运行结果如下:
在这里插入图片描述
报错就和上一节类似,找不到demo1(void)这个函数的定义。static关键字也将这个函数的作用域限定在本文件中。

把代码稍微改一改,改成如下嵌套调用的形式:

//main.c

#include <stdio.h>

extern int demo_a;
extern void demo(void);

int main()
{
    demo();
    printf("demo_a = %d\n", demo_a);
}

//demo1.c

int demo_a;

static void demo1(void)
{
	demo_a = 10;
}

void demo(void)
{
	demo1();//demo()函数中调用demo1()静态函数
}

这样在运行一下,就能正常地打印出来了。

这说明static修饰的函数只能在该文件内部被别的函数调用,不能在别的文件通过extern外部声明调用到。

2.2.2 使用场景

博主在工作中遇到static修饰函数地使用场景是,一个文件地函数中划分了若干个功能,提取为几个子函数,将这几个子函数用static修饰成静态函数。这样做防止外部函数调用子函数,只限制在该文件内调用。

例如如下代码:

//demo1.c

static void demo1(void)
{
	demo_a = 10;
}

static void demo1(void)
{
	printf("demo_a = %d\n", demo_a);
}

void demo(void)
{
	demo1();
	demo2();
}

demo(void)函数调用了自身c文件的两个其他的静态函数,这两个静态函数分别实现两个功能:赋值与打印。然后别的外部函数可以调用非静态的demo(void)函数,从而执行下面的两个子函数。

2.3 修饰局部变量

局部变量定义在某个函数内部,作用域仅限于该函数,函数运行完毕后就被释放掉了。如果在局部变量之前加上static修饰,就会不一样。

2.3.1 代码示例

首先举个简单的局部变量的例子:

//main.c

#include <stdio.h>

void demo(void)
{
    int a = 0;
    a++;
    printf("%d ", a);
}

int main()
{
    for (int i = 0; i < 5; i++)
    {
        demo();
    }
}

在demo()函数中定义一个局部变量a,初始化为0,接着自加1并打印出来。在main函数中通过for循环调用5次demo()函数,查看5次的打印结果如下:
在这里插入图片描述
这里打印出来了5个1,是因为每次进入demo()函数,int a都会新开辟一个栈空间存储这个临时变量,并初始化为0。接着自加1并打印,所以每次都输出1。

接着,稍微改一下代码,在int a局部变量定义之前加上static修饰,代码改为如下:

//main.c

#include <stdio.h>

void demo(void)
{
    static int a = 0;//加上static修饰
    a++;
    printf("%d ", a);
}

int main()
{
    for (int i = 0; i < 5; i++)
    {
        demo();
    }
}

再次运行程序,打印如下:
在这里插入图片描述
打印的结果变为了12345,也就是说,每次调用demo()函数后,a的值保留到了下一次调用,再自加1打印出来。

这里的原因是static关键字将局部变量a变成了静态局部变量。静态局部变量的特点是不会在函数调用后释放掉,而是在编译好了以后直接分配一个固定的地址并直接初始化为0(这一点类似于全局变量)。当每次demo()函数运行时,由于a已经预先定义好了,就不会执行static int a = 0;这一句语句。

2.4.2 使用场景

static局部变量能达到一个保留变量数值不被释放掉的效果,这一点在嵌入式C语言开发中非常有用。在周期调度某个函数时,static修饰的局部变量就可以保留上一周期的值。

举个一阶滤波算法的例子,一阶滤波公式如下:

Y(n) = α*X(n) + (1−α)*Y(n−1)

一阶滤波的输出值需要依赖上一次的输入值,所以就可以用一个静态局部变量存储本周期的输出值,到了下次调用函数的时候就成为了上次的输出值。参考如下代码。

#define Alpha 0.8

void first_order_filter(float input,float* output)
{
    static float output_previous = 0;
    *output = (1 - Alpha) * output_previous + input * Alpha;
    output_previous = *output;
}

3 总结

在工作中常见以上三种static关键字的使用方法。

>>返回个人博客总目录

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/537604.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

第13章:存储过程和存储函数

一、存储过程 1.1理解 含义&#xff1a; 存储过程stored procedure&#xff0c;思想是一组经过预先编译的SQL语句的封装。 存储过程预先存储在MySQL服务器上&#xff0c;需要执行的时候&#xff0c;客户端向服务器端发出调用存储过程的命令&#xff0c;服务器段把这组SQL执…

当我和ChatGPT-4聊完后,我觉得一切可能已经来不及了

飞机上有wifi&#xff0c;了然无味&#xff0c;在万米高空&#xff0c;和ChatGPT-4开始了一场坦诚的沟通&#xff0c;它全程都表现出高情商&#xff0c;以及不断尽量安抚我的情绪&#xff0c;而这&#xff0c;恰恰令我脊背发凉。 部分文字截取 ZM&#xff1a;我能不能理解每次对…

k8s学习-CKS真题-ImagePolicyWebhook容器镜像扫描

目录 题目环境搭建imagePolicyWebhook搭建 解题任务二任务三任务一检查 模拟考题参考 题目 Context cluster上设置了容器镜像扫描器&#xff0c;但尚未完全集成到 cluster 的配置中。 完成后&#xff0c;容器镜像扫描器应扫描并拒绝易受攻击的镜像的使用。 Task 注意&#xff…

5.17 ARM 作业

1. 2.用for循环实现1~100之间的和 13BA 3.xmind

可以找工作的C端的低代码产品,终于让我找到了

目录 写在前面 低代码平台 平台怎么选 各平台区别 为什么选它 写在前面 大家都知道低代码这个叫法是从B端叫起来的&#xff0c;也就是说不管是业务人员还是开发人员&#xff0c;都是企业内部使用。那么有没有C端的&#xff0c;且免费使用的低代码产品呢&#xff1f; 低代码…

一次性能优化思考过程

前言 最近业务上空闲了下来&#xff0c;也是把之前在开发时自身感受比较大的白屏时间放在了主线上去排查优化&#xff0c;这里记录一下笔者对于移动端vConsole脚本的引入问题全过程。 网络脚本与问题定位 对于白屏时间&#xff0c;与网络传输有很大关系&#xff0c;如图&…

该怎样学习网络安全知识?

首先&#xff0c;必须&#xff08;时刻&#xff09;意识到你是在学习一门可以说是最难的课程&#xff0c;是网络专业领域的顶尖课程&#xff0c;不是什么人、随随便便就能学好的。不然&#xff0c;大家都是黑客&#xff0c;也就没有黑客和网络安全的概念了。 很多朋友抱着学一…

#systemverilog# 之 event region 和 timeslot 仿真调度(五)实战

目录 一 问题代码 二 解决方法 2.1 调换代码顺序 2.2 #0 Delay 2.3 uvm class 执行移到re-avtive 2.4 搭建完备的UVM 验证平台 三 预期波形 经过之前文章的学习&#xff0c;想必大家对systemverilog 仿真调度的理解&#xff0c;应该八九不离十了。今天&#xff0c;我们…

基于STM32的NRF24L01 2.4G通讯模块的驱动实验(HAL库)

前言&#xff1a;本文为手把手教学NRF24L01 2.4G通讯模块的驱动实验&#xff0c;本教程的 MCU 采用STM32F103ZET6与STM32F103C8T6&#xff0c;彼此进行互相通讯。通过 CubeMX 软件配置 SPI 协议驱动NRF24L01 2.4G通讯模块&#xff08;HAL库&#xff09;。NRF24L01 2.4G是嵌入式…

渗透测试--5.2.hash密码的破解

目录 1.hashcat简介 2.hashcat参数 常见参数 哈希类型&#xff08;-m&#xff09; 破解模式&#xff08;-a&#xff09; 3.实例 步骤一&#xff1a;使用hash-identifier工具判断哈希值类型 步骤二&#xff1a;使用字典攻击进行破解 1.hashcat简介 hashcat号称世界上最…

js实现点击改变文字大小

目录 一、前言二、代码实现 一、前言 在编写代码之前我们先来看看通过js获取元素有几种方式&#xff1a; 1.第一种 document.querySelector() 返回文档中匹配指定的选择器组的第一个元素document.querySelectorAll(); 返回文档中匹配指定的选择器组的所有元素 <!DOCTYPE ht…

vector容器 [上]

目录 一、 对于vector的介绍 二、vector的定义 0x01 无参构造 0x02 构造并初始化n个val 0x03 使用迭代器进行初始化构造 0x04 拷贝构造 0x05 比较 三、 vector的遍历 0x01 push_back() 0x02 operator[] 和at() 0x03 遍历 四、vector 容量空间 0x01 max_size : 返回v…

【设置教程】未使用系统分配DNS地址 如何设置?答:

未使用系统分配DNS地址&#xff0c;是你的域名解析DNS地址不是当前系统的DNS地址。 设置方法&#xff1a;先关闭更新。域名控制台--域名列表--安全设置&#xff1a; 通过短信验证。 2、修改DNS地址&#xff1a;域名控制台--域名列表&#xff1a;点击域名&#xff1a; 3、点击&…

C语言函数速查

scanf函数 函数概要&#xff1a; scanf 函数从标准输入流中读取格式化字符串。 与 printf 格式化输出函数相反&#xff0c;scanf 函数是格式化输入函数。 函数原型&#xff1a; #include <stdio.h> ... int scanf(const char *format, ...);参数解析&#xff1a; 1…

事务 ---MySQL的总结(六)

事务 多进程进行并改变同一个数据&#xff0c;如果没有进行版本控制&#xff0c;就会出现数据不确定的问题&#xff0c;为此引入了事务的概念。可以进行数据回滚&#xff0c;解决潜在的问题。 事务的概念 一组的DML组成&#xff0c;这一些的DML要么同时成功&#xff0c;要么同…

Linux上开启coredump

Linux上开启core dump Core dump&#xff08;核心转储&#xff09;是在程序崩溃时生成的一种文件&#xff0c;其中包含了程序在崩溃时的内存状态信息。它可以帮助程序员在调试程序时快速定位问题&#xff0c;并且是一种非常有用的调试工具。core dump的作用如下&#xff1a; 帮…

Java面向对象程序设计实验报告(实验二 面向对象基础练习)

✨作者&#xff1a;命运之光 ✨ 专栏&#xff1a;Java面向对象程序设计实验报告 目录 ✨一、需求设计 ✨二、概要设计 ✨三、详细设计 ✨四、调试结果 ✨五、测试结果 ✨附录&#xff1a;源程序代码&#xff08;带注释&#xff09; 测试类demo2 Address类 Employee类…

Docker之DockerFile相关基础知识

DockerFile相关基础知识 一、Docker镜像原理1、操作系统组成部分1.1 七大子系统1.2 Linux文件系统 2、Docker镜像原理介绍2.1 原理图2.2 Docker镜像本质2.3 统一文件系统2.4 复用性2.5 统一性 二、容器转为镜像1、Docker镜像的制作1.1 容器转换为镜像1.2 镜像转为压缩文件1.3 导…

【SpringBoot】三:访问数据库

文章目录 1.DataSource2.JdbcTemplate2.1 准备环境2.2 准备表和数据2.3 配置数据源2.4 JdbcTemplate访问mysql2.5 创建实体类 ArticlePO2.6 测试2.6.1 测试聚合函数 3.mybatis3.1 单表CRUMD3.1.1 创建模块3.1.2 查询3.1.3 插入3.1.4 更新3.1.5 删除 3.2 ResultMap3.3 SQL 提供者…

Elasticsearch 集群部署管理

Elasticsearch 集群配置版本均为8以上 安装前准备 CPU 2C 内存4G或更多 操作系统: Ubuntu20.04,Ubuntu18.04,Rocky8.X,Centos 7.X 操作系统盘50G 主机名设置规则为nodeX.qingtong.org 生产环境建议准备单独的数据磁盘主机名 #各自服务器配置自己的主机名 hostnamectl set-ho…