当前位置: 首页 > news >正文

[C++——lesson11.static关键字]

目录

前言

一、🧐static关键字是什么?

二、🧐static关键字修饰的对象是什么? 

三、📖C 语言中的 static

3.1 static的C用法

3.2static的重点概念

3.3static修饰局部变量

3.4static在修饰局部变量和函数的作用

3.5static修饰全局变量和函数

3.6static在修饰全局变量和函数的作用

四、📖C++中的 static

4.1static的C++用法

4.2static在C++中的重点概念

静态成员为所有类对象所共享,不属于某个具体的实例

静态成员变量必须在类外定义,定义时不添加static关键字

静态成员函数没有隐藏的this指针,不能访问任何非静态成员

访问静态成员变量的特殊方式

静态成员和类的普通成员一样,也有public、protected、private3种访问级别,也可以具有返回值

五、static面试题

static OJ面试题

总结

1. 全局静态变量(文件作用域)

2. 局部静态变量(函数作用域)

3. 静态成员变量(类作用域)

4. 静态成员函数(类作用域)

5. 静态类(C++11 起,仅适用于嵌套类)

6. 静态断言(C++11 起,static_assert)

常见注意事项

示例代码

结束语


前言

  static,中文意思是静态的,作为C/C++中常用关键字中的一个很重要的关键字,其中用法多样且复杂难以理解,本文将会循序渐进,先从C语言的static讲起,慢慢延申到C++,从易到难,每一步都会举一些通俗易懂的例子帮助大家理解。

一、🧐static关键字是什么?

在 C/C++ 中,static关键字主要用于修饰变量和函数还可用于修饰 C++ 中的类成员它常被用来控制变量的存储方式和作用范围

具体如下:

  • 修饰局部变量
    • 存储位置改变:普通局部变量存储在栈区,进入作用域创建,出作用域释放。而被static修饰的局部变量存储在静态区,直到程序结束才释放。
    • 生命周期延长:静态局部变量只在函数第一次调用时初始化,函数调用结束后其值不会消失,会保留到下一次调用,具有 “记忆” 功能。
    • 作用域不变:静态局部变量的作用域仍局限于定义它的函数内部,只是生命周期变长,可用于记录函数调用次数等场景。
  • 修饰全局变量
    • 作用域缩小:全局变量默认具有外部链接属性,作用域是整个程序,可被其他文件通过extern声明后访问。但被static修饰后,变为内部链接属性,作用域仅限于定义它的文件,其他文件无法访问,可避免命名冲突。
    • 存储位置与初始化:静态全局变量存储在静态数据区,未赋值时默认初始化为 0。
  • 修饰函数
    • 限制函数可见范围:函数被static修饰后,成为静态函数,其作用域仅限于定义它的文件,不能被其他文件引用,可用于隐藏函数实现细节,使代码更具封装性。
  • 修饰 C++ 类成员
    • 静态成员变量:为类的所有对象所共享,不属于某个具体对象。必须在类外定义,定义时不加static,可通过 “类名::静态成员变量名” 或 “对象。静态成员变量名” 访问,常被用于表示类的共享状态或全局属性。
    • 静态成员函数:没有隐藏的this指针,不能访问非静态成员,可由类名直接调用,常用于提供与类相关的通用功能,而不依赖于特定对象的状态。

接下来我们将一一进行详解

二、🧐static关键字修饰的对象是什么? 

1.局部变量

2.全局变量

3.函数

4.修饰 C++ 类成员

三、📖C 语言中的 static

3.1 static的C用法

1️⃣: 修饰局部变量(称为静态局部变量)

2️⃣: 修饰全局变量(称为静态全局变量)

3️⃣: 修饰函数(称为静态函数)

3.2static的重点概念

1️⃣:在函数中声明变量时, static 关键字指定变量只初始化一次,并在之后调用该函数时保留其状态。
2️⃣:在声明变量时,变量具有静态持续时间,并且除非您指定另一个值。
3️⃣ :在全局和/或命名空间范围 (在单个文件范围内声明变量或函数时) static 关键字指定变量或函数为内部链接,即外部文件无法引用该变量或函数。
4️⃣:static 关键字 没有赋值时,默认赋值为 0 

5️⃣:static修饰局部变量时,会改变局部变量的存储位置,从而使得局部变量的生命周期变长。

