C++:C++全局变量:看完还不懂全局变量来捶我

news2024/12/26 15:07:21

我们知道,全局变量时C++语言语法和语义中一个很重要的知识点,首先它的存在意义需要从三个不同角度去理解。

  1. 对于程序员来说,它是一个记录内容的变量(variable)
  2. 对于编译/链接器来说,它是一个需要解析的符号 (symbol)
  3. 对于计算机来说,它可能是具有地址的一块内存(memory)

其次从语法/语义上说:

从作用域上看:带static关键字的全局变量范围只能限定在文件里,否则会外联到整个模块和项目中。

从生存期看:即使不带static关键字,它的存储在静态全局区,因为它是静态的,并且贯穿整个程序或模块运行期间(Notice :  正是跨单元访问和持续生存周期这两个特点使得全局变量往往成为一段受攻击代码的突破口,这一点十分重要)

从空间分配上看:定义且初始化的全局变量在编译期,会在数据段(.data)上分配空间,定义但未初始化的全局变量(相当于仅仅是声明)暂存(tentative definition)在.bss段,编译时自动清零,而仅仅是声明的全局变量只能算个符号,寄存在编译器的符号表内,不会分配空间,直到链接或者运行时再冲定向到相应的地址上。

下面我们从 两个方面介绍:变量初始化问题

1:全局变量初始化带来的变量重复定义

2:变量初始值默认值问题

1:全局变量初始化带来的重复定义问题。 

t.h

#pragma once
int a;


foo.cpp

#include<iostream>
#include<string>
#include "t.h"
using namespace std;

struct 
{
	int b1;
} b = {4};

 void foo() {
	cout << "全局变量 &a: " << &a
		<< " ,&b: " << &b
		<< " ,sizeof b: " << sizeof(b)
		<< " ,struct b.b: " << b.b1 << endl;
}

main.cpp

#include<iostream>
#include<string>
#include "foo.cpp"
#include "t.h"
using namespace std;

int c;

int main()
{
	foo();
	cout << "全局变量 &a: " << &a
		<< " ,c: " << c << endl;
}

 

很显然,

1:由于t.h 头文件中 声明且定义 int a (默认初始值为0),当 foo.cpp 和 main.cpp 同时引入 t.h头文件时,当main.o 和foo.o 两个文件链接时,就会发生 变量a 重复定义。 

2:同样由于 foo.cpp 声明并定义 void foo() 函数,当main.cpp 引入了 foo.cpp 源文件,在main.o和foo.o 两个文件链接时,也会发生:函数重复定义。

修改方案

解决变量的重定义:可以引入 static 关键字,这个关键字的作用就是

1:全局变量范围只能限定在文件里,不会外联到整个模块和项目中。

2:变量只在源文件中有效。

解决函数重定义:引入 inline关键字修饰函数

1:inline 相当于在函数定义的地方,将函数展开,这样就可以避免函数符号在符号表中被多次找到。

// t.h

#pragma once
static int a;


// foo.cpp
#include<iostream>
#include<string>
#include "t.h"
using namespace std;

struct 
{
	int b1;
} b = {4};

 inline void foo() {
	cout << "global varial &a: " << &a << " ,a value: "<< a
		<< " ,&b: " << &b
		<< " ,sizeof b: " << sizeof(b)
		<< " ,struct b.b: " << b.b1 << endl;
}

// main.cpp

#include<iostream>
#include<string>
#include "foo.cpp"
#include "t.h"
using namespace std;

int c;

int main()
{
	foo();
	cout << "global varial &a: " << &a << " ,a value: " << a
		<< " ,c: " << c << endl;
}

// 打印结果
global varial &a: 0x407028 ,a value: 0 ,&b: 0x404004 ,sizeof b: 4 ,struct b.b: 4
global varial &a: 0x407028 ,a value: 0 ,c: 0

2:局部/全局变量初始值或默认值问题

在C语言中的全局变量和静态变量都是会自动初始化为0,堆和栈中的局部变量不会初始化,造成拥有不可预测的值。

C++保证了所有对象与对象成员都会初始化,但其中基本数据类型的初始化还需要依赖构造函数。下面我们讨论一下C++ 中成员变量的初始化规则

2.1 初始化语法 

在C语言中在声明时用 = 即可完成初始化操作,C++偏向于使用圆括号()来完成。

// C语言风格

int i = 3;

int arr[] = {1,2,3};

// C++风格

int i (3);

int i = int(4)

int* p = new int(3)

int* arr = new int[3] {1,2,3}

在C语言中: int a ----》表示声明了整型变量a但未初始化,但是C++中总是会初始化(无论是否写了圆括号或者是否写了参数列表)

int  basic_var     // 未初始化,应用 “默认初始化机制”

Cperson person    // 初始化,以空的参数列表调用构造函数。

(可以参考C语言为什么没有默认初始化机制)

C语言令人抓狂的一面——全局变量

2.2:默认初始化机制规则 (局部变量/全局变量/静态变量)

定义基本数据类型变量(单个值,数组)的同时可以指定初始值,如果未指定C++会去执行默认初始化(default-initialization) , 那么这个规则是怎样的了?

