const
ANSI C允许声明常量,常量的样子和变量完全一样,只是它们的值不能被修改
int const a;
const int a;
这两个语句都是声明a为一个整型常量(注意与字面值常量区别),它的值不能被修改。使用const声明常量时如果不对对其初始化,该常量的值是随机值,不确定的。
如何使一个常量一开始拥有值:
1、在声明时对其进行初始化 const int a = 15;
2、函数中声明为const的形参在函数被调用时会得到实参的值。
那么声明的常量是否一定不可以改变了呢?
#include<stdio.h> int main() { const int a = 15; int *b = &a; *b = 20; printf("%d\n", a); return 0; }
编译运行输出结果:20
可以看到通过其他方式还是可以改变a的值为20的,a本质上还是局部变量,有地址空间,并不是常量,const这个关键字只是限定“a”这个符号不可以改变,但是并没有限定它的地址内容不可以改变
int const *pi;
const int *pi;
这两个语句都是声明pi为一个指向整型常量的指针,可以修改指针的值,但是你不能修改它所指向的值
#include<stdio.h> 2 int main() 3 { 4 const int a = 15; 5 const int *b = &a; 6 7 *b = 20; 8 b = NULL; 9 printf("%d\n", a); 10 return 0; 11 }
编译时,编译器提示constant.c:7:2: error: assignment of read-only location ‘*b’错误,编译不通过
其实这里只要记住 const 实际是修饰的*pi就可以了,*pi是指针的内容而不是指针本身;
int *const pi;
这里声明pi为一个指向整型的常量指针。此时指针是常量,它的值无法修改,但你可以修改它所指向的整型的值
#include<stdio.h> int main() { int a = 15; int c = 20; int *const b = &a; b = &c; printf("%d\n", a); return 0; }
同样编译时,编译器提示constant.c:8:2: error: assignment of read-only variable ‘b’错误,编译不通过
这里只要记住 const 实际是修饰的pi就可以了,pi是指针本身不允许改变而不是指针的内容
int const *const pi;
这里声明pi为一个指向整型常量的常量指针。无论指针还是它所指向的的值都是常量,不允许修改。
再看
typedef int* PINT
const PUINT a;
PUINt const a;
#include<stdio.h> typedef int * PINT; int main() { int a = 1; PINT b = &a; *b = 2; printf("%d\n", a); return 0; }运行结果:2
这里定义a是一个指向整型的常量指针,这里可以把PINT理解为向int一样的数据类型,而不要把PINT 做类似宏定义的简单替换int *
#define 宏定义是另一种创建名字常量的机制。
如:
#define MAX_ELEMENTS 50
const int max_elements = 50;
这种情况下,使用#define比使用const变量更好。因为只要允许使用字面值常量的地方都可以使用前者,比如声明数组的长度。const变量只能用于允许使用变量的地方
static extern auto register
链接属性
当组成一个程序的各个源文件分别编译之后,所有的目标文件以及那些从一个或多个函数库中引用的函数链接在一起,形成可执行程序。然而,如果相同的标示符出现在几个不同的源文件中时,标识符的链接属性(linkage)决定如何处理在不同文件中出现的标识符。标识符的作用域与它的链接属性有关,但这两个属性并不相同
链接属性一共有3中:external(外部)、internal(内部)、none(无)。
没有链接属性的标识符(none):总是被当作单独的个体,也就是说该标识符的多个声明被当作独立的不同实体
internal链接属性的标识符:在同一个源文件内的所有声明中都指向同一个实体,但位于不同源文件的多个声明则分属不同的实体
external链接属性的标识符:不论声明多少次,位于几个源文件都表示同一个实体
如:
typedef char * a;
int b;
int c(int d)
{
int e;
int f(int g);
...
}
缺省情况下,标识符b,c,f的链接属性为external,其余标识符链接属性则为none
关键字extern和static用于在声明中修改标识符的链接属性。如果某个声明(变量与函数)在正常情况下具有external链接属性,在它前面加上static关键字可以使它的链接属性变为internal。
static只对缺省链接属性为external的声明才有改变链接属性的效果。例如,你可以在上面的int e前面加上关键字static,但它的效果完全不一样,因为e的缺省链接属性不为external。
external关键字它为一个标识符指定external链接属性,这样就可以访问在其他任何位置定义的这个实体。
external用于源文件中一个标识符的第1次声明时,它指定该标识符具有external链接属性。但是,如果它用于该标识符的第2次或以后的声明是,它并不会更改由第1次声明所指定的链接属性
static int i; ---声明1
int func()
{
extern int i; ---声明2并不改变i的链接属性
...
}
存储类型
变量的存储类型(storage class)是指存储变量值的内存类型。变量的存储类型决定变量何时创建、何时销毁以及他的值保持多久。有三个地方可以用于存储变量:普通内存、运行时堆栈、硬件寄存器。
变量的缺省存储类型取决于它的声明位置。凡是在任何代码块之外声明的变量总是存储在静态内存中,也就是不属于堆栈的内存,这类变量称为静态(static)变量。对这类变量,你无法为他们指定其存储类型。静态变量在程序运行之前创建,在程序的整个执行期间始终存在。它始终保持原先的值,除非给它赋一个不同的值或者程序结束
在代码模块内部声明的变量的缺省存储类型是自动的(automatic),也就是说它存储在堆栈中,称为自动变量(auto)变量。有一个auto关键字就是用于修饰这种存储类型的,但它极少使用,因为代码块中的变量在缺省情况下就是自动变量。在程序执行到声明自动变量的代码块时,自动变量才会被创建,当程序的执行刘离开代码块时,这些自动变量便自行销毁。如一个函数被反复调用,这些自动变量都将自动重建。在代码块再次执行时,这些自动变量在堆栈中所占据的内存位置有可能和原先的位置相同,也可能不同。即使他们所占据的位置相同,你也不能保证这块内存同时不会有其他的用途。因此,可以说自动变量在代码块执行完毕后消失。当代码块再次执行时,它们的值一般并不是上次执行的值。
对于在代码块的内部声明的变量,如果给它加上关键字static,可以使它的存储类型从自动变为静态。具有静态存储类型的变量在整个程序执行过程中一直存在,而不仅仅在声明它的代码块执行时存在。但是,修改变量的存储类型并不表示修改该变量的作用域,它仍然只能在该代码块内部按名字访问。函数的形参不能声明为静态,因为实参总是在堆栈中传递给函数,用于支持递归。
关键字register可以用于自动变量的声明,提示它们应该存储于机器的硬件寄存器而不是内存中,这类变量称为寄存器变量。寄存器变量比存储在内存中的变量访问起来效率更高。
寄存器变量的创建和销毁时间和自动变量相同,但它需要一些额外的工作。在一个使用寄存器变量的函数返回之前,这些寄存器存储的值必须恢复,确保调用者的寄存器变量未被破坏。许多机器使用运行时堆栈来完成这个任务。当函数开始执行时,它把需要使用的所有寄存器的内容都保存到堆栈中,当函数返回时,这些值再复制回寄存器中
static关键字总结
当它用于函数定义时,或用于代码块之外的变量声明时,static关键字用于修改标识符的链接属性,从external改为internal,但标识符的存储类型和作用域不受影响。用这种方式声明的函数或变量只能在声明它们的源文件中访问
当它用于代码块内部的变量声明时,static关键字用于修改变量的存储类型,从自动变量修改为静态变量,但变量的链接属性和作用域不受影响。用这种方式声明的变量在程序执行前创建,并在程序的整个执行期间一直存在,而不是每次在代码块开始执行时创建,在代码块执行完毕后销毁。