[c/c++] static 关键字

news2025/2/27 8:14:13

从修饰的对象来看,static 可以修饰局部变量,也可以修饰全局变量,可以修饰函数;可以修饰类中的成员变量以及成员函数。

从生命周期的角度来看,static 修饰的对象的生命周期,与进程的生命周期是一致的。

从作用域的角度来看,局部静态变量的作用域是函数内,全局静态变量的作用域是模块内;类中的静态成员变量和成员函数的作用域,能访问到类的地方都可以访问到静态成员,类中的静态成员同时也受 public,private,protected 权限修饰符的影响。在 c 语言中,静态函数的作用域只在模块内,这样也是一种权限管理,不让其它模块来访问,增强了安全性;同时,多个模块中的两个静态函数的函数名也可以是相同的,给开发带来了方便。

1 static 修饰变量

1.1 局部静态变量

如下代码,在函数 Test() 中声明定义了一个静态变量 static int a = 10,局部静态变量有以下 3 点需要注意:

(1)作用域是函数内

(2)在 static int a = 10,只在启动的时候初始化一次,不像普通的变量,Test() 每次调用的时候都会将 a 赋值成 10。代码中的 a++ 分别是 11,12,13 ...

(3)在声明的时候需要初始化,并且初始化的值必须得是确定的

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

void Test(int data) {
  // static int a = data; // 这样编译不过,编译的时候就需要确定  error: initializer element is not constant
  static int a = 10;
  a++;
  printf("a = %d, data = %d\n", a, data);
}

int main() {
  for (int i = 0; i < 10; i++) {
    Test(i);
  }
  return 0;
}

运行结果如下:

1.2 全局静态变量

在 c 语言中,全局静态变量的作用域就是在源文件内,一个 c 文件中的函数无法访问到另一个 c 文件中声明的静态全局变量。

文件 s0.c 中定义一个静态变量:

static int static_ele = 100;

文件 s0.h 中使用 extern 声明了这个变量:

extern int static_ele;

文件 s2.c 中包含了头文件 s0.h,并且尝试访问变量 static_ele。

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "s0.h"

static int a = 100;

int main() {
  printf("a = %d, static_ele = %d\n", a, static_ele);
  return 0;
}

编译会报错,在 s2.c 中无法访问到 static_ele。

2 static 修饰函数

static 修饰的函数也是只能在 c 文件内部访问,在其它 c 文件不能访问。
 

文件 s3.c 中定义一个静态函数 static void SayHello():

#include <stdlib.h>
#include <stdio.h>

static void SayHello() {
  printf("hello in static function\n");
}

void SayHello1() {
  printf("hello1\n");
}

文件 s3.h 中是函数的声明:

void SayHello();
void SayHello1();

文件 s4.c 中包含了头文件 s3.h,并尝试调用函数 SayHello():

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "s3.h"

int main() {
  SayHello();
  SayHello1();
  return 0;
}

 编译报错,找不到函数定义:

在 c 语言中,经常使用函数指针,如果静态函数被一个函数指针维护,那么其它模块可以通过函数指针来调用静态函数的。static 关键字是编译阶段检查的语法,在运行时无法检查。

对 s3.c,s3.h,s4.c 进行修改,使用一个函数指针来保存静态函数 SayHello() 的地址,这样的话只要能拿到这个函数指针,就能调用对应的函数。

s3.c:

#include <stdlib.h>
#include <stdio.h>
#include "s3.h"

static void SayHello() {
  printf("hello in static function\n");
}

void SayHello1() {
  printf("hello1\n");
}

struct FunctionPointer fp = {
  .say_hello = SayHello
};

s3.h

void SayHello1();

struct FunctionPointer {
  void (*say_hello)();
};

extern struct FunctionPointer fp;

s4.c

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "s3.h"

int main() {
  fp.say_hello();
  SayHello1();
  return 0;
}

编译运行结果如下:

3 static 修饰类成员

3.1 静态成员变量

静态成员变量属于类,不属于对象。所有的对象共享一份静态成员变量,而不是每个对象都拥有一个。

