C++ 静态变量static的使用方法

static概述:

static关键字有三种使用方式,其中前两种只指在C语言中使用,第三种在C++中使用。

  1. 静态局部变量(C)

  2. 静态全局变量/函数(C)

  3. 静态数据成员/成员函数(C++)


静态局部变量

         静态局部变量在函数内部声明的静态变量。即使函数结束,静态局部变量的值也会保留。通常用在需要记住历史信息的情况下。

        局部静态变量是在函数内部声明的,但它有一个特别之处:它的生命周期是贯穿整个程序的运行期的,但它只在声明它的函数内部可见。这意味着,虽然它是局部的,但它的值在函数调用之间会保持不变!

        特点:

  • static类型分配在静态存储区,在程序整个运行期间都不释放;

  • static局部变量在初次运行时进行初始化工作,且只初始化一次;

  • 对于局部静态变量,如果不赋初值,编译期会自动赋初值0或者空

简单实例:

#include <iostream>void function() {static int count = 0; // 这就是一个局部静态变量count++;std::cout << "Count is: " << count << std::endl;
}int main() {function(); // 输出: Count is: 1function(); // 输出: Count is: 2function(); // 输出: Count is: 3return 0;
}

        在这个例子中,count是一个局部静态变量。每次调用function函数时,count的值都会递增,并且这个值会在函数调用之间保持。这就是局部静态变量的魅力所在!

        注意:

  • 当我们多次调用 function() 函数时,count 变量的值会累加,而不是每次都从 0 开始;
  • “记忆性”是程序运行很重要的一点就是可重复性,而static变量的“记忆性”破坏了可重复性,造成不同时刻同一函数的运行结果不同;
  • “生存期”全局性和唯一性。 普通的局部变量在栈上分配空间,因此每次调用函数时,分配的空间都可能不一样,而static具有全局唯一性的特点,每次调用时都指向同一块内存,这就造成一个很重要的问题—不可重入性;

局部静态变量的存储方式:

        局部静态变量虽然在函数内部声明,但它们的存储位置可不是在栈上哦!它们被存储在静态存储区,这意味着它们的生命周期会一直持续到程序结束。每次函数调用时,局部静态变量并不会重新初始化,而是会保持上一次函数调用结束时的值。

        想象一下,静态存储区就像是一个大仓库,局部静态变量就像是仓库里的一些特殊货物。这些货物在程序运行期间一直存在,不会因为函数的调用和返回而消失。每次你调用函数时,就像是去仓库里查看这些货物的状态,你可以更新它们的值,但它们的存在是持久的。


静态全局变量:

静态全局变量在函数外部声明的静态变量。它的作用范围局限于声明它的文件内。通常用于在一个文件内共享信息,但防止其他文件访问。

全局静态变量是在全局作用域声明的,但它的链接属性是内部的,这意味着它只能在本文件内访问,其他文件可不知道它的存在。

简单实例:

// file1.cpp#include <iostream>static int globalStaticVar = 10; // 这就是一个全局静态变量void printGlobalStaticVar() {std::cout << "Global static variable is: " << globalStaticVar << std::endl;
}// file2.cpp#include <iostream>// 这里尝试访问file1.cpp中的globalStaticVar会失败哦!
// extern int globalStaticVar; // 这行会报错,因为globalStaticVar是静态的,只在file1.cpp中可见int main() {printGlobalStaticVar(); // 输出: Global static variable is: 10return 0;
}

        在这个例子中,globalStaticVar是一个全局静态变量,它在file1.cpp中声明并定义。由于它是静态的,所以它的链接属性是内部的,只能在file1.cpp中访问。如果你尝试在file2.cpp中通过extern关键字来访问它,编译器会毫不留情地给你一个错误提示。

全局静态变量常常用来实现文件级别的封装,让某些变量只在本文件中可见和可用,避免与其他文件中的变量发生冲突。这个特性在大型项目中可是非常有用的哦!

