在 C/C++中 static 关键字详解

article/2025/9/12 17:03:14

static 关键字详解

    • C/C++ 中的 static
      • 1. 静态局部变量
      • 2. 静态全局变量
        • 全局变量 与 extren
      • 3. static 修饰函数
    • C++的 static 成员
        • 静态成员变量
          • const修饰的成员除外
        • 静态成员函数
    • 总结:

static是 C/C++中的关键字之一,是常见的函数与变量(C++中还包括类)的修饰符,它常被用来控制变量的存储方式和作用范围。 在众多高级语言中都有其作为关键字或函数出现,所以这也是应当被程序员熟知其各种含义的一个单词
在这里插入图片描述

我们知道在函数内部定义的变量,当程序执行到它的定义处时,编译器为它在栈上分配空间,函数在栈上分配的空间在此函数执行结束时会释放掉,这样就产生了一个问题: 如果想将函数中此变量的值保存至下一次调用时,如何实现? 最容易想到的方法是定义为全局的变量,但定义一个全局变量有许多缺点,最明显的缺点是破坏了此变量的访问范围(使得在此函数中定义的变量,不仅仅只受此函数控制)。static 关键字则可以很好的解决这个问题。
另外,在 C++ 中,需要一个数据对象为整个类而非某个对象服务,同时又力求不破坏类的封装性,即要求此成员隐藏在类的内部,对外不可见时,可将其定义为静态数据。

在这里插入图片描述

详见C/C++内存分布

如果static变量定义时未赋初值,编译时会自动将其赋值为0

C/C++ 中的 static

这里 static 作用主要影响着变量或函数的生命周期作用域,以及存储位置

1. 静态局部变量

定义在函数内部的变量称为局部变量(Local Variable),它的作用域仅限于函数内部, 离开该函数后就是无效的

当 static 修饰局部变量时:

 ● 变量的存储区域由变为静态常量区
 ● 变量的生命周期由局部变为全局
 ● 变量的作用域不变。

函数调用开辟栈帧,函数中的局部变量在栈上分配存储空间,当函数执行完毕,函数栈帧销毁,栈空间由系统回收
而在static修饰函数局部变量的时,其修饰的静态局部变量只执行初始化一次,延长了局部变量的生命周期,直到程序运行结束以后才释放,但不改变作用域。

下面用代码进行验证:

#include <stdio.h>void fun()
{static int val = 0;   //static 修饰局部变量val++;printf("%d\n", val);
}
int main()
{for (int i = 0; i < 7; i++){fun();}return 0;
}

没有 static 时, 函数每调用一次, 变量就会进行一次初始化值为 0,
当由 static修饰时, 初始化语句只会被执行一次所以值会一直累加。
在这里插入图片描述

2. 静态全局变量

在所有函数外部定义的变量称为全局变量(Global Variable),它的作用域默认是整个程序,也就是所有的源文件,包括 .c 和 .h 文件。

static 修饰全局变量时:

 ● 变量的存储区域在全局数据区的静态常量区
 ● 变量的作用域由整个程序变为当前文件(extern声明也不行)
 ● 变量的生命周期不变。

一个全局变量被 static 修饰,使全局变量只能在定义变量的当前文件使用,不能在其余文件使用,即使 extern外部声明也不行。

原因: 属于文件作用域的声明在缺省的情况下为 external 链接属性, 如定义个全局变量int g_a = 1;, a的链接属性为external,而加上 static会修改变量的缺省链接属性,改为internal
声明了全局变量 g_a 和 g_b (具有 external 链接属性 )的其他源文件在使用这两个变量时实际访问的是生命与此处的这两个变量;但是 g_c 只能由这个源文件访问,因为链接属性为internal

int g_a = 1;
extern int g_b;
static int g_c;

代码验证:

// add.c
static int global_val = 27;   //static 修饰全局变量//staticdemo1.c
extern global_val;int main()
{printf("%d", global_val);return 0;
}

不用 static 修饰 global_val 时的结果
在这里插入图片描述
而使用 static 修饰时,链接时就会出现链接错误无法执行。
在这里插入图片描述

