新闻详情

新闻详情

首页 / 资讯中心 / 详情

static 的五种含义:同一个关键字,五种不同的事

发布时间:2026/9/27 22:47:46来源:尧图网络
static 的五种含义:同一个关键字,五种不同的事
static大概是 C 里最「身兼数职」的关键字放在函数里的局部变量前、放在全局变量前、放在类成员前意思全不一样新手极易混淆。但万变不离其宗——它要么改变存储期storage duration变量活多久要么改变链接linkage名字在多少个翻译单元里可见要么只是声明一个编译期常量。本文把五种含义逐个拆开每种配一个能真编译运行的最小示例。1. 引子为什么同一个词意思差这么多先看三段代码猜猜static各自在干嘛voidf(){staticintx0;x;}// ① 函数内的 staticstaticintg0;// ② 全局的 staticstructS{staticintn;};// ③ 类里的 static① 让x跨调用存活② 让g只在本文件可见③ 让n被所有对象共享。同一个词管的三件事风马牛不相及。根源在于 C 语言历史包袱static最早只有「内部链接」一义后来被不断复用。理解它的钥匙是分清作用域scope名字在哪能用和存储期变量活到什么时候——下面每节都会点明它到底改了哪个。2. 含义一局部静态变量改的是存储期函数内的static局部变量作用域还是那个函数块但存储期变成了「静态存储期」第一次执行到时初始化一次之后一直存活到程序结束下次调用沿用上次的值。#includecstdiointnextId(){staticintid0;// 只初始化一次之后一直存在到程序结束returnid;}intmain(){printf(id1 %d\n,nextId());printf(id2 %d\n,nextId());printf(id3 %d\n,nextId());}id1 1 id2 2 id3 3如果你把static去掉id每次都是新的局部变量三次都会打印1。static在这里只改存储期不改作用域——id仍然只能在nextId里访问但它不再随函数返回而销毁。官方文档Storage duration — cppreference静态存储期的权威定义以及它与自动、动态、线程存储期的并列关系。2.1 关键坑C11 起局部静态的初始化是线程安全的这是面试常考点也是Meyers 单例Meyers singleton能成立的根基。C11 标准规定局部静态变量的初始化不会发生数据竞争data race多线程同时第一次进入时只有一个线程执行初始化其余阻塞等待。看这个单例#includecstdioclassLogger{public:staticLoggerinstance(){staticLogger inst;// C11 起初始化线程安全且只发生一次returninst;}voidlog(constchar*msg){printf(log: %s\n,msg);}private:Logger(){printf(Logger 构造只一次\n);}// 私有构造禁止外界 new};intmain(){Logger::instance().log(hello);Logger::instance().log(world);}Logger 构造只一次 log: hello log: world注意Logger只构造了一次——第二次instance()复用第一次的对象。在 C11 之前这个写法要用「双检锁double-checked locking」手写极易写错现在靠局部静态的线程安全初始化一行static Logger inst;就搞定。3. 含义二全局静态改的是链接全局变量前加static意思是内部链接internal linkage这个变量只在**当前翻译单元translation unit大致就是一个 .cpp 加上它 include 的头**里可见别的 .cpp 即使extern也找不到它。#includecstdiostaticintg_count42;// 静态全局变量内部链接只在当前翻译单元可见intmain(){printf(static 全局变量 g_count %d\n,g_count);}static 全局变量 g_count 42它的存储期仍是静态存储期活到程序结束和普通的全局变量一样static在这里纯粹是收窄了链接——把「全局可见」改成「文件内可见」避免和其它 .cpp 里的同名全局变量在链接阶段撞符号。这也是匿名命名空间anonymous namespace流行之前限制全局名字的主要手段。官方文档Program and linkage — cppreference内部链接、外部链接、无链接三者的精确界定。4. 含义三静态成员变量所有对象共享一份类里的static成员变量不属于任何一个对象而是被该类的所有对象共享只有一份。它同样活在静态存储区sizeof一个对象时不会把它算进去。#includecstdiostructWidget{Widget(){count;}// 每构造一个计数 1staticintcount;// 声明所有对象共享这一份};intWidget::count0;// 定义必须在类外C17 之前必须这样写intmain(){Widget a,b,c;printf(构造了 %d 个 Widget\n,Widget::count);printf(通过对象也能访问%d\n,a.count);}构造了 3 个 Widget 通过对象也能访问3重点在int Widget::count 0;这行类外定义。C17 之前静态成员变量只是「声明」必须在某个 .cpp 里单独「定义」一次否则链接时报undefined reference。C17 起可以用inline把定义直接写进类里见第 6 节少写一行还不容易漏。5. 含义四静态成员函数没有 this可作回调类里的static成员函数没有this指针因此它不能访问非静态成员但可以不依赖任何对象直接通过类名调用。这个特性让它很适合当回调函数比如传给std::thread、C 风格 API 的函数指针。#includecstdiostructMath{staticintadd(intx,inty){returnxy;}// 没有 this不能访问非静态成员};intmain(){printf(Math::add(3, 4) %d\n,Math::add(3,4));// 不用对象也能调用}Math::add(3, 4) 7非静态成员函数因为有this类型其实是int (Math::*)(int,int)没法直接当普通函数指针用静态成员函数类型就是int(*)(int,int)二者在「能不能当回调」上差别很大。6. 含义五类内 inline static constexpr 常量C17这是最「现代」的一种。在类里放一个编译期常量C17 之前要写成「类内static constexpr声明 类外定义」C17 起给static成员加inline定义直接写在类里头文件里就能完成不用再找地方放类外定义。#includecstdiostructConfig{staticconstexprintMaxRetry3;// C17类内直接定义无需类外staticconstexprdoubleTimeout1.5;};intmain(){printf(MaxRetry %d\n,Config::MaxRetry);printf(Timeout %.1f\n,Config::Timeout);}MaxRetry 3 Timeout 1.5constexpr保证它在编译期就是常量可以取地址、可以当模板参数、零运行时开销。inline解决了「头文件里定义导致多翻译单元重复定义」的老问题——这正是下一讲inline要展开的主题。7. 作用域 vs 存储期一张表收尾五种含义最容易被搞混是因为static在不同位置改的是不同的维度。把这张表存进知识库随时对照含义出现位置主要改变存储期链接局部静态变量函数内存储期跨调用存活静态无链接全局静态全局链接收窄到本 TU静态内部链接静态成员变量类内被所有对象共享一份静态外部链接类作用域静态成员函数类内去掉this可作回调静态外部链接类作用域类内 inline static constexpr类内编译期常量类内定义静态外部链接内联它们的存储期出奇一致——全是静态存储期活到程序结束真正分化的是作用域和链接。进程内存布局里它们都落在静态区data/BSS 段和栈上的自动变量、堆上的动态变量划清界限进程内存布局简化 ┌──────────────────┐ 高地址 │ 栈 stack │ ← 局部自动变量自动存储期函数返回即释放 ├──────────────────┤ │ 堆 heap │ ← new / make_unique动态存储期 ├──────────────────┤ │ BSS未初始化静态│ │ data已初始化静态│ ← static / 全局静态存储期程序结束才释放 ├──────────────────┤ │ 代码 text │ └──────────────────┘ 低地址哪种写法更优局部静态 vs 裸全局计数顺带回答一个常见取舍要做一个「跨函数调用累加的计数器」该用局部静态还是裸全局写法可见范围风险static int x放在函数内仅该函数可见封装好其它代码碰不到裸int x放全局整个程序extern 可见名字易冲突易被误改结论能用局部静态就别用裸全局——前者把状态封在函数内部符合「最小可见性」原则也省去全局命名污染。只有当多个函数都必须读写同一份状态时才提升到最好也加static的全局或类静态成员。8. 完整示例把五种 static 串起来下面这个程序整体编译运行覆盖前面所有要点#includecstdio// ② 全局静态本文件内可见staticintg_fileTag99;// ⑤ 类内 inline static constexpr编译期常量structLimits{staticconstexprintMaxItems5;};structCounter{// ③ 静态成员变量所有对象共享staticinttotal;// ④ 静态成员函数无 this可作回调式接口staticintgetTotal(){returntotal;}Counter(){total;}// ① 局部静态也常和类静态配合计数};intCounter::total0;// 类外定义非 inline 的经典写法intmain(){Counter a,b,c;printf(对象个数 %d\n,Counter::getTotal());printf(文件标签 %d\n,g_fileTag);printf(MaxItems %d\n,Limits::MaxItems);}对象个数 3 文件标签 99 MaxItems 59. 延伸阅读Storage duration — cppreference五种存储期自动/静态/动态/线程/空的总纲建议通读。Static members — cppreference类静态成员、静态函数的完整规则。Core Guidelines · I.2 / NR.* 关于避免全局状态、限制可见性的设计建议理解static全局为何要慎用。10. 一句话总结static在五个位置干五件不同的事函数内改存储期跨调用存活且 C11 起初始化线程安全、全局改链接收窄到本文件、类内成员是共享数据/无 this 的接口、类内inline static constexpr是编译期常量——存储期全都一样是静态存储期真正分化的是作用域与链接记住这点就不再晕。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

