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

目录
  • C/C++ 中的 static
    • 1. 静态局部变量
    • 2. 静态全局变量
    • 3. static 修饰函数
  • C++的 static 成员
    • 静态成员变量
    • 静态成员函数
  • 总结:

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

我们知道在函数内部定义的变量,当程序执行到它的定义处时,编译器为它在栈上分配空间,函数在栈上分配的空间在此函数执行结束时会释放掉,这样就产生了一个问题: 如果想将函数中此变量的值保存至下一次调用时,如何实现? 最容易想到的方法是定义为全局的变量,但定义一个全局变量有许多缺点,最明显的缺点是破坏了此变量的访问范围(使得在此函数中定义的变量,不仅仅只受此函数控制)。static 关键字则可以很好的解决这个问题。

另外,在 C++ 中,需要一个数据对象为整个类而非某个对象服务,同时又力求不破坏类的封装性,即要求此成员隐藏在类的内部,对外不可见时,可将其定义为静态数据。

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修饰的 成员函数,称之为静态成员函数

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

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

静态成员变量

1.静态成员变量必须在类外进行定义定义时不用加 static ,类中只是声明

2.静态成员变量为所有类对象所共享,并没有包含在具体的对象中。

所以并不影响 sizeof() 大小

3.静态成员变量的访问:

  • 类名::静态成员变量名
  • 对象.静态成员变量名。
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 指针

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注我们的更多内容!

(0)