注意:

        对于外部(全局)变量,不论是否有static限制,它的存储区域都是在静态存储区,生存期都是全局的,此时的static只是起作用域限制作用,限制作用域在本文件内部。

        全局静态变量虽然名字里有“全局”两个字,但它其实是个“伪全局”,因为它的作用域被限制在了声明它的那个文件里。这就像是你有个宝藏,虽然它很珍贵,但只有你一个人知道它藏在哪里,其他人都找不到它。

        所以,全局静态变量只能在当前文件内部被访问和修改,其他文件是看不到也摸不着的。这个特性在很多情况下都很有用,比如当你想要在某个文件中封装一些变量,但又不想让其他文件访问到它们时,全局静态变量就是你的好帮手啦!

全局静态变量的生命周期

全局静态变量在程序中具有以下生命周期特性:

  1. 创建与初始化‌:全局静态变量在程序开始执行之前就被创建,并且在程序结束时才被销毁。它们的初始化发生在程序启动时的某个阶段,具体取决于编程语言和编译器的实现。未初始化的全局静态变量可能会被赋予一个默认值(如0或NULL)。

  2. 持久性‌:由于全局静态变量在程序的整个生命周期内都存在,因此它们可以在程序的任何地方被访问和修改(当然,这取决于其作用域和访问权限)。这种持久性使得全局静态变量成为在不同函数或模块之间共享数据的理想选择。

  3. 单一实例‌:在程序的整个运行期间,全局静态变量只有一个实例。这意味着无论程序中有多少个函数或模块访问该变量,它们实际上都是在操作同一个内存地址上的数据。

全局静态变量的使用方法

全局静态变量在编程中的使用方法包括以下几个方面:

  1. 定义与声明‌:全局静态变量通常在文件的顶层作用域中声明,使用static关键字进行修饰。这表示该变量具有文件作用域,即它只能在定义它的文件内部被访问。

    // 在C语言中定义全局静态变量
    static int globalStaticVar = 0;
    
  2. 访问与修改‌:由于全局静态变量在程序的任何地方都可见(只要它在定义它的文件内部),因此可以通过直接引用其名称来访问和修改它的值。但是,需要注意的是,过度使用全局静态变量可能会导致代码难以维护和理解。因此,在实际编程中,应尽量避免不必要的全局静态变量使用。

  3. 跨文件共享‌:虽然全局静态变量具有文件作用域,但有时需要在多个文件之间共享数据。为了实现这一点,可以使用外部链接(external linkage)或头文件来声明全局静态变量,并在需要共享数据的文件中包含该头文件。然而,这种方法可能会引入一些复杂性,并且需要谨慎处理以避免命名冲突和数据竞争等问题。

  4. 注意事项‌:

    • 由于全局静态变量在程序的整个生命周期内都存在,因此应谨慎处理其内存管理问题,以避免内存泄漏或其他内存相关问题。
    • 在多线程环境中使用全局静态变量时,需要特别注意线程安全性问题。可能需要使用互斥锁或其他同步机制来保护对全局静态变量的访问。

全局静态变量的使用实例

// file1.cpp#include <iostream>// 声明一个全局静态变量
static int globalStaticVar = 0;void incrementGlobalStaticVar() {globalStaticVar++;std::cout << "Global static variable is now: " << globalStaticVar << std::endl;
}// file2.cpp#include <iostream>// 这里我们声明了一个与file1.cpp中同名的全局静态变量
// 但由于它是静态的,所以这两个变量其实是独立的哦!
static int globalStaticVar = 100;void printGlobalStaticVar() {std::cout << "In file2, global static variable is: " << globalStaticVar << std::endl;
}int main() {// 调用file1.cpp中的函数incrementGlobalStaticVar(); // 输出: Global static variable is now: 1incrementGlobalStaticVar(); // 输出: Global static variable is now: 2// 调用file2.cpp中的函数printGlobalStaticVar(); // 输出: In file2, global static variable is: 100return 0;
}

        在这个例子中,file1.cpp 和 file2.cpp 都声明了一个名为 globalStaticVar 的全局静态变量。但是,由于它们是静态的,所以这两个变量其实是完全独立的,互不影响。