⭐:接下来,将重点讲解上面三个作用个五点概念的理解,和应用

3.3static修饰局部变量

1️⃣:在函数中声明变量时, static 关键字指定变量只初始化一次,并在之后调用该函数时保留其状态。
5️⃣:static修饰局部变量时,会改变局部变量的存储位置,从而使得局部变量的生命周期变长。

接下来用们用一段代码来进行解析:

#include <stdio.h>
#include <stdlib.h>
void test()
{int x = 0;x++;printf("%d ", x);
}
int main()
{int i = 0;printf("%d\n", i);while (i < 10){test();i++;}return 0;
}

    这段代码中每次调用test()函数时创建局部变量x赋值为 0,每次局部变量x出了test()函数后都会自行销毁。
        所以可以很容易得到输出结果为:1 1 1 1 1 1 1 1 1 1


 接着我们用上static关键字来修饰  test()函数中的局部变量 x

#include <stdio.h>
#include <stdlib.h>
void test()
{static int x = 0;x++;printf("%d ", x);
}
int main()
{int i = 0;while (i < 10){test();i++;}return 0;
}

  输出结果变成了:1 2 3 4 5 6 7 8 9 10
        原因是static修饰了局部变量x,令局部变量x变成静态的,且只能初始化一次,使得每次test()函数结束时局部变量x都不销毁,再次进入test()函数时则保留原有数值运行,因此x++数值越来越大。

⭐总结:

(1)static关键字修饰局部变量不改变作用域,但是生命周期变长。

(2)本质上,static关键字修饰局部变量,改变了局部变量的存储位置,因为存储位置的差异,使得执行效果不一样。普通的局部变量放在栈区,这种局部变量进入作用域创建,出作用域释放。局部变量被static修饰后成为静态局部变量,这种变量放在静态区,创建好后,直到程序结束后才释放。

4️⃣:static 关键字 没有赋值时,默认赋值为 0 

接下来用们用一段代码来进行解析:

int a;
int main()
{char str[10];printf("integer: %d; string: (begin)%s(end)\n", a, str);return 0;
}

         在这段代码中,我们并没有对全局变量 a 和字符串数组 str 进行赋值,所以在输出时会出现随机值的现象。所以很容易得到如下的结果:

integer: 0; string: (begin)烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫攼l(end)

⚠ 注意:a 输出为 0 是因为 此时 a 是全局变量,也存放在静态区,所以可以默认值为 0 .


  •  接着我们用上 static关键字 来修饰 全局变量 a 和字符串数组 str 
static int a;
int main()
{static char str[10];printf("integer: %d; string: (begin)%s(end)\n", a, str);return 0;
}

 输出:

integer: 0; string: (begin)(end)

⭐总结:

         static的另一个作用是默认初始化为0。其实全局变量也具备这一属性,因为全局变量也存储在静态数据区。在静态数据区,内存中所有的字节默认值都是0x00,某些时候这一特点可以减少程序员的工作量。比如初始化一个稀疏矩阵,我们可以一个一个地把所有元素都置0,然后把不是0的几个元素赋值。如果定义成静态的,就省去了一开始置0的操作。再比如要把一个字符数组当字符串来用,但又觉得每次在字符数组末尾加‘\0’;太麻烦。如果把字符串定义成静态的,就省去了这个麻烦,因为那里本来就是 ‘\0’。

3.4static在修饰局部变量和函数的作用

作用:

        保持变量内容的持久

  • static的第一个作用是保持变量内容的持久,即static变量中的记忆功能和全局生存期。
  • 存储在静态数据区的变量会在程序刚开始运行时就完成初始化,也是唯一的一次初始化。之后再次运行到含有 static 关键字的初始化语句时不会再执行该语句。共有两种变量存储在静态存储区:全局变量和 static 变量,只不过和全局变量比起来,static 可以控制变量的可见范围。

3.5static修饰全局变量和函数

3️⃣ :在全局和/或命名空间范围 (在单个文件范围内声明变量或函数时) static 关键字指定变量或函数为内部链接,即外部文件无法引用该变量或函数。

        针对上面这个概念的理解我们一次来解析以下:
