资源预览内容
第1页 / 共9页
第2页 / 共9页
第3页 / 共9页
第4页 / 共9页
第5页 / 共9页
第6页 / 共9页
第7页 / 共9页
第8页 / 共9页
第9页 / 共9页
亲,该文档总共9页全部预览完了,如果喜欢就下载吧!
资源描述
C 语言之 const 与 static详解看 到 const 关键字,很多人想到的可能是const 常量,其实关键字const 并不能把变量变成常量! 在一个符号前加上const 限定符只是表示这个符号不能被赋值。 也就是它的值对于这个符号来说是 只读的,但它并不能防止通过程序的内部(甚至是外部 )的方法来修改这个值 (C 专家编程 .p21) 。也 就是说 const 变量是只读变量,既然是变量那么就可以取得其地址,然后修改其值。看来const 也 是防君子不防小人啊! :) const 使用情况分类详析1、const 的普通用法 const int n = 10;意思很明显, n 是一个只读变量,程序不可以直接修改其值。这里还有一个问题需要注意,即如下使 用:int an;在 ANSI C 中,这种写法是错误的,因为数组的大小应该是个常量,而n 只是一个 变量。2、const 用于指针 const int *p; int const *p; int * const p;在最后的一种情况下,指针是只读的(即 p 只读),而在另外两种情况下,指针所指向的对象是只读 的(即*p 只读)。const 是一个左结合的类型修饰符,它与其左侧的类型修饰符一起为一个类型修饰 符,所以, int const 限定 *p, 不限定 p。int *const 限定 p, 不限定 *p 。这里有一个简便的区分方 法:沿着* 号划一条线,如果const 位于* 的左侧,则 const 就是用来修饰指针所指向的变量,即指针指 向为常量;如果 const 位于* 的右侧, const 就是修饰指针本身,即指针本身是常量。3、const 用于函数的地址传递参数void foo(const int *p) 这种形式通常用于在数组形式的参数中模拟传值调用。也就是相当于函数调用者声称:“我给你一个 指向它的指针, 但你不能去修改它。 “如果函数编写者遵循了这个约定,那么就相当于模拟了值传递。 这也是 const 最有用之处了:用来限定函数的形参,这样该函数将不会修改实参指针所指的数据。 这里注意了,是函数不应该去修改而不是不能修改, 也就是说 const 不能阻止参数的修改 (原因见上 ) 。4、const 用于限定函数的返回值const int foo(); const struct mytype foo(); 上述写法限定函数的返回值不可被更新,当函数返回内部的类型时, 已经是一个数值, 当然不可被赋 值更新,所以,此时const 无意义,最好去掉,以免困惑。当函数返回自定义的类型时,这个类型 仍然包含可以被赋值的变量成员,所以,此时有意义。一、宏定义:主要是一些语法问题和技巧例如:#define FIND(s,e) (size_t) const int b;/错误,常量必须初始化int a = 10,b = 9; const int *p1 = /指针指向的内容只读,不能通过该指针去写*p1 = 11;/错误int * const p2 = /指针本身只读,指针初始化到一个对象后,将不能被修改p2 = /错误const int *p3 const = / 指针本身和指向的内容都是只读const char *fp1(void) /修饰返回值,表示返回的指针指向内容只读 char *p = “dddd“; return p; void fp1(const char *str) *str = 4; /错误const char *p = str;/p必须为 const ,才能接受 str int _tmain(int argc, _TCHAR* argv) const char *d = fp1(); printf(“%s“,d); 三、extern 用法:在别的文件中定义的变量,要想在本文件中使用,必须先用extern 声明,例如: extern a;之后就当成在本文件中定义的变量一样使用。四、static用法:1. 修饰变量,从生存域和访问域两个方面说明, 无论 static变量定义在函数内或外, 该变量都位于数 据段中;定义于函数体外的static变量的访问域仅仅是它所在文件中定义的函数,其他文件无法通 过 extern 对其声明后访问。2. 修饰函数,使得函数的访问域仅仅为其所定义的函数。3. 类中变量用 static修饰表示变量是类变量,类中函数用static修饰表示函数只能访问类中的 static变量,不接受 this指针,称为类函数。例如:/file1 void fstatic(void); static void fstatic(void) return; /file2 void fstatic(void); Main() fstatic();/声明仍然无法调用 总之, static实现了 c 语言的封装性,一定程度上实现了信息的封装和隐藏。五、sizeof用法: sizeof是一个运算符,编译器在编译时就能确定其的运算结果,其运算对象可以 是内部数据类型名、 struct类型名、 class类型名、数组变量和其他类型的变量,只有计算对象是 数组变量时,能得出数组实际的长度, 其他类型的变量作为运算对象时, 都将退化为对该对象的数据 类型的运算。另外,在计算struct和 class 等用户自定义数据类型的大小时,需要考虑编译器会对 其做优化处理, 根据一定的原则调整, 即会自动对齐,sizeof的结果往往会大于各个元素长度的和。例一:short l;/sizeof(l)=2 char *a = “abcddd“;/sizeof(a)=4 void *s = malloc(100);/sizeof(s)=4 char b = “abcddd“;/sizeof(b)=7*1 float c20;/sizeof(c)=20*4; 例二:struct A short a; short b; short c; ;/sizeof(A)=6 struct B int a; char b; short c; ;/sizeof(A)=8 struct C double a1; long a; int a2; char b; ;/sizeof(B)=24 #pragma pack(1)/使用 pack 指令强制编译器不做优化对其,这种方式一般用于这个struct D/结构体要直接写入文件或者直接用于网络收发的情况下 double a1; long a; int a2; char b; ; #pragma pack()/sizeof(B)=17 说明:例二中演示了数据对其的情况,由于 CPU 访问数据的特点是一次访问多个字节,故如果多字节 数据的首地址是 2 的整数倍的话, 将可以一次内存访问即可取得其所对应的所有数据,所以一个优化 要求就是变量的地址是其数据类型长度的整数倍,例如int a的 a 的地址要求是 4 的整数倍。针对结构体,如果结构体中所有元素类型相同,作为数组处理,入struct A;否则一般都是其最常 元素的整数倍,例如struct B和 C 。这样处理的目的是考虑定义结构体数组的情况,例如struct B b10; 那么这 10 个结构体变量如果每个长度都是8,将能保证每个变量的第一个元素都能是4的整 数倍,否则将达不到这个效果, 不能实现高效的内存访问, 故编译器将对不符合要求的结构体自动调 整数据对齐。最后需要交代的是, class 中的 static变量不被分配到栈上,所以不能计入sizeof中,空类的长度 是 1,有虚函数的长度为4,因为包含一个指向函数表的指针。下面分析几个面试题:例一:int fp1( char var)/ 返回值为 4,因为 var 作为参数传递时,已经退化为普通指针,/ 故其大小就是指针的大小,不作为数组名处理,无法计算出数组长度。return sizeof(var); 例二:/str1是一个数组名,该数组是一个指针数组,长度为3,故 sizeof(str1)为 3*4 void fp1(void) char *str1 = “hello“,“mico“,“china“; for(int i=0;i sizeof(str1)/sizeof(char *);i+) printf(“%s“,str1i); 六、volatile的用法:该词的意思是”易变的”, 用于修饰变量的一个关键字,表示该变量在很多地 方都能被改变,会被意象不到的改变,编译器不能对其优化,往往用于多任务系统或嵌入式系统中, 情况一:嵌入式系统中的很多外设寄存器的值会实时改变,如:#define PTA *(volatile unsigned char *)(0x00000001);情况二:嵌入式系统内存中的某些变量有可能被中断程序修改;情况三:多 任务系统中的共享变量可能随时改变。满足这些特点之一的变量必须要用volatile修饰,保证编译 器不能对其优化处理,如果被优化,往往程序的执行结果出错。例一:int a = 10; if(a = 10) a = 11; 这短代码正常被优化为a = 11; 但是,如果 volatile int a;则这段代码将不被优化。例二:/ 原始代码:int square(volatile int *ptr) return *ptr * *ptr; / 编译器优化完的代码:int square(volatile int *ptr) int a = *ptr; int b = *ptr; return a*b; / 正确代码:由于 *ptr的值随时可能改变,故优化代码中的a 和 b 的值可能不一样,故计int square(volatile int *ptr) /算的结果可能有误 int a = *ptr; return a*a; static有两种用途:一是修饰变量,二是修饰函数第一:修饰变量 例子: staticint a; void Func() staticint b; 全局变量默认的存储类型是extern ,若不加 static修饰,在不同头文件中定义名字相同的全局 变量会发生冲突。 static修饰符是一个能够减少这类命名冲突的有用工具。例如,以下声明语句 static int a; 其含义与下面的语句相同: int a; 只不过, a 的作用域限制在一个源文件内,对于其他源文件,a 是不可见的。因此,如果若干个函数 需要共享一组外部对象, 可以将这些函数放到一个源文件中,把它们需要用到的对象也都在同一个源 文件中以 static修饰符声明。 变量 b 是个局部变量, 在程序退出函数 Func 后,b 就不能被使用。 但是当程序再次进入函数Func 时,变量 b 保持上次运行后的值。第二:修饰函数 static修饰符不仅适用于变量,也适用于函数。一个函数,其默认存储状态也是extern 。如果 函数 f 需要调用另一个函数g,而且只有函数f 需要调用函数 g,我们可以把函数f 与函数 g 都放到 同一个源文件中,并且声明函数g 为 static: staticint g(int x) / g函数体 void f() / 其他内容b=g (a); 我们可以在多个源文件中定义同名的函数g,只要所有的函数g 都被定义为 static,或者仅仅只 有其中一个函数 g 不是 static。因此,为了避免可能出现的命名冲突,如果一个函数仅仅被同一个 源文件中的其他函数调用,我们就应该声明该函数为static。附一篇对变量存储类型解释的帖子: 如果说得规范一点, C/C+没有全局变量,只有外部和自动。C
网站客服QQ:2055934822
金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号