YuE2 零样本翻唱实战:把一段录音做成爵士版 2026/9/27 23:36:14

YuE2 零样本翻唱实战:把一段录音做成爵士版

YuE2 零样本翻唱实战:把一段录音做成爵士版 【免费下载链接】YuE YuE2: frontier music generation with symbolic planning, zero-shot covers, and agentic music editing. 项目地址: https://gitcode.com/GitHub_Trending/yue/YuE 手里有一段 source.wav&…

阅读更多 →
Ryujinx Switch 模拟器新手上手指南:从源码构建到跑起游戏只需 6 步 2026/9/27 23:36:14

Ryujinx Switch 模拟器新手上手指南:从源码构建到跑起游戏只需 6 步

Ryujinx Switch 模拟器新手上手指南:从源码构建到跑起游戏只需 6 步 【免费下载链接】Ryujinx 用 C# 编写的实验性 Nintendo Switch 模拟器 项目地址: https://gitcode.com/GitHub_Trending/ry/Ryujinx Ryujinx 是用 C# 编写的开源 Nintendo Switch 模拟器。…

阅读更多 →
VB.NET+SQL Server BS架构订餐系统:从环境搭建到部署避坑 2026/9/27 23:36:14

VB.NET+SQL Server BS架构订餐系统:从环境搭建到部署避坑