全局变量 与 extren

 具有 extrenal 链接属性的实体在其他语言术语中称作全局实体(global entity ),所有源文件中的函数均可以访问它。只要变量并非声明与代码块或者函数定义内部,它在缺省的情况下链接属性即为 extrenal。如果一个变量声明在代码块内部,在它面前添加 extren 关键字将使它使它所引用的是全局变量而非局部变量。
  具有链接属性为 extrenal 的实体总是具有静态存储类型。 全局变量在程序开始执行前创建,并在整个执行过程中始终存在。从属于函数的局部变量在函数在函数开始执行时进行创建,在函数执行完毕后销毁,但用于执行函数的机器指令在程序生命周期内一直存在。

使用 extren 进行声明提高代码的可读性是良好的编程习惯。

3. static 修饰函数

函数的作用域与全局变量一样都是整个程序。

当 static 修饰函数时:

 ● 函数的作用域由整个程序变为当前文件(extern声明也不行)

一个函数被 static 修饰,使函数只能在定义的源文件使用,不能在其余文件使用,即使 extern外部声明也不行。(同static 修饰全局变量)

如果我们将函数声明为 static,就会把它的链接属性从external,改为internal,这样将使得其他源文件不能访问这个函数;对于函数而言,存储类型不是问题,因为代码总是存储在只读的代码区中。

// add.c
static int add(int a, int b)
{return a + b;
}//staticdemo1.c
extern add(int a, int b);int main()
{printf("%d", add(10, 20));return 0;
}

这里直接看结果:

没有 static 修饰:在这里插入图片描述

static 修饰:报了与修饰全局变量时同样的链接错误。
在这里插入图片描述




C++的 static 成员

 声明为static类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用static修饰的 成员函数,称之为静态成员函数

注:静态成员无法在构造函数初始化列表初始化!!

在这里插入图片描述

注:静态的成员变量一定要在类外进行初始化!!

const修饰的成员除外
在这里插入图片描述
在这里插入图片描述

class A
{
public :A(){   //构造函数_count++;}A(const A& y){_count++;}static int GetCount(){    //静态成员函数return _count;}
public:      int a;//静态成员变量--》在类中的是声明要在类外进行定义static int _count;  static const int sa = 99;const static int sb;static const int sc;
};int A::_count = 0;   //静态变量 _count 定义
const int A::sb = 88;
const int A::sc = 77;void TestA() {cout << A::GetCount() << endl;  // 类静态成员通过 类名::静态成员 来访问A a1, a2; A a3(a1);cout << A::GetCount() << endl;
}

静态成员变量

  1. 静态成员变量必须在类外进行定义定义时不用加 static ,类中只是声明
    在这里插入图片描述
const修饰的成员除外

在这里插入图片描述

  1. 静态成员变量为所有类对象所共享,并没有包含在具体的对象中。
    所以并不影响 sizeof() 大小

  2. 静态成员变量的访问: 类名::静态成员变量名 或 对象.静态成员变量名。

cout << A::_count << endl;
cout << a1._count << endl;

在这里插入图片描述
类的对象可以使用静态成员函数和非静态成员函数。

注:静态成员变量也受访问限定符(public、protected、private)的限制。 所以私有的仍要通过类成员函数接口来进行访问,可以在通过类中公有的成员函数进行访问,

cout << A::GetCount() << endl;

但这种方式调用获取静态成员变量必须由静态成员函数访问,不能通过类名来调用类的非静态成员函数,否则就会出错
在这里插入图片描述

类外初始化的值:
下面代码的输出结果是?
在这里插入图片描述

静态成员函数

  1. 静态成员函数没有隐藏的 this 指针,不能访问非静态成员(变量、 函数)!
    在这里插入图片描述
    因为静态成员函数没有隐藏的 this 指针所以也不能定义成const成员函数(const 本质就是修饰隐藏参数this )
    在这里插入图片描述

  2. 静态成员函数不能调用非静态成员函数。
    在这里插入图片描述

