jjzjj

static和const的作用与区别

李奇同学喵喵呜~ 2025-02-11 原文

const定义的常量在超出其作用域之后其空间会被释放,而static定义的静态常量在函数执行后不会释放其存储空间。

我们先来说static.

 

static主要有三个作用:

1.修饰局部变量,成为静态局部变量

2.修饰全局变量,成为静态全局变量

3.修饰函数,成为静态函数

我们一个一个来解释.

1.修饰局部变量。成为静态局部变量

我们先来看下面这一段程序:

#include<stdio.h>
void test()
{
	int a = 5;
	a++;
	printf("%d", a);
}
int main()
{
	int i = 0;
	while (i < 10)
	{
		test();
		i++;
	}
}

输出结果是多少呢?

我们看到主函数一个循环是循环10次test函数,然后每执行一次test,都会打印一次a,a从6开始每次都+1(因为a++在前面,所以从6开始).

所以结果应该是6 7 8 9 10 11 12 13 14 15

那结果是不是如我们所想的呢?

我们来看一下运行结果:

这...?好像不符合我们的猜想诶。

为什么会出现10个6呢?

我们仔细想一下,便可以大致知道问题所在。

就是每一次进入循环的时候a都被重置了,所以不会一直加下去。

这是为什么呢?

因为a是我们所创建的一个临时变量,它会随着函数的结束而被销毁,下次再进来时,会被重新创建。

所以会造成我们刚才那种结果。

那我们就要输出那样6-15这样的结果该怎么做呢?

我们可以在test函数里面int前面加上一个static

这样可以让这个局部变量随着函数的结束而不会被销毁,原理稍后会解释的

改成如下:

void test()
{
	static int a = 5;
	a++;
	printf("%d ", a);
}

这样我们再运行一次,来看看结果吧

符合我们的猜想了。

那具体为什么是这样呢?这里就要说到数据在内存中存储的三个区域

栈区   堆区  静态区

栈区:存放局部变量和函数参数等的地方。栈区的作用范围过了之后会自动回收栈区分配的内存,不需要手动管理。

堆区:由比如malloc,realloc等函数所主动申请的内存,使用完之后须用free释放内存,若申请完之后忘记释放内存,则很容易造成内存泄漏。

静态区:静态变量和全局变量所存储的区域,一旦静态区的内存被分配,直到程序全部结束之后才会被释放。

静态区这个换而言之:静态区的生命周期和程序的生命周期是一样的,出了作用范围不会被销毁(上面static int a =5)这个例子,相当于作用范围不变,但生命周期延长了。

那么上面那个例子我们也可以进行解释原理了:

static修饰局部变量时,实际改变的是变量的存储位置,原来在栈区,被修饰后放在了静态区。

所以说除了作用范围之后不会被销毁。

2.static修饰全局变量

现在vs编译器里面创建两个源文件:test1.cpp,test2.cpp

我们首先在文件test1.cpp里面创建一个全局变量,如下:

 

 紧接着,我们知道引用别的文件里的全局变量需要extern这个函数,所以我们再test2.cpp这个文件里引用一下这个全局变量。

下面看输出结果:

 

 完美的输出了外部的文件。

但是如果我们再test1.cpp里面的全局变量前面加上static,我们可以看一下程序能否成功运行。

我们运行这段程序:

 

程序出错了.

我们看错误原因:无法解析的外部符号。

这是为什么啊,我们明明已经用了extern这个函数来调用外部文件里的那个全局变量了啊,为什么无法解析呢?

 

 那肯定是static捣的鬼嘛,它在这里发挥了什么作用呢?

一个全局变量本来是具有外部属性的,但是被static修饰后,外部属性就变成了内部连接属性,只能在自己所在的源文件内使用,不能在其他文件内使用。

这样呢,被static修饰后的全局变量给我们的感觉是作用域变小了(只能在自己文件内部使用,不能在别的文件内使用),但实质是链接属性变了。

3.static修饰函数

这个和那个修饰全局变量的类似。

还是创建两个源文件test1.c、test2.c。(此时文件名后缀为.c,不是.cpp,在c++环境中无法看到这种效果)

我们在test1.cpp文件里写如下的段程序:

 

 紧接着我们在test2.c里面写Add函数内容。

 

结果:

 完美的输出了我们想要的结果。

但如果我们在test2.c里面的Add函数里面加上static:

再次运行 :

发生错误。

错误原因也是无法解析的外部符号。

错误呢,和上个修饰全局变量一样:

 一个函数本来是具有外部连接属性的,但是被static修饰后,外部链接属性就变成了内部连接属性,只能在自己所在的源文件内部使用,不能在其他文件内部使用了。

再来说一下const

如果把const放在变量类型名前,说明这个变量的值是保持不变的,该变量必须在定义时初始化,初始化后对它进行的任何赋值都是非法的。

1.const修饰常变量
例如:

int a=5;

a=6;

此时a的值被修改为6。

const int a=5;

 此时变量a的值便可再被修改

若仍写a=6,则程序便会出现错误。

2.const修饰常量静态字符串

例如:

const char* str="fdsafdsa";

如果没有const的修饰,我们可能会在后面有意无意的写str[4]=’x’这样的语句,这样会导致对只读内存区域的赋值,然后程序会立刻异常终止。有了const,这个错误就能在程序被编译的时候就立即检查出来,这就是const的好处。让逻辑错误在编译期被发现。

3.修饰函数的参数

直接举例子说明:

1.防止指针修改指针指向的内容内容

我们写以下函数

void String(char* str1,const char* arr2);

这个时候我们可以在String这个函数里面修改arr1内容,但是如果修改arr2程序便会报错,此时arr2不可再被修改。

或:

2.防止指针修改指针指向的地址

void Swap(int* const p1,int* const p2);