在 main 函数中,我们调用了 incrementGlobalStaticVar 函数,这个函数会递增 file1.cpp 中的 globalStaticVar。然后,我们调用了 printGlobalStaticVar 函数,这个函数会打印出 file2.cpp 中的 globalStaticVar 的值。

        你看,全局静态变量就是这样,虽然名字听起来像全局的,但实际上它只在自己所在的文件中起作用。这个特性在很多情况下都非常有用哦!

全局静态变量在多线程的使用示例:

        在多线程环境中使用全局静态变量时,需要特别注意线程安全性问题。以下是一个使用全局静态变量在多线程中的示例,同时展示了如何使用互斥锁来保护对全局静态变量的访问。

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>// 定义全局静态变量和互斥锁
static int globalStaticVar = 0;
pthread_mutex_t lock;// 线程函数
void* threadFunc(void* arg) {int threadId = *(int*)arg;free(arg); // 释放动态分配的内存// 加锁pthread_mutex_lock(&lock);// 访问和修改全局静态变量globalStaticVar++;printf("Thread %d incremented globalStaticVar to %d\n", threadId, globalStaticVar);// 解锁pthread_mutex_unlock(&lock);return NULL;
}int main() {const int numThreads = 10;pthread_t threads[numThreads];// 初始化互斥锁if (pthread_mutex_init(&lock, NULL) != 0) {fprintf(stderr, "Failed to initialize mutex\n");return EXIT_FAILURE;}// 创建线程for (int i = 0; i < numThreads; i++) {int* threadId = malloc(sizeof(int)); // 动态分配内存*threadId = i + 1;if (pthread_create(&threads[i], NULL, threadFunc, threadId) != 0) {fprintf(stderr, "Failed to create thread %d\n", i + 1);return EXIT_FAILURE;}}// 等待线程完成for (int i = 0; i < numThreads; i++) {pthread_join(threads[i], NULL);}// 销毁互斥锁pthread_mutex_destroy(&lock);printf("Final value of globalStaticVar: %d\n", globalStaticVar);return EXIT_SUCCESS;
}

代码解释

  1. 全局静态变量和互斥锁的定义‌:

    • static int globalStaticVar = 0;:定义了一个全局静态变量globalStaticVar,并初始化为0。
    • pthread_mutex_t lock;:定义了一个互斥锁lock,用于保护对globalStaticVar的访问。
  2. 线程函数‌:

    • void* threadFunc(void* arg):线程函数,接受一个void*类型的参数。
    • 在线程函数内部,首先通过pthread_mutex_lock(&lock);加锁,然后访问和修改全局静态变量globalStaticVar,最后通过pthread_mutex_unlock(&lock);解锁。
  3. 主函数‌:

    • 初始化互斥锁。
    • 创建多个线程,每个线程都执行threadFunc函数。
    • 等待所有线程完成。
    • 销毁互斥锁。
    • 打印全局静态变量globalStaticVar的最终值。

注意事项

  • 在多线程环境中使用全局静态变量时,务必使用互斥锁或其他同步机制来保护对变量的访问,以避免数据竞争和不确定的行为。
  • 动态分配的内存需要在适当的时候释放,以避免内存泄漏。在本例中,线程函数内部释放了动态分配的内存。

静态成员变量:

静态成员变量是编程语言中的一个重要概念,它通常与类(或结构体)相关联,并具有以下定义和特性:

定义

静态成员变量是在类(或结构体)内部使用static关键字声明的变量。它属于类本身,而不是类的某个特定对象。因此,静态成员变量在类的所有对象之间共享。

特性

  1. 共享性‌:

    • 静态成员变量在类的所有对象之间共享。这意味着,无论创建了多少个类的对象,它们都访问同一个静态成员变量。
  2. 存储位置‌:

    • 静态成员变量存储在静态存储区,而不是对象的实例存储区。因此,静态成员变量的生命周期贯穿整个程序运行期间。
  3. 访问方式‌:

    • 静态成员变量可以通过类名直接访问(使用类名::静态成员变量的语法),也可以通过类的对象访问(但这种方式并不常见,因为静态成员变量与特定对象无关)。
  4. 初始化‌:

    • 静态成员变量必须在类外部进行初始化。这是因为静态成员变量不属于任何对象,因此不能在构造函数中进行初始化
  5. 内存分配‌:

    • 静态成员变量在程序开始执行时就被分配内存,并在程序结束时才被销毁。这意味着静态成员变量在程序的整个生命周期内都存在
  6. 用途‌:

    • 静态成员变量常用于实现类的全局属性或计数器等功能。例如,可以使用静态成员变量来跟踪类的实例数量或存储与类相关的全局配置信息。

 示例代码(C++):