1. 首先说一下全局变量,全局变量的作用域十分的广,只要在一个源文件中定义后,这个程序中的所有源文件、对象以及函数都可以调用,生命周期更是贯穿整个程序。文件中的全局变量想要被另一个文件使用时就需要进行外部声明(以下用extern关键字进行声明)。-----也就是说全局变量既可以在源文件中使用,也可以在其他文件中使用(只需要使用extern外部链接以下即可)

2. static修饰全局变量和函数,会改变全局变量和函数的链接属性-------变为只能在内部链接,从而使得全局变量的作用域变小。
 

接着我们用代码进行解析:

  • 首先,在Hello.c文件中定义一个全局变量 char a 和函数 PrintfHello(),之后在test.c文件中进行extern 进行外部链接,运行代码:

  • 之后我们进入正题 在全局变量和函数之前用 static进行修饰看看效果:

         会发现生成错误,无法解析外部符号a和PrintfHello()等,全局变量a和PrintfHello()不能被test.c文件调用了。所以我们可以很容易看出static修饰后让全局变量a和PrintfHello()的作用域变小了,令全局变量a和PrintfHello()无法被其他文件调用。

⭐总结:

1.全局变量和函数本身是具有外部链接属性的,在Hello.c文件中定义的全局变量和函数,在test.c文件中可以通过【链接】来使用;

2. 但如果全局变量被static修饰,那这个外部链接属性就会被修改成内部链接属性此时这个全局变量就只能在自己的源文件中使用;

3.6static在修饰全局变量和函数的作用

⭐作用:
        如果加了 static,就会对其它源文件隐藏。例如在 a 和 printHello 的定义前加上 static,main.c 就看不到它们了。利用这一特性可以在不同的文件中定义同名函数和同名变量,而不必担心命名冲突。static 可以修饰函数和变量,将其对其他源文件隐藏起来,从而避免命名冲突。对于函数来讲,static 的作用仅限于该隐藏功能。

四、📖C++中的 static

        本小节主要介绍在 C++中引入了面向对象的特性(类)之后,static关键字的一些用途。当然 C++ 是兼容 C 语言的,所以C语言中的 static 在C++中也是成立的。

4.1static的C++用法

 声明为static的类成员称为类的静态成员,分为如下两类:

  • 用static修饰的成员变量,称之为静态成员变量
  • 用static修饰的成员函数,称之为静态成员函数

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

4.2static在C++中的重点概念

1️⃣:静态成员为所有类对象所共享,不属于某个具体的实例
2️⃣:静态成员变量必须在类外定义,定义时不添加static关键字
3️⃣:静态成员函数没有隐藏的this指针,不能访问任何非静态成员
4️⃣:访问静态成员变量的特殊方式

5️⃣:静态成员和类的普通成员一样,也有public、protected、private3种访问级别,也可以具有返回值

⭐:接下来,将重点讲解上面三个作用个五点概念的理解,和应用

  • 静态成员为所有类对象所共享,不属于某个具体的实例

请看如下代码:

class A
{
private:static int _n;int _k;char _a;
};
int main()
{cout << sizeof(A) << endl; //8return 0;
}

         这里的运行结果为8,这里的计算规则是按照C语言那套计算结构体大小的规则。并没有把我静态成员变量_n考虑进去,因为静态成员变量属于整个类,是类的所以对象,所以静态变量成员不计入总大小。

  • 静态成员变量必须在类外定义,定义时不添加static关键字

        静态成员变量属于整个类,而不是类的某个特定实例,它在所有类实例之间是唯一的。因此,需要在类外为其分配存储空间并进行初始化。定义时使用类名加作用域解析运算符 “::” 来指明该变量属于哪个类,无需再次使用 static 关键字。

示例:

