服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C/C++ - C/C++的关键字之static你了解吗

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

2022-10-07 16:22小菜鸡加油 C/C++

这篇文章主要为大家详细介绍了C/C++的关键字之static,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助

C语言

隐藏

场景演示

当我们同时编译多个文件时,所有未加static前缀的全局变量和函数都具有全局可见性。会导致符号表认为存在同名全局变量和函数发生碰撞。

场景:全局的变量/函数在.h中会在多个.cc文件中拥有且全局可见有链接问题。

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

a.h

#pragma once
#include<stdio.h>
void Test()
{
printf("I am test..\n");
}

b.c

#include"a.h"
void call()
{
  Test();
}

c.c

#include"a.h"
int main()
{
 Test();   
}

makefile

all:c
c:c.o b.o
	gcc -o $@ $^ 
c.o:c.c
	gcc -c $^ 
b.o:b.c
	gcc -c $^ 
.PHONY:clean
clean:
	rm -rf *.o c

运行结果

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

此时查看b.o和c.o符号表。(readelf -s xxx.o)

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

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

可以看到双方的.o符号表中都认为有一个GLOBAL全局函数的Test,两者汇编阶段形成的符号表此时在汇总的阶段就会产生同名全局函数冲突。

此时在两者的二进制文件里都认为各自拥有Test()函数,且都在全局。而全局函数只能有一个名字(注:重载是底层重新命名了)。虽然我们知道两个Test()是同一个,但是link的时候认为有两个同名函数实现,因此报link错。

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

解决方法

声明和定义分离

养成声明和定义分离的习惯,在.h中只声明不定义。在.c文件中定义。

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

a.h

#include<stdio.h>
void Test();

a.c

#include"a.h"
void Test()
{
  cout<<"I am test..."<<endl;
}

makefile

all:c
c:c.o b.o a.o
	gcc -o $@ $^ 
c.o:c.c
	gcc -c $^ 
b.o:b.c
	gcc -c $^ 
a.o:a.c
	gcc -c $^ 
.PHONY:clean
clean:
	rm -rf *.o c

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

为什么此时就可以正常运行了?

依然查看符号表,可以发现b.o和c.o中此时只是给Test声明留了一个全局的NOTYPE位置。

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

而在a.o中定义Test(),因此a.o中是func类型。

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

最后三个.o文件链接的时候确定Test()实际在最后生成的.out文件中的虚拟内存地址。运行时加载到内存中,之后的详细过程就是linux创建进程中的事情。

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

使用static关键字及缺陷

那如果我就是想要直接在.h中存放一个公共的全局的对象来供其他所有文件使用呢?使用static关键字。

a.h

#pragma once
#include<stdio.h>
static void Test()
{
printf("I am test..\n");
}

代码结构:

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

此时为什么又成立呢?两者.o文件中为什么对同名的全局函数包容了呢?可以看到此时两者的符号表中仍然是func,按照场景演示中的例子,应该报错的。

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

此时反汇编查看Test()函数地址。我们发现此时生成了两个test函数,不过函数地址不同。

结论:static函数作用域仅在自己所在文件,其实是编译后不同文件下的同名static函数会有不同的内部命名

不同.c文件include了static变量之后该变量只在各自包含该变量的.c中可见。

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

既然生成了两份,我们就可以发现,如果是一个静态的全局变量,我们分别进行修改实际上对两个不同的变量进行修改的。如果要解决全局变量统一性访问,保证全局变量不可变即可。另外一种方式就是使用单例模式。

a.h

#pragma once
#include<stdio.h>
static int a =0;

b.h

#include"a.h"
void call();

b.c

#include"b.h"
void call()
{
 a = 1;
 printf("a=%d\n",a);
}

c.c

#include"b.h"
int main()
{
 a=2;
 printf("a=%d\n",a); 
 call();
 printf("a=%d\n",a);
}

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

保持变量内容的持久

  • 全局静态变量

在全局变量前加上关键字static,全局变量就定义成一个全局静态变量。

内存中位置:静态存储区,在整个程序运行期间移植存在。

初始化:未经初始化的全局静态变量会被自动初始化为0(自动对象的值是任意的,除非他是被显示初始化)。

作用域:全局静态变量是从定义指出开始,到文件结尾,在声明他的文件之外是不可见的。

  • 局部静态变量

内存位置:静态存储区

初始化:未经初始化的局部静态变量会被自动初始化为0(自动对象的值是任意的,除非他是被显示初始化)。

作用域:为局部作用域,当定义他的函数或者语句块结束时,作用域结束。但是当局部静态变量离开作用域后,并没有被销毁,依然驻留在内存中,只不过我们不能再对它进行访问,直到该函数再次被调用,并且值不变。

