编程语言(3)—变量和作用域
对于静态语言,类型是编译期确定的。变量在运行期,有创建、初始化、使用、销毁四个生命周期状态。
C语言和C++
变量创建和销毁
运行期局部变量在栈上创建,全局变量和静态变量在静态区创建,new 产生的变量在堆上创建。栈变量的作用域等于函数作用域,全局变量和静态变量的作用域是进程执行期,new产生的堆变量可能逃逸,需要用户自行管理生命周期和内存释放。堆变量内存未释放会导致内粗泄露,内存重复释放会导致进程崩溃。
为了避免堆变量的遗忘释放或重复释放,C++标准库提供智能指针帮助管理对象的释放。原则上C++一切堆变量都应该使用智能指针。
- std::unique_ptr,独占所有权,不可复制,只能移动。
- std::shared_ptr,共享所有权,使用引用计数(reference count)管理资源的释放。
- std::weak_ptr,弱引用,不增加引用计数;依赖于 std::shared_ptr,用于解决循环引用问题
1 |
|
变量初始化
- 局部变量,未初始化的为不确定值(即垃圾值)
- 静态变量,自动初始化为 0 或 NULL(指针类型),静态变量初始化只执行一次
- 全局变量,自动初始化为 0 或 NULL
C++类变量规则
- 普通成员变量, 必须在构造函数中显式初始化,未初始化的普通成员变量值是未定义的(包含随机值)
- 静态成员变量是类级别的,需要在类外初始化,自动初始化为 0 或 NULL(指针类型),静态变量初始化只执行一次
- const变量必须在构造函数的初始化列表中初始化,初始化后不可修改
- C++11 允许为 非静态成员变量 提供类内初始值。静态成员变量仍需在类外初始化。
- 成员变量按 声明的顺序 初始化,与初始化列表的顺序无关。
变量作用域
全局变量跨.cpp文件,链接多文件共享;静态变量只能被当前.cpp文件访问;局部变量作用域为函数;类成员变量需要在类内或对象中访问,随着对象的销毁而销毁。
变量修饰
static, 静态变量
const,表示常量,不可改变量。修饰函数表示函数不可能修改类成员变量
volatile,告诉编译器不要对该变量进行优化。常用于 多线程编程 或 硬件编程,避免编译器对变量的访问进行优化。
extern,声明变量或函数在其他文件中定义。它不分配存储空间,只是告诉编译器该变量在其他地方定义过。链接时全局变量在多个文件共享。
mutable 用来修饰类中的成员变量,即使类的对象是 const 类型,成员变量仍然可以被修改。
JAVA
JAVA所有变量和函数都是写到类中,类名和文件名相同。main函数是在主类中定义的static函数。JAVA没有全局变量,因为所有变量都是在类中。
JAVA基本类型变量存储在栈中,引用类型变量存储在堆中。引用类型变量使用new创建,jvm的垃圾回收模块负责对象回收。
JAVA的变量都会初始化,不会有随机值。
类静态变量和静态模块随着类加载而初始化和执行,如果某类无须被加载,则不会执行该类中的静态变量和静态块。
- 类的加载通常是在访问类的静态成员(如静态字段、静态方法)或创建类的实例时触发的。
- 类加载器会在需要时加载类,不会提前加载。
变量访问控制
Java中,如果需要使用其他包中的类,必须利用import显式地导入它们。import 语句只能导入类,而不能导入方法、字段
Java 的访问控制包括四种权限:
- public:类或成员可以被任何类访问。
- protected:类或成员可以被同一个包中的类或子类访问。
- default(无修饰符):类或成员只能被同一个包中的类访问。
- private:类或成员只能在当前类内部访问。
包名通常采用 小写字母,以避免与类名的命名冲突。
包名通常使用 反向域名,例如 com.example.myapp。这种做法避免了不同公司或组织的包名冲突。
变量修饰
除了类,java的变量和函数也有访问修饰符
1 | // public:变量可以被任何其他类访问,无论它们在同一包中还是不同的包中。 |
static,声明为 static 为静态变量,属于类。
final,声明为 final 的变量是常量,初始化之后不能再修改。final修饰函数表示该函数不能被重写
volatile 变量会告诉 Java 虚拟机(JVM)每次访问该变量时都要从内存中读取,而不是从缓存中读取。它通常用于多线程编程中,确保变量的可见性。
synchronized 主要用于方法,确保同一时刻只有一个线程能执行该方法。
native 关键字用于声明本地方法,这些方法通过 JNI(Java Native Interface)调用非 Java 语言(如 C、C++)编写的代码。native通常用于方法声明。
Go
栈变量指局部变量、以及值语义的数据结构(int, float, bool, struct)。当变量的生命周期仅限于当前函数时,Go 会尽量将其分配到栈上。
堆内存,通常分配给引用类型(slice, map, channel, pointer)或者使用new创建变量且变量的地址被传递到函数外部。
Go 变量的访问控制。全局定义的小写字母只能包内访问,大写字母可以跨包访问。访问其他包的变量需要先import。
堆分配的变量由GC的垃圾回收模块负责回收。
Go变量相关的关键字,
- var:声明变量
- :=:简短声明变量
- const:声明常量
Go不提供继承,不提供静态变量
Python
Python的变量有自己的特别之处,比如,Python局部变量区域可以访问全局变量,但无法修改全局变量
1 | x = 10 |
if __name__ == "__main__"
函数里可以正常修改全局变量(其余函数不行
1 | x = 10 |
python无法对变量进行修饰,但可以通过@注解对函数进行修饰。例如@staticmethod,@classmethod
常见@注解
1 | @classmethod 装饰器定义类方法,允许方法访问类本身,而不是实例。 |
本文标题:编程语言(3)—变量和作用域
文章作者:Infinity
发布时间:2024-12-01
最后更新:2025-01-11
版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC-SA 3.0 CN 许可协议。转载请注明出处!