您的位置:首页 > 娱乐 > 八卦 > C/C++-static关键字

C/C++-static关键字

2024/12/26 2:49:17 来源:https://blog.csdn.net/2303_79972050/article/details/141105886  浏览:    关键词:C/C++-static关键字

static 关键字

static关键字:按编程语言划分

  1. 纯C:C语言中的static关键字用法
  2. C++:保留C语言的用法,新增了类和结构体的static用法.

两大基本概念说明

  1. 生命周期:变量实际在内存中存在的时间
  2. 作用域:变量可以访问的范围.

举例说明

char* get_name()
{char name[20];scanf("%s",name);return name;
]

name这个字符数组的生命周期是get_name函数调用开始到结束调用.
return name;这一语句返回了一个栈上的地址.函数栈帧销毁.这个变量的内存回收了.这会产生一个段错误.
name的作用域:正常只能get_name函数内部可以访问,除非传递指针或者引用的操作.—name作为一个局部变量.

C语言

1. 更改内存存储位置(static 修饰局部变量)

static int i = 0;注意这里的i为局部变量。
局部变量在栈上创建,static修饰局部变量会更改其在内存的存储位置。这时候的i会在静态区,生命周期:创建到程序运行结束,且这个代码语句只会执行一次,因为其只能在静态区存储一次,后面默认存在。
static修饰的局部变量类似全局变量,但不意味着它们完全相同

  • 对于变量,我们讨论三个部分,即作用域,生命周期,链接属性。
    static修饰的局部变量,只是更改了其生命周期,但其作用域还是限定在函数块内,或者函数内部的某一块作用域(循环内或者某个if语句内)。直接用i变量访问只能限定在某个该死的块内。

Q1:既然其生命周期已经更改,意味着它只要创建了,就必然一直存在。能否通过指针的方式实现访问呢?
当然可以,我们可以通过作用域更大的指针来存储改局部变量在静态区的地址。

#include<stdio.h>
static int* p;//定义一个全局作用域的static int的指针
void test()
{static int i = 0;p = &i;
}int main()
{test();printf("%d", *p);return 0;
}
  • 上面的static 修饰了指针变量,但指针变量也是变量,也分局部变量和全局变量。static 无论修饰全局指针变量还是局部指针变量,谨记,不要让它指向一个栈上的局部变量的内存。因为函数栈帧空间一旦销毁,局部变量销毁,栈帧空间重复利用会覆盖原来地址的数据,static指针会成为悬垂指针,访问未知内存,这是很危险的。因此,必须让其存储一个静态区段或者堆区的地址,指向静态区或者堆区。
#include<stdio.h>
static int* p;
void test()
{int i = 100;p = &i;
}
void test2()
{printf("test2():\n");
}
int main()
{test();test2();//利用test先前的栈帧空间,来达到覆盖数据的效果printf("%d", *p);return 0;
}

这里的p指向了一个垃圾值(无意义的值)。
如果static修饰局部指针变量,那么这个指针变量也存储在静态区。

2.更改链接属性(static修饰全局变量,函数)

1.什么是链接属性?

回顾:源文件到可执行代码。
预处理,编译,汇编,链接。
预处理:头文件内容拷贝/宏替换/去掉注释/条件编译
编译:检查语法错误,生成汇编代码区(指令集)
汇编:将汇编代码生成二进制机器码。
链接:把各个文件的二进制机器码链接在一起生成可执行程序。
先看第一个例子
static.c文件中声明一个静态全局变量.

//static.c
static int s_var = 666;
//Main.c
#include<stdio.h>
int main(void)
{//能打印成功吗?printf("%d",s_var);return 0;
}

失败了,在Main.c中添加 int s_var =999; 这一语句.
会出现命名冲突吗?
答案肯定不会.成功打印999.
这说明了什么?
static修饰的全局变量,只能作用在它自己的单个文件内.外部文件不会链接到它,对于其它文件好像隐藏了.----这是C中用static实现封装性的一个好方式

再看

//static.c
//去掉static关键字
int s_var = 666;
//Main.c
#include<stdio.h>
extern int s_var;//外部声明,提醒链接器在其它文件找这个变量.
int main(void)
{printf("%d",s_var);return 0;
}