如下的count变量作用域在test函数中,而生命周期是整个程序。在第一次进入test()的时候会初始化,之后进入test()就不再执行第5行代码了。

#include<stdio.h>
void test()
{
  static int count =0;
  count++;
}
int main()
{
  for(int i =0 ; i < 10 ; i++ ) test();
}

默认初始化为0

默认初始化为0:在静态存储区,内存中所有的字节默认值都是0x00。

#include <stdio.h>
int a;
int main(void)
{
  int i;
  static char str[10];
  printf("integer: %d;  string: (begin)%s(end)", a, str);
  return 0;
}

 

Cpp

static类成员变量

声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;静态的成员变量一定要在类外进行初始化

  • 静态变量属于整个类,所有对象,生命周期在整个程序间运行
  • 在类成员函数中,可以随便访问

static类成员方法

用static修饰的成员函数,称之为静态成员函数。(因为该成员变量没有this指针)

static成员函数,没有this指针,不使用对象就可以调用–>fun::。

静态成员函数可以调用非静态成员函数(/成员)吗?不行。没有this指针

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

class Date
{
  public:
  	Date(int year=0,int month=1,int day=1)
      {
      }
  	void f1()
      {
      }
  	static void f2()
      {
		   f1();//没有this指针
      }
  private:
}
class Date{
public:
  	void f3()
      {
          f4();//突破类域+访问限定符就可以访问 Date::f4();/对象.f4()
          //类里面是一个整体都在类域中,类里面不受访问限定符限制
      }
  	static void f4()
      {
      }
private:
};

单例模式

  • 单例模式

一个类只能创建一个对象,即单例模式,该模式可以保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息,这种方式简化了在复杂环境下的配置管理。

1.如何保证全局(一个进程中)只有一个唯一的实例对象

参考只能在堆上创建对象和在栈上创建对象,禁止构造和拷贝构造及赋值。

提供一个GetInstance获取单例对象。

2.如何提供只有一个实例呢?

饿汉模式和懒汉模式。

3.使用场景

由于全局的变量在.h中会在多个.cc文件中拥有且可见容易有链接问题。而static又只能在当前文件可见。因此真要处理成全局的就使用单例模式。

具体的单例模式在特殊类设计中提及。

总结

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

原文链接:https://blog.csdn.net/zstuyyyyccccbbbb/article/details/123054676

延伸 · 阅读

精彩推荐
  • C/C++C语言开发实现贪吃蛇游戏

    C语言开发实现贪吃蛇游戏

    这篇文章主要为大家详细介绍了C语言开发实现贪吃蛇游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    C 小白8842021-09-18
  • C/C++深入C++拷贝构造函数的总结详解

    深入C++拷贝构造函数的总结详解

    本篇文章是对C++中拷贝构造函数进行了总结与介绍。需要的朋友参考下...

    C++教程网5312020-11-30
  • C/C++C++ Primer中&、*符号的多重定义与int *p和int* p的区别讲解

    C++ Primer中&、*符号的多重定义与int *p和int* p的区别讲解

    今天小编就为大家分享一篇关于C++Primer中&、*符号的多重定义与int *p和int* p的区别讲解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,...

    yangyongUestc5412021-07-28
  • C/C++详解C++虚函数的工作原理

    详解C++虚函数的工作原理

    这篇文章主要介绍了C++虚函数的工作原理的的相关资料,文中讲解非常细致,代码帮助大家更好的理解和学习,感兴趣的朋友可以了解下...

    让我思考一下10702021-09-13
  • C/C++opencv3/C++实现光流点追踪

    opencv3/C++实现光流点追踪

    今天小编就为大家分享一篇opencv3/C++实现光流点追踪,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...

    阿卡蒂奥7292021-08-08
  • C/C++C语言二维数组几种常用的表示方法

    C语言二维数组几种常用的表示方法

    今天小编就为大家分享一篇关于C语言二维数组几种常用的表示方法,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随...

    BLSxiaopanlaile8922021-07-24
  • C/C++C++实现基数排序的方法详解

    C++实现基数排序的方法详解

    本篇文章是对使用C++实现基数排序的方法进行了详细的分析介绍,需要的朋友参考下...

    C++教程网2132020-12-13
  • C/C++C++计算图任意两点间的所有路径

    C++计算图任意两点间的所有路径

    这篇文章主要为大家详细介绍了C++求图任意两点间的所有路径 ,具有一定的参考价值,感兴趣小伙伴们可以参考一下 ...

    矮油~8912021-06-08