静态成员变量有以下几点需要注意,在代码中通过注释做了说明。

(1)需要在类外初始化

(2)在类外初始化的时候不能使用 static 修饰

(3)静态成员变量可以被 public,private,protected 修饰,访问权限与非静态成员变量是一致的

#include <iostream>
#include <string>

class Test {
public:
  Test() {
    std::cout << "Test()" << std::endl;
    public_a = 1;
    private_a = 2;
    protected_a = 3;
  }

  ~Test() {
    std::cout << "~Test()" << std::endl;
  }

  void Do() {
    std::cout << "public a = " << public_a << std::endl;
    std::cout << "private a = " << private_a << std::endl;
    std::cout << "protected a = " << protected_a << std::endl;
  }

  static int public_a;

private:
  static int private_a;

protected:
  static int protected_a;
};

// 类的静态成员变量在类外初始化的时候
// 不能再加 static 关键字
// 如果还带 static 关键字,会报错
// error: ‘static’ may not be used when defining (as opposed to declaring) a static data member
// static int Test::public_a = 10;
// static int Test::private_a = 20;
// static int Test::protected_a = 30;

// 类的静态成员变量需要在类外初始化
int Test::public_a = 10;
int Test::private_a = 20;
int Test::protected_a = 30;

int main() {
  Test t;
  t.public_a = 100;
  t.Do();
  // t.private_a = 200;
  // t.protected_a = 300;
  Test::public_a = 1000;
  // Test::private_a = 200;
  // Test::protected_a = 300;
  t.Do();
  return 0;
}

 运行结果如下图所示:

3.2 静态成员函数

静态成员函数有以下几点需要注意:

(1)可以在类内部声明和实现

(2)也可以在类内部声明,在类外部实现,在类外部实现时,不能 static 修饰

(3)静态成员函数中不能调用非静态成员函数,也不能使用非静态成员变量,因为静态函数中没有 this 指针

#include <iostream>
#include <string>

class Test {
public:
  Test() {
    std::cout << "Test()" << std::endl;
    public_a = 1;
    private_a = 2;
    protected_a = 3;
  }

  ~Test() {
    std::cout << "~Test()" << std::endl;
  }

  void Do() {
    std::cout << "public a = " << public_a << std::endl;
    std::cout << "private a = " << private_a << std::endl;
    std::cout << "protected a = " << protected_a << std::endl;
  }

  static void StaticDo() {
    // Do(); // error: cannot call member function ‘void Test::Do()’ without object
    // public_b = 10; // error: invalid use of member ‘Test::public_b’ in static member function
    public_a = 10;
    std::cout << "StaticDo" << std::endl;
  }

  static void StaticDo1();

  static int public_a;
  int public_b;

private:
  static int private_a;

  static void PrivateStaticDo() {
    std::cout << "Private StaticDo" << std::endl;
  }

protected:
  static int protected_a;

  static void ProtectedStaticDo() {
    std::cout << "Protected StaticDo" << std::endl;
  }
};

int Test::public_a = 10;
int Test::private_a = 20;
int Test::protected_a = 30;

// error: cannot declare member function ‘static void Test::StaticDo1()’ to have static linkage
/* static */ void Test::StaticDo1() {
  std::cout << "StaticDo1()" << std::endl;
}

int main() {
  Test t;
  t.StaticDo();
  t.StaticDo1();
  // t.PrivateStaticDo();
  // t.ProtectedStaticDo();
  // Test::PrivateStaticDo();
  // Test::ProtectedStaticDo();
  return 0;
}

代码运行结果如下:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1473509.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

大话设计模式——3.建造者模式(Builder Pattern)

1.定义&#xff1a; 将一个复杂对象的构建与它的表示分离&#xff0c;使得同样的构建过程可以创建不同的表示。UML图 2.示例&#xff1a; 汽车或者电脑的组装可以采用构造者模式进行设计&#xff0c;如汽车的引擎或者轮胎&#xff0c;电脑的处理器、内存、主板等都可以进行…

【Java设计模式】四、适配器模式