相关推荐

  • 一文读懂c++之static关键字

    一.静态变量 与C语言一样,可以使用static说明自动变量.根据定义的位置不同,分为静态全局变量和静态局部变量. 全局变量是指在所有花括号之外声明的变量,其作用域范围是全局可见的,即在整个项目文件内都有效.使用static修饰的全局变量是静态全局变量,其作用域有所限制,仅在定义该变量的源文件内有效,项目中的其他源文件中不能使用它. 块内定义的变量是局部变量,从定义之处开始到本块结束处为止是局部变量的作用域.使用static修饰的局部变量是静态局部变量,即定义在块中的静态变量.静态局部变量具有局

  • C/C++的关键字之static你了解吗

    目录 C语言 隐藏 场景演示 解决方法 保持变量内容的持久 默认初始化为0 Cpp static类成员变量 static类成员方法 单例模式 总结 C语言 隐藏 场景演示 当我们同时编译多个文件时,所有未加static前缀的全局变量和函数都具有全局可见性.会导致符号表认为存在同名全局变量和函数发生碰撞. 场景:全局的变量/函数在.h中会在多个.cc文件中拥有且全局可见有链接问题. a.h #pragma once #include<stdio.h> void Test() { printf(&

  • 深入浅析c/c++ 中的static关键字

    static关键字 1,static 成员变量 static 成员变量不随着对象的创建而开辟内存空间.也就是说,不管从哪个对象去看static成员变量,都是一样的. 2, static 成员方法 static 成员方法里不可以调用非static 成员方法. 非static 成员方法里可以调用static 成员方法. 原因:非static 成员方法里是没有this指针的,所以在里面调用非static 成员方法时,无法传递this指针.static 成员方法不需要this指针. 重点:初始化stat

  • 详解c++中的 static 关键字及作用

    注:若没有特指是 静态成员时,默认都是普通成员: 1 类中的普通成员 类中的成员变量 和 成员函数 是分开存储的.其中, 1)每个对象都有独立的成员变量:成员变量可以存储在 栈空间.堆空间.全局数据区: 2)所有对象共享类的成员函数:成员函数 只能存储在 代码段: 2 类中的静态成员(static) 类中的静态成员 1.用 static关键字 修饰: 2.可以用 类名::成员名 访问 静态成员: 3.静态成员 属于 整个类: 4.静态成员 是所属类的成员,其它类不能访问: 5.静态成员的内存分配

  • C++面试基础之static关键字详解

    前言 static是 c++ 的关键字,顾名思义是表示静态的含义.它在 c++ 中既可以修饰变量也可以修饰函数.那当我们使用 static 时,编译器究竟做了哪些事情呢? 早先面试中被问到 static 关键字,感觉既熟悉又陌生.熟悉是都知道如何去使用它,陌生又来自不知道它究竟对我们程序做了什么.今天就来好好复习下这个关键字,本文的重点也在第三部分. 先看一下示例代码: test1.cpp #include <iostream> extern int a_int; extern void fu

  • C++语言基础 this和static关键字

    一.this关键字 this是一个指针,可用其访问成员变量或成员函数 下面是使用this的一个完整示例: #include <iostream> using namespace std; class Student{ public: void setname(char *name); void setage(int age); void setscore(float score); void show(); private: char *name; int age; float score;

  • C/C++中的static关键字详解

    目录 C/C++ 中的 static 1. 静态局部变量 2. 静态全局变量 3. static 修饰函数 C++的 static 成员 静态成员变量 静态成员函数 总结: static是 C/C++中的关键字之一,是常见的函数与变量(C++中还包括类)的修饰符,它常被用来控制变量的存储方式和作用范围. 在众多高级语言中都有其作为关键字或函数出现,所以这也是应当被程序员熟知其各种含义的一个单词 我们知道在函数内部定义的变量,当程序执行到它的定义处时,编译器为它在栈上分配空间,函数在栈上分配的空间

  • Java中的Static class详解及实例代码

     Java中的Static class详解 Java中的类可以是static吗?答案是可以.在Java中我们可以有静态实例变量.静态方法.静态块.类也可以是静态的. java允许我们在一个类里面定义静态类.比如内部类(nested class).把nested class封闭起来的类叫外部类.在java中,我们不能用static修饰顶级类(top level class).只有内部类可以为static. 静态内部类和非静态内部类之间到底有什么不同呢?下面是两者间主要的不同. (1)内部静态类不需

  • C++中的explicit关键字详解

    目录 前言 1. 抑制构造函数定义的隐式转换 2. 为转换显式地使用构造函数 3. 类型转换运算符可能产生意外结果 4. 显示的类型转换运算符 5. explicit练习 5.1 当不使用explict关键字时 5.2 使用explict关键字时 5.3 explicit 标识的构造函数中存在一个默认值 前言 最近在阅读android底层源码的时候,发现其中好多代码使用了explicit关键字,因此这里对explicit关键字进行了分析和介绍. 1. 抑制构造函数定义的隐式转换 在要求隐式转换的

  • JAVA面试题 static关键字详解

    问题 面试官Q1:请说说static关键字,你在项目中是怎么使用的? static 关键字可以用来修饰:属性.方法.内部类.代码块: static 修饰的资源属于类级别,是全体对象实例共享的资源: 使用 static 修饰的属性,静态属性是在类的加载期间初始化的,使用类名.属性访问 案例说明 ①修饰成员变量 package com.ant.param; public class StaticFieldDemo { public static void main(String[] args) {

  • C# 中的partial 关键字详解

    目录 引言 分部类 partial 分部限制 分部接口和结构 分部方法 this 和 partial 的区别 引言 partial 关键字用于拆分一个类.一个结构.一个接口或一个方法的定义到两个或更多的文件中. 每个源文件包含类型或方法定义的一部分,编译应用程序时将把所有部分组合起来.在设计 Framework 时,可以充分利用 partial 这个特性. 分部类 什么情况下需要拆分类定义呢? 处理大型项目时,使一个类分布于多个独立文件中可以让多位程序员同时对该类进行处理. 当使用自动生成的源文

  • Java中的final关键字详解及实例

    Java中的final关键字 1.修饰类的成员变量 这是final的主要用途之一,和C/C++的const,即该成员被修饰为常量,意味着不可修改. 上面的代码对age进行初始化后就不可再次赋值,否则编译时会报类似上图的错误. 如果修饰的是引用类型的变量,那么初始化后就不能让他指向另一个对象,如下图所示 2.修饰方法 用final关键字修饰的方法是不能被该类的子类override(重写),因此,如果在想明确禁止 该方法在子类中被覆盖的情况下才将方法设置为final的. 注:类的private方法会

  • C++ 中try finally关键字详解

    try-finally语句是Microsoft对C和C++语言的扩展,它能使32位的目标程序在异常出现时,有效保证一些资源能够被及时清除,这些资源的清除任务可以包括例如内存的释放,文件的关闭,文件句柄的释放等等.try-finally语句特别适合这样的情况下使用,例如一个例程(函数)中,有几个地方需要检测一个错误,并且在错误出现时,函数可能提前返回. #include <windows.h> #include <stdio.h> try-finally语句的语法与try-excep

  • PHP中的self关键字详解

    前言 PHP群里有人询问self关键字的用法,答案是比较明显的:静态成员函数内不能用this调用非成员函数,但可以用self调用静态成员函数/变量/常量:其他成员函数可以用self调用静态成员函数以及非静态成员函数.随着讨论的深入,发现self并没有那么简单.鉴于此,本文先对几个关键字做对比和区分,再总结self的用法. 与parent.static以及this的区别 要想将彻底搞懂self,要与parent.static以及this区分开.以下分别做对比. parent self与parent

  • C#中的yield关键字详解

    在"C#中,什么时候用yield return"中,我们了解到:使用yield return返回集合,不是一次性加载到内存中,而是客户端每调用一次就返回一个集合元素,是一种"按需供给".本篇来重温yield return的用法,探秘yield背后的故事并自定义一个能达到yield return相同效果的类,最后体验yield break的用法. 回顾yield return的用法 以下代码创建一个集合并遍历集合. class Program { static Ran

随机推荐