栈:中的变量和堆中的变量(动态内存),会保有不确定的值

全局变量和静态变量(包括静态局部变量):会初始化为零。

那么为啥会有这个区别了 ?

首先,未初始化的和初始化为零的静态/ 全局变量编译器是同样对待的,把他们存储在 进程 BSS段(这是全零的一段内存空间)中,所以他们会被 “默认初始化”为零。

#include<iostream>
#include<string>
using namespace std;

int g_var;
int* g_pointer;
static int g_static;

int main() {
    int l_var;
    int* l_pointer;
    static int l_static;

    cout << g_var << endl << g_pointer << endl << g_static << endl;
    cout << l_var << endl << l_pointer << endl << l_static << endl;
};

 很显然:局部变量没有初始化,编译器直接报错。 

2.3:成员变量的初始化 

成员变量分为成员对象和内置类型成员,其中成员对象总会被初始化,而我们要做的就是在构造函数中初始化其中的内置类型成员

#include<iostream>
#include<string>
using namespace std;

class A {
public:
    int v;
};
A g_var;

int main() {
    A l_var;
    static A l_static;
    cout << g_var.v << ' ' << l_var.v << ' ' << l_static.v << endl;
    return 0;
}


打印结果: 0 6422280 0

从打印结果可知:内置类型的成员变量的 “默认初始化”行为取决于所在对象的存储方式,而存储方式对应的默认初始化规则是不变的。(即:全局对象变量 g_var 和局部静态变量对象 l_static存储在静态区,所以对象的 内置类型也存储在静态区,所以这个内置类型变量就可以默认初始化,而局部变量对象 l_var  存储在 栈中,所以它的 内置类型也在栈中,所以它就不能默认初始化,因而也就存在不确定值) 。

所以:

在 Effective C++ :item4 中,讨论了如何正确地在构造函数中初始化数据成员,这里不过多讨论,直接给出初始化的写法

class A{
public:
    int v;
    A(): v(0);     // 直接在圆括号中初始化变量 v
};

 

 

 

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

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

相关文章

java使用JSch连接服务器实现命令交互

java使用JSch连接服务器实现命令交互1、通过maven引入jsch2、代码编写&#xff08;1&#xff09;创建MyUserInfo&#xff08;2&#xff09;创建Shell类连接服务器&#xff08;3&#xff09;启动3、测试结果JSch官网 1、通过maven引入jsch <dependency><groupId>co…

万字长文--详解Git(快速入门)

Git基础与扩展Git1、Git概念1.1 关于版本控制1.2 Git基础概念2、Git基础操作2.1 安装并配置Git2.2 Git的基本操作3、Github操作3.1 关于开源3.2 注册账号3.3 远程仓库的使用4、Git分支操作4.1 本地分支操作4.2 远程分支操作Git 1、Git概念 1.1 关于版本控制 文件的版本管理的…

7 种常用的数据挖掘技术分享

有人说&#xff1a;一个人从1岁活到80岁很平凡&#xff0c;但如果从80岁倒着活&#xff0c;那么一半以上的人都可能不凡。 生活没有捷径&#xff0c;我们踩过的坑都成为了生活的经验&#xff0c;这些经验越早知道&#xff0c;你要走的弯路就会越少。 摘要: 随着信息领域的进步…

详解DFS(深度优先搜索)算法+模板+指数+排列+组合型枚举+带分数四道例题

目录 前言&#xff1a; 1.背景 2.图解分析 3.算法思想 4.dfs四大例题 4.1.递归实现指数型枚举 题解&#xff1a; 4.2.递归实现排列型枚举 题解&#xff1a; 字典序: 4.3.递归实现组合型枚举 题解: 4.4.带分数 题解&#xff1a; 5.最后&#xff1a; 前言&#xff1a;…

来了解一下ASN.1?

想要了解证书&#xff0c;必须先了解ASN.1和编码规则。这篇文章简单介绍ASN.1&#xff0c;不过分探讨细节&#xff0c;大家如果有兴趣可以继续深入研究。 一、ASN.1 ASN.1是Abstract Syntax Notation One&#xff08;抽象文法描述语言&#xff09;的缩写。计算机系统之间交换…

Android Studio 阅读 frameworks/base 下的代码

从网上搜的方案都是生成 android.ipr&#xff0c;但是这个需要整编&#xff0c;整编一次比较费时费劲&#xff0c;所以想了个巧招 首先用 Android Studio 打开 frameworks/base&#xff0c;其文件夹目录大概形如下&#xff1a; ├── Android.bp├── Android.mk├── api …

入门深度学习——基础知识总结(python代码实现)

入门深度学习——基础知识总结&#xff08;python代码实现&#xff09; 目前&#xff0c;AI基本上可以说是烂大街了。几乎什么都可以说使用了AI技术&#xff0c;听起来很拉风&#xff0c;很nb的样子。而其中目前最为火热的非深度学习&#xff08;Deep Learning&#xff09;莫属…

VisionPro (R) QuickBuild 工具使用问题解决 自用

