const,volatile,static,typdef,几个关键字辨析和理解
1、const类型修饰符
const它限定一个变量初始化后就不允许被改变的修饰符。使用const在一定程度上可以提高程序的安全性和可靠性。它即有预编译命令的优点也有预编译没有的优点。const修饰的变量被编译器处理只读变量(不是常量,常量是放到内存的只读区域的)放在内存中,由编译器限定不允许改变。
例如:const int Max=100; Max++会产生错误;
(2)便于进行类型检查,使编译器对处理内容有更多了解,消除了一些隐患。
例如: void f(const int i) { .........} 编译器就会知道i是一个不允许修改的变量;
(3)可以避免意义模糊的数字出现,同样可以很方便地进行参数的调整和修改。 同宏定义一样,可以做到不变则已,一变都变!
如(1)中,如果想修改Max的内容,只需要:const int Max=you want;即可!
例如: void f(const int i)
(6) 可以节省空间,避免不必要的内存分配。 例如:
#define PI 3.14159 //常量宏放入只读内存中 const double Pi=3.14159; //此时并未将Pi放入ROM中 ......
double i=Pi; //此时为Pi分配内存,以后不再分配!
double I=PI; //编译期间进行宏替换,分配内存
double j=Pi; //没有内存分配
double J=PI; //再进行宏替换,又一次分配内存!
const定义常量从汇编的角度来看,只是给出了对应的内存地址,而不是像#define一样给出的是立即数,所以,const定义的常量在程序运行过程中只有一份拷贝,而#define定义的常量在内存中有若干份拷贝。
(6) 提高了效率。
编译器通常不为普通const常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有了存储与读内存的操作,使得它的效率也很高。(参考百度百科)
2、volatile类型修饰符
用来修饰被不同线程访问和修改的变量(即随时会被意想不到的改变)。volatile的作用是作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值。
int square(volatile int *ptr)
{
return ((*ptr) * (*ptr));
}
int square(volatile int* &ptr)//这里参数应该申明为引用,不然函数体里只会使用副本,外部没法更改
{
int a,b;
a = *ptr;
b = *ptr;
return a*b;
}
long square(volatile int*ptr)
{
int a;
a = *ptr;
return a*a;
}
3、static类型修饰符
当一个进程的全局变量被声明为static之后,它就是静态全局变量。静态全局变量和其他的全局变量的存储地点并没有区别,要说有区别就是static是在.data段因为编译器会在你未初始化时自动初始化为0,而普通变量已初始化的变量在data段或者未初始化在.bss段内;static变量它只在定义它的源文件内有效,其他源文件无法访问它。普通全局变量extern后,它就可以被其他源文件及其函数访问,而static变量是无法extern 因为编译器在其他源文件中看不到被static修饰的变量。
1.用在全局变量上时例如
1 //test.c
2 #include <stdio.h>
3 #include "test.h"
4
5 static char str[] = "read ok !";
6 int test()
7 {
8 printf("static is %s\n",str);
9 return 0;
10 }
主函数:
1 //main.c
2
3 #include <stdio.h>
4 #include "test.h"
5
6 char str[]="is not ok!";
7
8 int main()
9 {
10 test();
11 printf("static is %s\n",str);
12 return 0;
13 }
上面程序输出的结果是明显的,但如果去掉test.c内的str的static修饰符后编译时链接会出错,因为是有两个地方存在相同的变量,导致编译器编译时无法识别应该使用哪一个。但当你用static 修饰test.c内的str变量后编译器就知道在那个源文件内该使用哪一个。
2,在局部变量上使用时
普通的局部变量在栈空间上分配,这个局部变量所在的函数被多次调用时,每次调用这个局部变量在栈上的位置都不一定相同。局部变量也可以在堆上动态分配,但是记得使用完这个堆空间后要释放。
static局部变量中文名叫静态局部变量。它与普通的局部变量比起来有如下几个区别:
1)位置:静态局部变量被编译器放在全局存储区.data(前面提到编译器会自动初始化),所以它虽然是局部的,但是在程序的整个生命周期中存在。
2)访问权限:静态局部变量只能被其作用域内的变量或函数访问。也就是说虽然它会在程序的整个生命周期中存在,由于它是static的,它不能被其他的函数或源文件访问。
3)值:静态局部变量如果没有被用户初始化,则会被编译器自动赋值为0,以后每次调用静态局部变量的时候都用上次调用后的值。这个比较好理解,每次函数调用静态局部变量的时候都修改它然后离开,下次读的时候从全局存储区读出的静态局部变量就是上次修改后的值。
例如:
1 #include "test.h"
2 #include <stdio.h>
3 int test()
4 {
5 int b = 0;
6 static int a;
7 printf("%d,%d\n",a++,b++);
8 return 0;
9 }
主函数:
1 #include <stdio.h>
2 #include "test.h"
3
4 int main()
5 {
6 test();
7 test();
8 test();
9 test();
10 test();
11 return 0;
12 }
结果:
0,0 注意这里验证了static变量的自动初始化
1,0
2,0
3,0
4,0
3.static函数
既然static变量在其他源程序中不可以访问,那用在函数前是否也有相同的功能呢?验证一下:
//test.c
#include "test.h"
#include <stdio.h>
static int test()
{
int b = 0;
printf("%d\n",b);
return 0;
}
int mytry()
{
test();
return 0;
}
头文件:
//test.h
#ifndef __TEST_H__
#define __TEST_H__
#include"test.h" static int test();
int mytry(); #endif
主函数:
//main.c
#include <stdio.h>
#include "test.h" int main()
{
//test();//加上这一句程序链接时会出错
mytry();
return 0;
}
结果你猜对了吗?所以static函数可以很好地解决不同原文件中函数同名的问题,因为一个源文件对于其他源文件中的static函数是不可见的。参考博客
4、typdef 或 #define 类型修饰符
(网上好多地方都有关于这两个关键字如何使用的讨论,本文只是简单罗列各自的特性。)
1.#define是预处理指令,通常用来替代常量(大写)在编译预处理时进行简单的替换,不作正确性检查,不关含义是否正确照样带入。即使你在末尾习惯性的写上 ; define 还是会乖乖的替换,幽默点说如果你爱浪爱自由#define是不二之选(但是记住出来混是要还的)
知乎上看到这样的代码:
#include <iostream>
#define start using namespace std;
int main(int argc, char *argv[]) {
#define end <<endl;}
#define print cout<< start
print"hello world!"
end 作者:DreamPiggy
链接:https://www.zhihu.com/question/29798061/answer/78916243
来源:知乎。
另一个例子:
#define Pi 3.1415926; (Pi*R*R ) 就会成为 (3.1415926;*R*R)
2.typdef 是由编译器处理的,由名字也能感觉到功能的一点差别,他是用于长类型 有一个剪短的名字而常常被使用,其实typdef创造的是一个类型(官方的解释是任何声明变量的语句前面加上typedef之后,原来是变量的都变成一种类型。不管这个声明中的标识符号出现在中间还是最后.)
如:
define PINT int* PINT a,b 就是 int *a;int b; 而 typedef PINT int* PINT a,b 就是 int *a,int *b;
常见用法:
- 用在C代码中,帮助struct。声明struct新对象时,必须要带上struct,即形式为: struct 结构名对象名,如:
struct tagPOINT1 {
int x; int y;
};
struct tagPOINT1 p1;
而在C++中,则可以直接写:结构名对象名,即:tagPOINT1 p1;
typedef struct tagPOINT
{
int x; int y;
}POINT; POINT p1; 简洁多了吧
- 用typedef来定义与平台无关的类型。
比如定义一个叫 FLOAT的浮点类型,在目标平台一上,让它表示最高精度的类型为:
typedef long double FLOAT;
在不支持 long double 的平台二上,改为:
typedef double FLOAT;
在连 double 都不支持的平台三上,改为:
typedef float FLOAT;
也就是说,当跨平台时,只要改下 typedef 本身就行,不用对其他源码做任何修改。
标准库就广泛使用了这个技巧,比如size_t。另外,因为typedef是定义了一种类型的新别名,不是简单的字符串替换,所以它比宏来得稳健。
- 还有就是类似#deine的作用
3.区别
- typedef是有作用域的,而#define不管怎么写都是全局的
- 细节上的差别
typedef int * pint ;
#define PINT int *
那么:
const pint p ;//p不可更改,但p指向的内容可更改
const PINT p ;//p可更改,但是p指向的内容不可更改。
两者的区别还很多。。。。。。在同样的功能下看个人喜好。
5、extern
- 默认情况下所有文件的变量都可以访问,只需要在定义变量时添加一个extern(extern int age)(而且没有分配内存)引用一下就行,这个不管int age是定义在哪个文件中都可以得到,而且此变量还可以被修改。
- extern引用的时候,优先找本文件夹,如果找不到再去其它文件夹。
2017.9.1
本博客起笔与于一个月前刚到实习地点,在实习最后一天再次继续完成。
const,volatile,static,typdef,几个关键字辨析和理解的更多相关文章
- let 、const 、var、function声明关键字的新理解
今天在群里看到大佬们讨论let .const 的提升问题,有个大佬问 三种声明都在什么阶段提升? 什么阶段?这个真不清楚,以前是只知道let.const存在死区,没有变量提升,一下子就懵了 后经手 ...
- const extern static 终极指南
const extern static 终极指南 不管是从事哪种语言的开发工作,const extern static 这三个关键字的用法和原理都是我们必须明白的.本文将对此做出非常详细的讲解. co ...
- php中$this、static、final、const、self 等几个关键字的用法
<?phpclass A { public static function get_self(){ return new self(); } public static function get ...
- php类中的$this,static,const,self这几个关键字使用方法
本篇文章主要分享一下关于php类中的$this,static,final,const,self这几个关键字使用方法 $this $this表示当前实例,在类的内部方法访问未声明为const及stati ...
- C语言关键字—-sizeof 、typedef、const、static、register、extern、#define
关键字:sizeof .#define.typedef.const.static.register.extern sizeof 1. 作用:求数据所占得内存空间大小 2. 本质:求数据得类型所占的内存 ...
- C++ static、const和static const类型成员变量声明以及初始化
C++ static.const和static const 以及它们的初始化 const定义的常量在超出其作用域之后其空间会被释放,而static定义的静态常量在函数执行后不会释放其存储空间. sta ...
- 今天思考一个问题,PHP const和static的区别
static关键字在类中是,描述一个成员是静态的,static能够限制外部的访问,因为static后的成员是属于类的,是不属于任何对象实例,其他类是无法访问的,只对类的实例共享,能一定程序对该成员尽心 ...
- const、static和extern的正确使用方式
我们在看一些大牛的第三方时,里面会出现很多const.static和extern,尤其是const和static,const和extern的结合使用,直接令很多小伙伴懵逼了,今天就详细讲解一下这三个关 ...
- 《OOC》笔记(1)——C语言const、static和extern的用法
<OOC>笔记(1)——C语言const.static和extern的用法 C语言中const关键字用法不少,我只喜欢两种用法.一是用于修饰函数形参,二是用于修饰全局变量和局部变量. 用c ...
随机推荐
- CSS3+JS完美实现放大镜模式
最近看到一篇讲放大镜的文章,实践后感觉效果非常好,这里分享给大家. 效果如下: 其实现核心: CSS函数,如:calc() -- 动态计算:var() -- 使用自定义变量 CSS伪元素:::befo ...
- 前端面试之HTTP
前端面试之HTTP的基本性质 1 HTTP代理 在浏览器和服务器之间,有许多计算机和其他设备转发了HTTP消息.简而言之,他们中间的部分就是代理! 代理的作用 缓存(可以是公开的也可以是私有的,像浏览 ...
- ETL优化(转载)
1.引言 数据仓库建设中的ETL(Extract, Transform, Load)是数据抽取.转换和装载到模型的过程,整个过程基本是通过控制用SQL语句编写的存储过程和函数的方式来实现对数据的直接操 ...
- c++ 三五法则 自己理解
简介 三五法则规定了什么时候需要 1 拷贝构造函数 2 拷贝赋值函数 3 析构函数 1. 需要析构函数的类也需要拷贝构造函数和拷贝赋值函数. 通常,若一个类需要析构函数,则代表其合成的析构函数 ...
- luogu p2622
题目描述 现有n盏灯,以及m个按钮.每个按钮可以同时控制这n盏灯--按下了第i个按钮,对于所有的灯都有一个效果.按下i按钮对于第j盏灯,是下面3中效果之一:如果a[i][j]为1,那么当这盏灯开了的时 ...
- Dockerfile,Dockerfile 参考文档
Dockerfile,Dockerfile 参考文档 1.Dockerfile 1.1Usage 1.2Format 1.3Parser directives 1.4escape 1.5Environ ...
- 文本处理三剑客简介(grep、awk、sed)
本章内容: 命令 描述 awk 支持所有的正则表达式 sed 默认不支持扩展表达式,加-r 选项开启 ERE,如果不加-r 使用花括号要加转义符\{\} grep 默认不支持扩展表达式,加-E 选项开 ...
- (7)Linux使用注意事项
1.Linux 严格区分大小写 和 Windows 不同,Linux 是严格区分大小写的,包括文件名和目录名.命令.命令选项.配置文件设置选项等. 2.Windows 下的程序不能直接在 Linux ...
- Thank in Java
Think in Java 2.一切都是对象 2.1 引用操作对象 Java 中一切都是对象,因此可以采用单一固定得语法. 操作对象得标识符实际上是对对象得一个 "引用"refer ...
- 深入理解Js中的this
深入理解Js中的this JavaScript作用域为静态作用域static scope,但是在Js中的this却是一个例外,this的指向问题就类似于动态作用域,其并不关心函数和作用域是如何声明以及 ...