此时p1和p2指向的地址便不可再被修改。

以上就是static和const的大致区别,当然会有很多的遗漏之处,欢迎补充!

有关static和const的作用与区别的更多相关文章

  1. ruby-on-rails - 如果 Object::try 被发送到一个 nil 对象,为什么它会起作用? - 2

    如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象

  2. ruby - 触发器 ruby​​ 中 3 点范围运算符和 2 点范围运算符的区别 - 2

    请帮助我理解范围运算符...和..之间的区别,作为Ruby中使用的“触发器”。这是PragmaticProgrammersguidetoRuby中的一个示例:a=(11..20).collect{|i|(i%4==0)..(i%3==0)?i:nil}返回:[nil,12,nil,nil,nil,16,17,18,nil,20]还有:a=(11..20).collect{|i|(i%4==0)...(i%3==0)?i:nil}返回:[nil,12,13,14,15,16,17,18,nil,20] 最佳答案 触发器(又名f/f)是

  3. ruby-on-rails - `a ||= b` 和 `a = b if a.nil 之间的区别? - 2

    我正在检查一个Rails项目。在ERubyHTML模板页面上,我看到了这样几行:我不明白为什么不这样写:在这种情况下,||=和ifnil?有什么区别? 最佳答案 在这种特殊情况下没有区别,但可能是出于习惯。每当我看到nil?被使用时,它几乎总是使用不当。在Ruby中,很少有东西在逻辑上是假的,只有文字false和nil是。这意味着像if(!x.nil?)这样的代码几乎总是更好地表示为if(x)除非期望x可能是文字false。我会将其切换为||=false,因为它具有相同的结果,但这在很大程度上取决于偏好。唯一的缺点是赋值会在每次运行

  4. ruby - 这两个 Ruby 类初始化定义有什么区别? - 2

    我正在阅读一本关于Ruby的书,作者在编写类初始化定义时使用的形式与他在本书前几节中使用的形式略有不同。它看起来像这样:classTicketattr_accessor:venue,:datedefinitialize(venue,date)self.venue=venueself.date=dateendend在本书的前几节中,它的定义如下:classTicketattr_accessor:venue,:datedefinitialize(venue,date)@venue=venue@date=dateendend在第一个示例中使用setter方法与在第二个示例中使用实例变量之间是

  5. ruby - Sinatra set cache_control to static files in public folder编译错误 - 2

    我不知道为什么,但是当我设置这个设置时它无法编译设置:static_cache_control,[:public,:max_age=>300]这是我得到的syntaxerror,unexpectedtASSOC,expecting']'(SyntaxError)set:static_cache_control,[:public,:max_age=>300]^我只想将“过期”header设置为css、javaascript和图像文件。谢谢。 最佳答案 我猜您使用的是Ruby1.8.7。Sinatra文档中显示的语法似乎是在Ruby1.

  6. spring.profiles.active和spring.profiles.include的使用及区别说明 - 2

    转自:spring.profiles.active和spring.profiles.include的使用及区别说明下文笔者讲述spring.profiles.active和spring.profiles.include的区别简介说明,如下所示我们都知道,在日常开发中,开发|测试|生产环境都拥有不同的配置信息如:jdbc地址、ip、端口等此时为了避免每次都修改全部信息,我们则可以采用以上的属性处理此类异常spring.profiles.active属性例:配置文件,可使用以下方式定义application-${profile}.properties开发环境配置文件:application-dev

  7. ruby - 这两段代码有什么区别? - 2

    打印1:defsum(i)i=i+[2]end$x=[1]sum($x)print$x打印12:defsum(i)i.push(2)end$x=[1]sum($x)print$x后者是修改全局变量$x。为什么它在第二个例子中被修改而不是在第一个例子中?类Array的任何方法(不仅是push)都会发生这种情况吗? 最佳答案 变量范围在这里无关紧要。在第一段代码中,您仅使用赋值运算符=为变量i赋值,而在第二段代码中,您正在修改$x(也称为i)使用破坏性方法push。赋值从不修改任何对象。它只是提供一个名称来引用一个对象。方法要么是破坏性

  8. ruby - Ruby 中 .next 和 .succ 的区别 - 2

    Ruby中的Fixnum方法.next和.succ有什么区别?看起来它的工作原理是一样的:1.next=>21.succ=>2如果有什么不同,为什么有两种方法做同样的事情? 最佳答案 它们是等价的。Fixnum#succ只是Fixnum#next的同义词。他们甚至在thereferencemanual中共享同一block. 关于ruby-Ruby中.next和.succ的区别,我们在StackOverflow上找到一个类似的问题: https://stacko

  9. ruby - 在参数为 `yield self` 的方法中使用 `&block` 和在没有参数 `yield self` 的方法中使用 `&block` 有什么区别吗? - 2

    我明白了defa(&block)block.call(self)end和defa()yieldselfend导致相同的结果,如果我假设有这样一个blocka{}。我的问题是-因为我偶然发现了一些这样的代码,它是否有任何区别或者是否有任何优势(如果我不使用变量/引用block):defa(&block)yieldselfend这是一个我不理解&block用法的具体案例:defrule(code,name,&block)@rules=[]if@rules.nil?@rules 最佳答案 我能想到的唯一优点就是自省(introspecti

  10. ruby-on-rails - "assigns"在 Ruby on Rails 中有什么作用? - 2

    我目前正在尝试学习RubyonRails和测试框架RSpec。assigns在此RSpec测试中做什么?describe"GETindex"doit"assignsallmymodelas@mymodel"domymodel=Factory(:mymodel)get:indexassigns(:mymodels).shouldeq([mymodel])endend 最佳答案 assigns只是检查您在Controller中设置的实例变量的值。这里检查@mymodels。 关于ruby-o

随机推荐