右击我的电脑选择属性。搜索“安全中心” 点击病毒和威胁保护 在病毒和威胁保护中选择威胁信息&#xff08;当前威胁-保护历史记录&#xff09; 受影响的项目 file: C:\WINDOWS\sysWOW64\cognex.dll 相机和光源不能同时触发&#xff0c;光源要先于相机触发并且持续相机采集…

leetCode周赛-328

相关题解题目一&#xff1a;6291. 数组元素和与数字和的绝对差题目二&#xff1a;6292. 子矩阵元素加 1题目三&#xff1a;6293. 统计好子数组的数目题目四&#xff1a;2538. 最大价值和与最小价值和的差值题目一&#xff1a;6291. 数组元素和与数字和的绝对差 题目链接&#…

Web Security 之 Insecure deserialization

Insecure deserialization 在本节中&#xff0c;我们将介绍什么是不安全的反序列化&#xff0c;并描述它是如何使网站遭受高危害性攻击的。我们将重点介绍典型的场景&#xff0c;并演示一些 PHP、Ruby 和 Java 反序列化的具体示例。最后也会介绍一些避免不安全的反序列化漏洞的…

14正交向量与子空间

正交向量与子空间 本章研究的重点还是之前提到过的子空间&#xff0c;但是本章我们主要从正交的角度来探讨这些子空间具有的性质&#xff0c;主要内容见下图。 注意&#xff0c;上图指出了我们之前没有关注到的子空间的一些性质&#xff1a;对于一个矩阵&#xff0c;其零空间…

微信推送消息给女友提醒每天天气情况,本文讲解流程,附带代码,可快速上手。

实现的效果图 此模板为自己添加的&#xff0c;各位看客可以自行添加&#xff0c;如何添加接着往下看。备注在数据库自定义设置。如果你非专业人士&#xff0c;可以联系作者给你专属设置。如果需要代为设置跳转到文章结束位置。 完整的代码 代码下载地址 微信公众号设置 申…

《Linux Shell脚本攻略》学习笔记-第八章

8.1 简介 TCP/IP网络的运作过程就是在节点之间传递分组。每一个分组中都包含了目标的IP地址以及处理分组中数据的应用程序端口号。 当节点接收到分组时&#xff0c;它会查看自己是否就是改分组的目的地。如果是&#xff0c;节点会再检查端口号并调用相应的应用程序来处理分组数…

Cookie 会话身份验证是如何工作的?

在 Web 应用程序中&#xff0c;Cookie-Session 是一种标准的身份验证方法。饼干&#xff0c;也被称为“sweet cookies”。类型为“小文本文件”&#xff0c;是指一些网站为了识别用户身份而存储在客户端的数据。Session的主要功能是通过服务器记录用户的状态。 在典型的在线购物…

类与类之间关系的表示方式

1.关联关系 关联关系是对象之间的一种引用关系&#xff0c;用于表示一类对象与另一类对象之间的联系&#xff0c;如老师和学生&#xff0c;师傅和徒弟&#xff0c;丈夫和妻子等。关联关系是类与类之间最常用的一种关系&#xff0c;分为一般关联关系&#xff0c;聚合关系和组合…

郭天祥十天入门单片机学习笔记

电子元件 排阻 有两种&#xff1a; nn1 二极管 几个概念 限流电阻&#xff1a;与二极管串联&#xff0c;防止电流过大烧毁二极管导通压降&#xff1a;二极管亮起的最小电压 贴片式绿色为阴极&#xff0c;插入式短脚为阴极 数码管 共阴极&#xff1a;对应段选信号置1亮…

测试开发 | 这些常用测试平台,你们公司在用的是哪些呢?

测试管理平台是贯穿测试整个生命周期的工具集合&#xff0c;它主要解决的是测试过程中团队协作的问题。在整个测试过程中&#xff0c;需要对测试用例、Bug、代码、持续集成等等进行管理。下面分别从这四个方面介绍现在比较流行的管理平台。 图片108091 50.2 KB 测试用例管理平台…

Doris 使用记录(随机更新(ง •_•)ง)

文章目录知识点常见数据分布方式使用表动态分区Hash分桶知识点 常见数据分布方式 分布式数据库中常见的数据分布方式。 轮询&#xff1a;Round-Robin&#xff0c;假设分桶数为3&#xff0c;数据按顺序依次写入桶1、桶2、桶3&#xff0c;然后继续循环。 区间&#xff1a;Ran…

【Lp-CVT and Applications】

Lp-CVT and Applications 论文笔记 本文是论文 Lp Centroidal Voronoi Tessellation and its Applications 的学习笔记。有不足之处&#xff0c;请指出&#xff01;非常感谢&#xff01; 1 主要工作 解决问题&#xff1a;推广CVT以适用各向异性和六面体网格划分。推广 CVT &am…

如何模拟实现一个“缓存”?

目录 前言 一、LRU Cache是什么 二、模拟实现 2.1、通过继承 LinkedHashMap 模拟实现 2.2、自主模拟实现LRU Cache 2.2.1、LRU Cache的定义 2.2.2、存放结点 2.2.3、访问结点 2.2.4、LRU Cache 完整模拟代码 小结 前言 这次主要实现一个类似缓存的一种数据结构&#xf…