简介:基于VB与VB.NET结合SQL Server开发的Web订餐系统,采用B/S架构,面向需要完成课程设计、毕业设计或餐饮信息化项目的计算机专业学生与开发人员,涵盖了菜品管理、订单处理、餐台管理等典型模块。项目源码经过完整测试校正&#…

阅读更多 →
NativeWind 原理剖析:从 Tailwind CSS 到 React Native StyleSheet 的编译与运行时架构 2026/9/27 23:36:07

NativeWind 原理剖析:从 Tailwind CSS 到 React Native StyleSheet 的编译与运行时架构

移动开发跨平台前端 【免费下载链接】nativewind The utility-first workflow you love from Tailwind CSS in your React Native applications. 项目地址: https://gitcode.com/gh_mirrors/na/nativewind 点击查看 免费下载 本篇技术指南以 NativeWind 官方文档 H…

阅读更多 →
AiShort 入门指南 | 复制精选提示词到 ChatGPT/DeepSeek/Cursor,30 秒提升 AI 对话质量 2026/9/27 23:36:07

AiShort 入门指南 | 复制精选提示词到 ChatGPT/DeepSeek/Cursor,30 秒提升 AI 对话质量

AI 应用提示工程人工智能前端 【免费下载链接】ChatGPT-Shortcut Stop writing prompts from scratch — a searchable prompt library for ChatGPT, Claude, Gemini and Cursor Русский 한국어 العربية हिन्दी ไทย | 别再从头写提示词&…

阅读更多 →
SVGEdit 快速上手指南:在浏览器中部署、集成与二次开发的完整方案 2026/9/27 23:36:07

SVGEdit 快速上手指南:在浏览器中部署、集成与二次开发的完整方案

前端图形学 【免费下载链接】svgedit Powerful SVG-Editor for your browser 项目地址: https://gitcode.com/gh_mirrors/sv/svgedit 点击查看 免费下载 SVGEdit 是一款基于 Web、由 JavaScript 驱动的开源 SVG 绘图编辑器,可在任何现代浏览器中运行&am…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