#include <iostream>class MyClass {
public:// 声明静态成员变量(类内声明)static int staticVar;static const int constStaticVar; // 常量静态成员变量// 成员函数可以访问静态成员变量void printStaticVar() {std::cout << "静态成员变量的值: " << staticVar << std::endl;}
};// 静态成员变量的类外定义(不添加static关键字)
int MyClass::staticVar = 0;// 常量静态成员变量的类外定义
const int MyClass::constStaticVar = 100;int main() {// 通过类名直接访问静态成员变量MyClass::staticVar = 5;std::cout << "通过类名访问: " << MyClass::staticVar << std::endl;// 通过对象访问静态成员变量MyClass obj1, obj2;obj1.staticVar = 10;std::cout << "通过obj2访问: " << obj2.staticVar << std::endl;// 调用成员函数访问静态成员变量obj1.printStaticVar();// 访问常量静态成员变量std::cout << "常量静态成员变量: " << MyClass::constStaticVar << std::endl;return 0;
}
  1. 在类内,我们声明了静态成员变量staticVar和常量静态成员变量constStaticVar
  2. 在类外,我们对这两个静态成员变量进行了定义和初始化,注意没有使用static关键字
  3. 静态成员变量可以通过类名直接访问(MyClass::staticVar),也可以通过对象访问(obj1.staticVar
  4. 所有对象共享同一个静态成员变量,修改一个对象的静态成员变量会影响所有对象

不过,C++17 支持在类内直接初始化静态成员,但需要使用 inline 关键字

class MyClass {
public:// C++17特性:inline静态成员变量可以在类内直接初始化inline static int staticVar = 0;
};

这种情况下就不需要在类外单独定义了

  • 静态成员函数没有隐藏的this指针,不能访问任何非静态成员
  • 原理
    普通成员函数在编译时,编译器会隐式地增加一个形参 this,并将当前对象的地址赋值给 this,所以普通成员函数能访问调用它的对象的成员。而静态成员函数属于整个类,不与任何对象实例关联,编译器不会为其隐式传递 this 指针。由于非静态成员是属于类的具体对象的,没有 this 指针就无法确定要访问哪个对象的非静态成员,因此静态成员函数不能访问非静态成员变量和非静态成员函数。
  • 代码示例
#include <iostream>
class MyClass {
private:int nonStaticVar; // 非静态成员变量
public:static void staticFunc() {// 以下代码会编译错误,因为没有this指针,无法访问nonStaticVar// std::cout << nonStaticVar << std::endl; }void nonStaticFunc() {nonStaticVar = 0; // 非静态成员函数可以访问非静态成员变量}
};
  • 调用方式:静态成员函数通常通过类名加作用域运算符 “::” 来调用,也可以通过对象调用,但它并不依赖于对象状态。例如MyClass::staticFunc()myObj.staticFunc()myObjMyClass的对象),这也体现了它与没有 this 指针、不依赖特定对象的特性。
  • 访问静态成员变量的特殊方式

当静态成员变量为公有时,可有如下三种进行访问:

  1. 通过对象.静态成员来访问
  2. 通过类名::静态成员来行访问
  3. 通过匿名对象突破类域进行访问
class A
{
public:
// 声明static int _k;
};
// 定义
int A::_k = 0;
int main()
{A a;cout << a._k << endl;  //通过对象.静态成员来访问cout << A::_k << endl; //通过类名::静态成员来行访问cout << A()._k << endl;//通过匿名对象突破类域进行访问return 0;
}

当静态成员变量变成私有时,可采用如下方式:

  1. 通过对象.静态成员函数来访问
  2. 通过类名::静态成员函数来行访问
  3. 通过匿名对象调用成员函数进行访问
class A
{
public:static int GetK(){return _k;}
private:static int _k;
};
int A::_k = 0;
int main()
{A a;cout << a.GetK() << endl; //通过对象.静态成员函数来访问cout << A::GetK() << endl;//通过类名::静态成员函数来行访问cout << A().GetK << endl; //通过匿名对象调用成员函数进行访问return 0;
}
  • 静态成员和类的普通成员一样,也有public、protected、private3种访问级别,也可以具有返回值
  • 1、静态成员函数可以调用非静态成员函数吗?

答案:不可以,因为静态成员函数是没有this指针的,无法调用非静态成员函数。
 

  • 2、非静态成员函数可以调用类的静态成员函数吗?

答案:可以,因为静态成员为所有类对象所共享,不受访问限制

五、static面试题

搞清楚了static的特性,来看几道道面试题:

面试题1:

实现一个类,计算中程序中创建出了多少个类对象。

思路:
假设命名该类为A,那么A类型的对象一定是经过构造函数或拷贝构造的,那么我们就可以分别定义两个静态成员变量,在构造函数和拷贝构造里++变量,这样,每创建一次对象,变量就++一次,自然就好求了。如下:
 

class A
{
public:A(){++_count1;}A(const A& aa){++_count2;}static int GetCount1(){return _count1;}static int GetCount2(){return _count2;}
private:static int _count1; static int _count2;
};
int A::_count1 = 0;
int A::_count2 = 0;
A Func(A a)
{A copy(a);return copy;
}
int main()
{A a1;A a2 = Func(a1);cout << a1.GetCount1() << endl; // 1cout << a2.GetCount2() << endl; // 3cout << A::GetCount1() + A::GetCount2() << endl; // 4
}
  • 分析:

        A a1 调用了一次构造函数;a2 = Func(a1),调用了一次拷贝构造;A copy(a),调用了一次拷贝构造;return copy 返回的时候,copy会销毁,所以提前需要进行拷贝构造进行拷贝保存 。所用总共四次。

static OJ面试题

题目链接:

JZ64 求1+2+3+...+n

思路:
这里我可以自己单独定义一个Sum类,专门进行求和,我定义n个对象,它就会调用n次构造函数,此时就可以在构造函数内实现累加,为了实现累加,需要在Sum类里设定两个静态成员变量,因为静态成员属于整个类,以此确保每次访问的变量都是同一个,最后,返回累加的值即可。

注意:
如若不支持变长数组,我们只能用new来完成,在获取返回的累加值时,可以单独在类内写个函数返回私有成员变量,该函数可以是静态成员函数,这样就可以指定类域去调用,不需要借助对象了。也可以借助友元。

代码:
 

#include<iostream>
using namespace std;
class Sum
{
public:Sum() //构造函数内实现累加{_ret += _i;_i++;}int GetRet()  //static int GetRet() 也可以是静态成员函数{return _ret;  //返回获取的求和值}
private://静态成员变量类内声明static int _i;static int _ret;
};
//静态成员变量类外定义
int Sum::_i = 1;
int Sum::_ret = 0;
class Solution {
public:int Sum_Solution(int n) {Sum a[n]; //支持变长数组可以这样写return a[1].GetRet(); //注意通过对象去调用成员函数//return Sum::GetRet();静态成员函数支持用类域访问/* 如若不支持变长数组,就用new来开辟n个空间Sum* ptr = new Sum[n];return ptr->GetRet(); */}
};

总结

1. 全局静态变量(文件作用域)

  • 定义:在全局变量前加 static,如 
  • static int g_var;
  • 特性
    • 仅在当前文件可见(文件作用域),其他文件无法通过 extern 访问
    • 存储在全局数据区,程序生命周期内存在
    • 未初始化时默认值为 0
  • 用途:限制全局变量的可见范围,避免不同文件中同名变量的冲突

2. 局部静态变量(函数作用域)

  • 定义:在函数内部定义的 static 变量,如 
  • void func() { static int count = 0; }
  • 特性:
    • 仅在函数内可见,但生命周期与程序一致(首次调用时初始化,后续调用不再重新初始化)
    • 存储在全局数据区,而非栈区
    • 多线程环境下需注意线程安全
  • 用途:保存函数调用间的状态(如计数器、单例模式)

3. 静态成员变量(类作用域)

  • 定义:在类内声明,类外定义的 static 变量
  • 特性:
    • 属于整个类,而非某个对象,所有对象共享同一实例
    • 必须在类外定义(C++17 前),定义时不加 static 关键字:
      int MyClass::var = 0;
    • C++17 起可使用 inline 在类内直接初始化:
      inline static int var = 0;
    • 可通过类名直接访问(MyClass::var)或对象访问(obj.var
  • 用途:存储类级别的共享数据(如对象计数器、全局配置)

4. 静态成员函数(类作用域)

  • 定义类内声明时加 static 的函数,如 
    class MyClass { static void func(); };
  • 特性
    • 无隐藏的 this 指针,不能访问非静态成员(变量 / 函数)
    • 可访问静态成员变量和其他静态成员函数
    • 通过类名直接调用(MyClass::func())或对象调用(obj.func()
    • 不能被 virtual 修饰(无多态特性)
  • 用途:提供类级别的工具函数,与类关联但不依赖对象状态

5. 静态类(C++11 起,仅适用于嵌套类)

  • 定义在类内部定义的 static 类,如:
class Outer {
public:static class Inner {public:void func() {}};
};
  • 特性:嵌套的静态类无需依赖外部类对象即可使用
  • 用途:逻辑上与外部类关联但无需访问外部类成员的辅助类

6. 静态断言(C++11 起,static_assert

  • 定义编译期断言,如 
    static_assert(sizeof(int) == 4, "int 必须为 4 字节");
  • 特性
    • 在编译阶段检查条件,失败则编译报错
    • 第一个参数为编译期常量表达式,第二个参数为错误信息字符串
  • 用途:验证编译环境、类型大小等编译期条件

常见注意事项

  1. 存储区域:所有 static 变量(全局 / 局部 / 成员)均存储在全局数据区,程序结束时释放
  2. 初始化顺序不同编译单元中的全局静态变量初始化顺序不确定,可能导致 "静态初始化顺序问题"
  3. 访问权限:静态成员变量仍受类的访问控制(public/private/protected)限制
  4. 继承关系:派生类可访问基类的 public/protected 静态成员,且不影响基类的静态成员值

示例代码

#include <iostream>// 1. 全局静态变量(仅当前文件可见)
static int global_static = 10;class MyClass {
public:// 3. 静态成员变量(类内声明)static int static_var;int non_static_var = 20;// 4. 静态成员函数static void static_func() {std::cout << "static_var: " << static_var << std::endl;// 错误:不能访问非静态成员// std::cout << non_static_var << std::endl;}// 非静态成员函数可访问静态成员void non_static_func() {static_var++; // 访问静态成员}
};// 3. 静态成员变量(类外定义)
int MyClass::static_var = 0;void count_calls() {// 2. 局部静态变量(仅初始化一次)static int calls = 0;calls++;std::cout << "调用次数: " << calls << std::endl;
}int main() {// 访问全局静态变量std::cout << "全局静态变量: " << global_static << std::endl;// 访问局部静态变量(通过函数)count_calls(); // 调用次数: 1count_calls(); // 调用次数: 2// 访问类的静态成员MyClass::static_var = 5;MyClass::static_func(); // static_var: 5MyClass obj;obj.non_static_func();MyClass::static_func(); // static_var: 6return 0;
}

通过上述总结可以看出,static 关键字的核心是控制存储周期和作用域,合理使用可提高代码的封装性和安全性。

结束语

以上就是我对C/C++ static关键字的理解

感谢你的三连支持!!!

http://www.dtcms.com/a/403497.html

相关文章:

  • 小说网站开发流程具体app开发公司 上海
  • 【阿里DeepResearch】写作组件WebWeaver详解
  • 汽车面向服务架构(SOA)网络安全对策
  • 视频网站用php做做视频网站需要什么职位工作
  • Git 无法访问 GitHub(Recv failure: Connection was reset)问题解决教程
  • 佛山网站设计是学校网站建设项目需求报告
  • (8)ASP.NET Core2.2 中的MVC路由一
  • pytest中的assert断言
  • C++开源库使用:nlohmann/json
  • Appium笔记
  • 找人做彩票网站多少钱专业网站优化公司报价
  • 天津平台网站建设公司东莞市南城区
  • 【开题答辩全过程】以 Java医院就医平台系统为例,包含答辩的问题和答案
  • 11.路由器的接口及其相关知识(2025年9月25日)
  • C#调用基于python打包的税务申报期限解析器exe工具
  • 沧州市东光建设局 网站技术教程优化搜索引擎整站
  • Java线程安全:synchronized锁机制详解
  • 浅谈Linux内核的LRU算法作用和原理
  • 自己做网站 最好的软件下载win10一键优化
  • Vue3 + Ant Design Vue 实现表格无限滚动加载(自定义指令版)
  • Golang语言基础篇006_Golang流程控制
  • 襄樊网站网站建设网站建设中管理员登录的代码怎么写
  • 打工人日报#20250924
  • 网站的构思重庆建设工程造价信息
  • 【lua】luajit 命令行使用指南
  • 网站配置系统搭建一个网站需要多久
  • 绿联UGOS Pro九月更新,三端优化,影音相册再升级
  • JAVA学习-预科部分(路线、博客、预备基础)
  • 使用IDEA创建项目
  • Transformer - Multi-Head Attention(多头注意力)