#include <iostream>class MyClass {
public:// 声明静态成员变量static int staticVar;// 静态成员函数(用于访问静态成员变量)static void printStaticVar() {std::cout << "Static variable value: " << staticVar << std::endl;}
};// 在类外部初始化静态成员变量
int MyClass::staticVar = 0;int main() {// 通过类名访问静态成员变量MyClass::staticVar = 42;MyClass::printStaticVar(); // 输出:Static variable value: 42// 创建类的对象MyClass obj1, obj2;// 通过对象访问静态成员变量(不推荐,但合法)obj1.staticVar = 100;obj2.printStaticVar(); // 输出:Static variable value: 100return 0;
}

        在上述示例中,MyClass类具有一个静态成员变量staticVar和一个静态成员函数printStaticVar我们在类外部初始化了staticVar,并在main函数中通过类名和对象两种方式访问了它。需要注意的是,尽管可以通过对象访问静态成员变量,但这种做法并不推荐,因为静态成员变量与特定对象无关。

静态成员函数:

定义

静态成员函数是在类(或结构体)内部使用static关键字声明的函数。与普通的成员函数不同,静态成员函数不依赖于类的特定对象,因此它们不能访问类的非静态成员变量或非静态成员函数(除非通过对象指针或引用传递)。

特性

  1. 与对象无关‌:

    • 静态成员函数不与类的任何特定对象相关联。因此,它们不能访问对象的非静态成员(除非显式地传递对象指针或引用)。
  2. 访问静态成员‌:

    • 静态成员函数可以访问类的静态成员变量和其他静态成员函数。这是因为静态成员属于类本身,而不是类的某个特定对象。
  3. 调用方式‌:

    • 静态成员函数可以通过类名直接调用(使用类名::静态成员函数名的语法),也可以通过类的对象调用(但这种方式并不常见,因为静态成员函数与特定对象无关)。
  4. 隐藏this指针‌:

    • 与普通成员函数不同,静态成员函数不隐式地接收this指针。这意味着在静态成员函数中不能使用this来访问对象的成员。
  5. 用途‌:

    • 静态成员函数常用于实现与类相关的功能,但不依赖于类的特定对象。例如,可以使用静态成员函数来创建类的实例、管理类的全局状态或提供与类相关的实用功能。

示例代码(C++):

#include <iostream>class MyClass {
public:// 声明静态成员变量static int staticVar;// 声明静态成员函数static void printStaticVar() {std::cout << "Static variable value: " << staticVar << std::endl;}// 非静态成员函数(用于对比)void printInstanceVar() {std::cout << "Instance variable value: " << instanceVar << std::endl;}private:// 非静态成员变量int instanceVar;
};// 在类外部初始化静态成员变量
int MyClass::staticVar = 42;int main() {// 通过类名调用静态成员函数MyClass::printStaticVar(); // 输出:Static variable value: 42// 创建类的对象MyClass obj;// 通过对象调用非静态成员函数(需要初始化instanceVar)obj.instanceVar = 100;obj.printInstanceVar(); // 输出:Instance variable value: 100// 注意:不能通过对象调用静态成员函数(尽管可以编译通过,但不推荐)// obj.printStaticVar(); // 这将正确编译并运行,但不符合静态成员函数的设计意图return 0;
}

         在上述示例中,MyClass类具有一个静态成员变量staticVar和一个静态成员函数printStaticVar。我们还定义了一个非静态成员函数printInstanceVar用于对比。在main函数中,我们通过类名调用了静态成员函数,并通过对象调用了非静态成员函数。需要注意的是,尽管可以通过对象调用静态成员函数(这在语法上是允许的),但这种做法并不推荐,因为静态成员函数与特定对象无关。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【已解决】黑马点评项目Redis版本替换过程的数据迁移