静态函数部分

//main.c
#include<stdio.h>
#include"add.h"
int main()
{printf("%d", add(2,3));return 0;
}
//add.c
#include"add.h"
static int add(int x ,int y)
{return x + y;
}

static int add(int x, int y);//add.h
链接属性是分为外部链接,内部链接属性和无链接属性。
我们上面的例子说明来链接属性。
在链接之前每个文件都单独预处理,编译,汇编。在这过程中,main.c不知道add函数的存在,因为add函数具体实现放在add.c中了。它只能搁置这个函数的调用指令。
等到链接时,它会根据生成的符号表(函数只有实现了才会生成地址)找到add函数并调用。
static修饰的函数具有内部链接属性
啥意思?在链接过程中,add函数被static修饰了,那么我们称add具有内部链接属性。只能在add.c文件链接该函数,外部文件调用了add函数,链接阶段也找不到add函数,这里会报一个链接错误。

未被static的函数具有外部链接属性链接时,只要有函数声明,它能在外部文件找函数的定义,找不到就报错。
这里把上面例子的static关键字去掉即可。

总结: static修饰变量
首先,局部变量,块作用域的变量(也是局部变量)无链接属性。
因为它们的作用域限制了它们注定不会在外部文件使用。
(不要想着通过static和指针的方式,延长生命周期让外部文件能使用这个局部变量,没意义!)。

一般的全局变量如int size = 0,具有外部链接属性。

//main.c
#include<stdio.h>
int size = 10;
int main()
{printf("%d", size);return 0;
}
//test.c
//注意使用外部文件的全局变量要声明!!!
#include<stdio.h>
extern int size;//声明使用外部文件的全局变量size。
void test()
{printf("%d", size);
}

main.cint size = 10改成static int size = 10;
全局变量size就有内部链接属性了,无论外部怎么声明都找不到main.c的size了。

总结:static修饰函数和局部变量,那么它们只能在定义它们的文件内部访问。有助于隐藏实现细节,防止不同文件之间的命名冲突。

C++

C++引入了面向对象的特性.
C++中结构体和类都能写入函数.
下面讨论类与结构体的static关键字,作为C的拓展,前面C部分的static用法完全实用C++.
在类中的static这么理解.
静态的变量或者方法—类中的函数,我们称为方法.等价于类的字段和方法.

class Object {
public:static int x;static int y;static void print() {std::cout << "(" << x << "," << y << ")" << std::endl;}
};
int Object::x = 0;
int Object::y = 0;
int main()
{Object obj;obj.x = 10;obj.y = 5;Object obj2;obj2.x = 20;obj2.y = 10;obj.print();std::cin.get();
}

static修饰类的x,y是所以实例公有的字段.
任何实例都可以访问该静态字段,也可直接修改该字段.
`从内存角度上说,obj,obj2的x,y实际指向同一块内存.只要一个实例修改了这块内存的值,那么可以说这个类的部分修改了.

//最好的方式:通过类名和::访问修改字段和调用类的方法
int main()
{Object::x = 10;Object::y = 20;Object::print();std::cin.get();
}

若你要放入一个信息,这个信息与类有关,同时你又希望所有对象共享这一块数据就用static修饰.

静态方法
简单来说,若你了解C++的this指针,那么无须我多说了.
对于静态方法,它不会隐式传递this指针,静态方法不能访问类的实例字段.
比如下面就是一种错误写法.

class Object {
public:int x;int y;static void print() {std::cout << "(" << x << "," << y << ")" << std::endl;}
};
class Object {
public:int x;int y;static void print() {std::cout << "(" << x << "," << y << ")" << std::endl;}
};
int main()
{Object obj;obj.x = 10;obj.y = 20;obj.print();return 0;
}

print方法是静态方法,它不会拿到类里任何实例的字段.所以这里会报一个错误
既然静态方法没有隐式传递指针,那么我们就显示传递对象不就好了吗?

static void print(Object& obj) {std::cout << "(" << obj.x << "," << obj.y << ")" << 									std::endl;}

完,static的个人总结差不多就这些了.

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com