  3. 非静态成员函数可以调用静态成员函数。

static void fun(){_count = 0;}int GetCount(){//cout << this << endl;    fun();return _count;}



总结:

(1) 静态成员变量使用前必须先初始化(在类外定义),如:int A::_count = 0;
(2) 静态成员变量为所有类对象所共享,也受访问限定符(public、protected、private)的限制
(3) 静态成员函不能调用非静态成员函数,非静态成员函数可以调用静态成员函数
(4) 静态成员函数没有隐藏的 this 指针
所以静态成员函数可以访问类的静态成员、不能访问类的非静态成员(静态成员函数如何访问非静态成员)
在这里插入图片描述




文章内容随着对C++的深入学习还有待更加完善,欢迎大佬指正




参考:
书籍:《C和指针》
https://www.cnblogs.com/33debug/p/7223869.html
https://baike.baidu.com/item/%E9%9D%99%E6%80%81%E5%87%BD%E6%95%B0/5644260


http://chatgpt.dhexx.cn/article/wcgIH4DL.shtml

相关文章

Windows添加路由的方法

假设本来的局域网网关是192.100.10.1&#xff0c;现在要访问网关是192.100.20.0的服务器&#xff0c;可以手动添加路由。 方法&#xff1a; 1.以管理员的身份打开cmd命令窗口 2.输入 route add 192.100.20.0 mask 255.255.255.0 192.100.10.1 -p&#xff08;最后加上-p就是…

如何在 Linux 上添加路由?

在 Linux 系统中&#xff0c;路由是网络通信的关键组件之一。通过添加路由&#xff0c;您可以指定数据包在网络中的传输路径&#xff0c;从而实现网络连接和数据转发。本文将详细介绍如何在 Linux 上添加路由&#xff0c;以便您可以根据需要配置网络路由并实现灵活的网络连接。…

Linux上添加路由,删除路由,修改路由配置(route add, route del, 路由表项基本知识)

路由基础知识&#xff1a; 是由一项或者多项路由表组成的&#xff0c;每个IP报文被发送前&#xff0c;系统都会查找主机路由表&#xff0c;决定将这个报文从 哪个网卡&#xff0c;发送到哪个下一跳。路由表项可以分成主机路由&#xff0c;网络路由和默认路由3种。每条路由表项…

【网络】route和 IP route的区别|route 和 IP route 添加路由

目录 route和 IP route的区别 route 和 IP route 显示路由 route 和 IP route 添加路由 route 添加路由 IP route 添加路由 添加的路由持久化 linux下添加路由的方法 使用 route 命令 使用ip route 命令 route和 IP route的区别 route是一个相当简单的工具&#xff0c;非常适合…

addRoute动态添加路由

项目进行到最后&#xff0c;突然通知说要使用动态路由&#xff0c;在网上找了一堆方法&#xff0c;发现不是要使用vuex就是要搭配缓存&#xff0c;这个时候完全不知如何下手&#xff0c;该怎么做简单一点呢&#xff1f; 先介绍一下项目需求&#xff0c;根据用户登录后返回的唯…

CentOS7添加路由

目录 一、route命令介绍 1、语法 2、命令使用举例 默认&#xff08;网关&#xff09;路由 主机路由 网络路由 二、ip 命令 三、永久添加路由条目&#xff08;重启不会失效&#xff09; 一、route命令介绍 命令用来显示并设置Linux内核中的网络路由表&#xff0c;设置的…

添加路由表

添加永久路由&#xff08;网络重启后仍然有效&#xff09; windows系统&#xff1a; 1.cmd打开控制台 2.输入&#xff1a;route add -p目的ip地址mask 子网掩码网关地址 ubuntu系统&#xff1a; 1.任意位置新建terminal输入&#xff1a; 2.sudo chmod 777 -R /etc/rc.local回车…

添加路由的2种方式--router

第一种cmd方法: 重新创建一个项目 添加路由&#xff1a; 打开文件夹 在路径栏里面输入cmd 打开cmd命令行 输入 vue create router-pro 选择下面选项 按下键选择路由并回车&#xff1a; 选择vue2版本 问你是否使用路由的历史模式&#xff1a; 输入n 问你eslint的语法规范选择…

内外网同时使用之添加路由

鉴于工作需要&#xff0c;开发的项目需要在内网中进行测试。但是出现bug时又需要在互联网中查询bug来源&#xff0c;在整个过程中&#xff0c;需要切换内外网&#xff0c;断了外网切内网&#xff0c;断了内网切外网&#xff0c;麻烦的不是一批。所以向项目经理请教了一下&#…

RANSAC算法原理与实现

参考原文&#xff1a; RANSAC算法学习笔记 重点内容&#xff1a; 算法流程&#xff1a; 1、在可以有&#xff08;也可以没有&#xff0c;主要看应用场景&#xff09;条件限制&#xff08;比如选的子集里的点不能过远等&#xff09;的情况下&#xff0c;随机选取子集&#xff…

深度解析RANSAC算法(精华修正版)

RANSAC算法看似简单&#xff0c;实际上还是有很多坑的&#xff0c;网上有一些关于RANSAC算法的介绍不准确&#xff0c;或者说不全面。 之前我写过一个rnsac算法简介的博客&#xff0c;那么这篇博客将带你再次填这个大坑&#xff01; 目录 1. RANSAC算法论述 2. RANSAC算法…

利用RANSAC算法筛选SIFT特征匹配

关于RANSAC算法的基本思想&#xff0c;可从网上搜索找到&#xff0c;这里只是RANSAC用于SIFT特征匹配筛选时的一些说明。 RANSAC算法在SIFT特征筛选中的主要流程是&#xff1a; (1) 从样本集中随机抽选一个RANSAC样本&#xff0c;即4个匹配点对 (2) 根据这4个匹配点对计算变…

Ransac算法学习python版

初学小白,注释的代码比较详细 import numpy as np import scipy as sp import scipy.linalg as sldef ransac(data, model, n, k, t, d, debug False, return_all False):"""参考:http://scipy.github.io/old-wiki/pages/Cookbook/RANSAC伪代码:http://en.wi…

RANSAC算法实现 + 直线拟合

一、RANSAC算法 1.参考资料 [1]题目来源与解析&#xff1a;商汤科技SLAM算法岗的RANSAC编程题 [2]牛客网题目&#xff1a;[编程题]线性回归 [3]牛客网解答参考&#xff1a;商汤科技某算法岗的编程题有点过分了啊 [4]RANSAC算法原理&#xff1a;RANSAC翻译、经典RANSAC以及…

精匹配——Opencv实现RANSAC算法进行误匹配对剔除,并和最小二乘法对比

精匹配——RANSAC算法思想及优缺点 目录 精匹配——RANSAC算法思想及优缺点前言一、RANSAC简介二、RANSAC基本思想1.步骤2.迭代次数的公式3.举例&#xff08;拟合直线&#xff0c;拟合最佳单应性矩阵&#xff09; 三、最小二乘法1、最小二乘法的主要思想2、最小二乘解3、仿射变…

ransca算法详细介绍

1、算法概述&#xff1a; RANSAC算法的基本假设是样本中包含正确数据(inliers&#xff0c;可以被模型描述的数据)&#xff0c;也包含异常数据(outliers&#xff0c;偏离正常范围很远、无法适应数学模型的数据)&#xff0c;即数据集中含有噪声。这些异常数据可能是由于错误的测…

RANSAC 特征匹配算法解析

一、RANSAC特征匹配算法简介   RANSAC算法是RANdom SAmple Consensus的缩写&#xff0c;意为随机抽样一致。表面上的意思就是从匹配样本中随机取样&#xff0c;寻找一致的样本点。RANSAC算法是根据一组包含异常数据的样本数据集&#xff0c;计算出数据的数学模型参数&#x…

RANSAC算法(原理及代码实现+迭代次数参数自适应)

RANSAC算法 前言算法流程Python代码RANSAC算法迭代参数的自适应 前言 随机样本一致性 (RANSAC) 是一种迭代方法&#xff0c;用于从一组包含异常值的观察数据中估计数学模型的参数&#xff0c;此时异常值不会对估计值产生影响。简言之&#xff0c;RANSAC是一种滤除异常值的常用算…

RANSAC算法简介

文章目录 1 算法简介2 基本思想3 参数4 应用案例&#xff08;直线拟合&#xff09; 1 算法简介 RANSAC算法的基本假设是样本中包含正确数据(inliers&#xff0c;可以被模型描述的数据)&#xff0c;也包含异常数据(outlies&#xff0c;偏离正常范围很远、无法适应数学模型的数据…