黑马点评项目Redis版本替换过程的数据迁移 【哭哭哭】附近商户中需要用到的GEO功能只在Redis 6.2以上版本生效 如果用的是老版本&#xff0c;美食/KTV的主页能正常返回&#xff0c;但无法显示内容 上次好不容易升到了5.0以上版本&#xff0c;现在又用不了了 Redis 6.2的windo…

本地部署deepseek模型步骤

文章目录 0.deepseek简介1.安装ollama软件2.配置合适的deepseek模型3.安装chatbox可视化 0.deepseek简介 DeepSeek 是一家专注于人工智能技术研发的公司&#xff0c;致力于打造高性能、低成本的 AI 模型&#xff0c;其目标是让 AI 技术更加普惠&#xff0c;让更多人能够用上强…

[论文总结] 深度学习在农业领域应用论文笔记14

当下&#xff0c;深度学习在农业领域的研究热度持续攀升&#xff0c;相关论文发表量呈现出迅猛增长的态势。但繁荣背后&#xff0c;质量却不尽人意。相当一部分论文内容空洞无物&#xff0c;缺乏能够落地转化的实际价值&#xff0c;“凑数” 的痕迹十分明显。在农业信息化领域的…

快速分析LabVIEW主要特征进行判断

在LabVIEW中&#xff0c;快速分析程序特征进行判断是提升开发效率和减少调试时间的重要技巧。本文将介绍如何高效地识别和分析程序的关键特征&#xff0c;从而帮助开发者在编写和优化程序时做出及时的判断&#xff0c;避免不必要的错误。 ​ 数据流和并行性分析 LabVIEW的图形…

展示统计信息收集情况