文章目录 1、适配器模式2、举例 1、适配器模式 适配器模式Adapter Pattern&#xff0c;是做为两个不兼容的接口之间的桥梁目的是将一个类的接口转换成客户希望的另外一个接口适配器模式可以使得原本由于接口不兼容而不能一起工作的那些类可以一起工作 最后&#xff0c;适配器…

【51单片机】红外遥控红外遥控电机调速(江科大)

1.红外遥控简介 红外遥控是利用红外光进行通信的设备,由红外LED将调制后的信号发出,由专用的红外接收头进行解调输出 通信方式:单工,异步 红外LED波长:940nm 通信协议标准:NEC标准 2.硬件电路 红外发送部分 IN高电平时&#xff0c;LED不亮&#xff0c;IN低电平时&…

Langchain-Chatchat:离线运行的大模型知识库 | 开源日报 No.182

chatchat-space/Langchain-Chatchat Stars: 22k License: Apache-2.0 基于 ChatGLM 等大语言模型与 Langchain 等应用框架实现的开源、可离线部署的检索增强生成 (RAG) 大模型知识库项目。该项目是一个可以实现完全本地化推理的知识库增强方案&#xff0c;重点解决数据安全保护…

【C语言】linux内核netdev_start_xmit函数

一、中文注释 static inline netdev_tx_t netdev_start_xmit(struct sk_buff *skb, struct net_device *dev, struct netdev_queue *txq, bool more) {// 获取网络设备操作集合const struct net_device_ops *ops dev->netdev_ops;int rc;// 调用实际发送数据包的函数&…

RCS-YOLO:检测头和网络结构的改进

目录 摘要 原理 总体结构图 RCS模块原理 代码实现 RCS-Based One-Shot Aggregation 代码实现 检测头改进 手动计算anchor代码 yaml文件 已详细修改的代码 程序启动命令 可论文指导 V ------------> jiabei-545 往期推荐 摘要 凭借速度和准确性之间的出色平…

学习笔记-李沐动手学深度学习(七)(19-21,卷积层、填充padding、步幅stride、多输入多输出通道)

总结 19-卷积层 【补充】看评论区建议的卷积动画视频 数学中的卷积 【链接】https://www.bilibili.com/video/BV1VV411478E/?fromsearch&seid1725700777641154181&vd_sourcee81e116c4ffe5e79d4bc44738263eda4 【可判断是否为卷积的典型标志】两个函数中自变量相加…

瑞_Redis_Redis客户端

文章目录 1 Redis客户端1.1 Redis命令行客户端1.2 图形化桌面客户端1.2.1 资源准备1.2.2 安装1.2.3 建立连接 &#x1f64a; 前言&#xff1a;本文章为瑞_系列专栏之《Redis》的基础篇的Redis客户端章节。由于博主是从B站黑马程序员的《Redis》学习其相关知识&#xff0c;所以本…

导轨安装式称重传感器信号变送器差分信号隔离转换0-10mV/0-20mV/0-±10mV/0-±20mV转0-5V/0-10V/4-20mA

主要特性 DIN11 IPO 压力应变桥信号处理系列隔离放大器是一种将差分输入信号隔离放大、转换成按比例输出的直流信号导轨安装变送模块。产品广泛应用在电力、远程监控、仪器仪表、医疗设备、工业自控等行业。此系列模块内部嵌入了一个高效微功率的电源&#xff0c;向输入端和输…

图像处理ASIC设计方法 笔记4 异步FIFO

P66 异步FIFO 这段对FIFO的描述很精彩&#xff1a; 理解了水位信号的含义&#xff08;相当于通常所说的满/空标志&#xff0c;注意读时钟域的空信号&#xff0c;写时钟域的满信号&#xff0c;只能在它各自的时钟域直接使用&#xff0c;如果要在另一时钟域用&#xff0c;需要使…

顺丰科技2024届春季校园招聘常见问题解答及SHL测评题库

顺丰科技2024届春季校园招聘常见问题解答及SHL测评题库 Q&#xff1a;顺丰科技2024届校园招聘面向对象是&#xff1f; A&#xff1a;2024届应届毕业生&#xff0c;毕业时间段为2023年10月1日至2024年9月30日&#xff08;不满足以上毕业时间的同学可以关注顺丰科技社会招聘或…

【无标题】vite配置代理--loadEnv

环境变量的定义是&#xff1a;会根据当前那代码环境产生值的变化的变量 在 Vite 项目中&#xff0c;使用环境变量提供了一种在不同环境下定制化应用行为的方式。通过读取环境变量&#xff0c;我们可以设置不同的配置信息。 开发中常见的场景有&#xff1a; 区分开发和生产环境 …

<网络安全>《54 概念讲解<第一课 IT和OT>》

1 基本概念 IT&#xff1a;Information Technology的缩写&#xff0c;指信息技术&#xff1b;主要指的是企业中的各个应用系统&#xff0c;包括ERP、MES、EAM、OA等&#xff0c;分布部署在不同的网络层级。除了应用系统&#xff0c;还有计算机&#xff0c;服务器等等&#xff…

Qt介绍以及qt_creater的安装和C++项目工程创建

最近天气严寒&#xff0c;同学们要注意保暖哦&#xff01;学习的同时别忘了照顾好自己呀&#xff01;o(*&#xffe3;▽&#xffe3;*)ブ 目录 一、Qt 1、Qt概念 2、常见的GUI 二、安装qt_creater 方法一&#xff1a; 方法二&#xff1a; 三、Qt_creater 中C项目的创建 …

【BUG 记录】MyBatis-Plus 处理枚举字段和 JSON 字段

【BUG 记录】MyBatis-Plus 处理枚举字段和JSON字段 一、枚举字段&#xff08;mysql环境已测、postgresql环境已测&#xff09;1.1 场景1.2 定义枚举常量1.3 配置枚举处理器1.4 测试 二、JSON字段&#xff08;mysql环境已测&#xff09;2.1 导包2.2 使用对象接受2.3 测试 三、JS…

SpringBoot快速入门(黑马学习笔记)

需求 需求&#xff1a;基于SpringBoot的方式开发一个Web应用&#xff0c;浏览器发起请求/hello后&#xff0c;给浏览器返回字符串"Hello World~"。 开发步骤 第一步&#xff1a;创建SpringBoot工程项目 第二步&#xff1a;定义HelloController类&#xff0c;添加方…

【惠友小课堂】骨质疏松≠老年人“专利”,年轻人也不能忽视(文末附自我测试)

虽说现在大家对于骨质疏松并不陌生&#xff0c;许多中老年人甚至年轻人都开始认识到“维护骨骼要趁早”&#xff0c;但依旧有人对骨质疏松存在一些“误解”&#xff0c;今天就来一一解开。&#xff08;PS&#xff1a;文末有骨质疏松自我测试哦~&#xff09; 某在读大学生 “我这…

学成在线_课程计划查询_前端页面无法跳转

问题描述 在进行课程计划查询的接口开发时通过了http-client测试但点开课程修改界面后点击保存并进行下一步时无法跳转到修改课程计划查询的页面。 问题原因 课程信息修改的Controller层没有实现 QAQ&#xff08;可能是老师在讲这一块的时候没有提这一点&#xff08;我也记…

Netty权威指南——基础篇2(NIO编程)备份

1 概述 与Socket类和ServerSocket&#xff0c;NIO也提供了SocketChannel和ServerSocketChannel两种不同的套接字通道实现。这两种新增的通道都支持阻塞和非阻塞两种模式。阻塞模式使用简单&#xff0c;但性能和可靠性都不好&#xff0c;非阻塞模式则正好相反。一般来说&#xf…

论文阅读:《High-Resolution Image Synthesis with Latent Diffusion Models》

High-Resolution Image Synthesis with Latent Diffusion Models 论文链接 代码链接 What’s the problem addressed in the paper?(这篇文章究竟讲了什么问题&#xff1f;比方说一个算法&#xff0c;它的 input 和 output 是什么&#xff1f;问题的条件是什么) 这篇文章提…