看看最近是否收集失败 SET LINES 200 PAGES 0 SET LONG 100000 longc 100000 COLUMN REPORT FORMAT A200VARIABLE stat_report CLOB; BEGIN:stat_report : DBMS_STATS.REPORT_STATS_OPERATIONS (since > SYSDATE-3 , until > SYSDATE , detail_lev…

STM32 TIM输入捕获 测量频率

输入捕获简介&#xff1a; IC&#xff08;Input Capture&#xff09;输入捕获 输入捕获模式下&#xff0c;当通道输入引脚出现指定电平跳变时&#xff0c;当前CNT的值将被锁存到CCR中&#xff0c;可用于测量PWM波形的频率、占空比、脉冲间隔、电平持续时间等参数 每个高级定时器…

如何将 Windows 上的文件传递到 Mac 上

文章目录 效果需求Windows 上设置共享磁盘【可选】新建一个带有密码的账户查看 Windows 的 IP 地址Mac 上链接 Windows 共享的磁盘 效果 需求 Windows 上有一个有密码的账户 Windows 上设置共享磁盘 windows 这边需要用 Administrator 权限的账号&#xff0c;把要共享的磁盘设…

NLP模型大对比:Transformer > RNN > n-gram

结论 Transformer 大于 RNN 大于 传统的n-gram n-gram VS Transformer 我们可以用一个 图书馆查询 的类比来解释它们的差异&#xff1a; 一、核心差异对比 维度n-gram 模型Transformer工作方式固定窗口的"近视观察员"全局关联的"侦探"依赖距离只能看前…

ODP(OBProxy)路由初探

OBProxy路由策略 Primary Zone 路由 官方声明默认情况&#xff0c;会将租户请求发送到租户的 primary zone 所在的机器上&#xff0c;通过 Primary Zone 路由可以尽量发往主副本&#xff0c;方便快速寻找 Leader 副本。另外&#xff0c;设置primary zone 也会在一定成都上减少…

Python NumPy(7):连接数组、分割数组、数组元素的添加与删除

1 连接数组 函数描述concatenate连接沿现有轴的数组序列stack沿着新的轴加入一系列数组。hstack水平堆叠序列中的数组&#xff08;列方向&#xff09;vstack竖直堆叠序列中的数组&#xff08;行方向&#xff09; 1.1 numpy.concatenate numpy.concatenate 函数用于沿指定轴连…

在线课堂小程序设计与实现(LW+源码+讲解)

专注于大学生项目实战开发,讲解,毕业答疑辅导&#xff0c;欢迎高校老师/同行前辈交流合作✌。 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;…

生成模型:扩散模型(DDPM, DDIM, 条件生成)

扩散模型的理论较为复杂&#xff0c;论文公式与开源代码都难以理解。现有的教程大多侧重推导公式。为此&#xff0c;本文通过精简代码&#xff08;约300行&#xff09;&#xff0c;从代码运行角度讲解扩散模型。 本文包括扩散模型的3项技术复现&#xff1a; 1.DDPM (Denoising…

DeepSeek大模型技术解析:从架构到应用的全面探索

一、引言 在人工智能领域&#xff0c;大模型的发展日新月异&#xff0c;其中DeepSeek大模型凭借其卓越的性能和广泛的应用场景&#xff0c;迅速成为业界的焦点。本文旨在深入剖析DeepSeek大模型的技术细节&#xff0c;从架构到应用进行全面探索&#xff0c;以期为读者提供一个…

[权限提升] 常见提权的环境介绍

关注这个框架的其他相关笔记&#xff1a;[内网安全] 内网渗透 - 学习手册-CSDN博客 通过前期的渗透测试&#xff0c;我们大概率会拿到目标的一个 Shell&#xff0c;比如 WebShell 或者 MSF Shell 等等&#xff0c;不同的 Shell 对应提权的姿势也不同&#xff0c;比如有的 Shell…

SQL注入漏洞之高阶手法 宽字节注入以及编码解释 以及堆叠注入原理说明

目录 宽字节注入 编码区分 原理 函数 转译符号解释 注意 绕过方式详解 堆叠【Stack】注入攻击 注入语句 宽字节注入 在说宽字节注入之前 我们需要知道编码相关的知识点&#xff0c;这个有助于搞定什么是宽字节注入 分清楚是ascii码是什么宽字节注入代码里面加入了adds…

Spring Boot - 数据库集成05 - 集成MongoDB

Spring Boot集成MongoDB 文章目录 Spring Boot集成MongoDB一&#xff1a;使用前的准备1&#xff1a;依赖导入 & 配置2&#xff1a;实体类创建 二&#xff1a;核心 - MongoRepository三&#xff1a;核心 - MongoTemplate1&#xff1a;集合操作2&#xff1a;文档操作(重点)3&…

用 Scoop 优雅管理 Windows 软件:安装、配置与使用全指南

本篇将主要讲讲如何用「Scoop」优雅管理 Windows 软件&#xff1a;安装、配置与使用全指南 一、Scoop 是什么&#xff1f; Scoop 是一款专为 Windows 设计的命令行软件包管理工具&#xff0c;它能让你像 Linux 系统一样通过命令快速安装、更新和卸载软件。其核心优势包括&…

基于SpringBoot的假期周边游平台的设计与实现(源码+SQL脚本+LW+部署讲解等)

专注于大学生项目实战开发,讲解,毕业答疑辅导&#xff0c;欢迎高校老师/同行前辈交流合作✌。 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;…

JavaScript - Web APIs(下)

日期对象 目标&#xff1a;掌握日期对象&#xff0c;可以让网页显示日期 日期对象&#xff1a;用来表示时间的对象 作用&#xff1a;可以得到当前系统时间 学习路径&#xff1a; 实例化 日期对象方法 时间戳 实例化 目标&#xff1a;能够实例化日期对象 在代码中发…

复古壁纸中棕色系和米色系哪个更受欢迎?

根据最新的搜索结果&#xff0c;我们可以看到棕色系和米色系在复古壁纸设计中都非常受欢迎。以下是对这两种颜色系受欢迎程度的分析&#xff1a; 棕色系 受欢迎程度&#xff1a;棕色系在复古壁纸中非常受欢迎&#xff0c;因为它能够营造出温暖、质朴和自然的氛围。棕色系的壁纸…