C语言声明(存储类别,作用域,链接属性)

声明

声明的语法

一般地,声明具有下列形式:

				声明说明符 声明符;

声明说明符(declaration specifier)描述声明的变量或函数的性质。
声明符(declarator)给出了它们的名字,并且可以提供关于其性质的额外信息。
声明说明符分为以下 4 大类:

  • 存储类型
    存储类型一共有4种:auto、static、extern 和 register。
  • 类型限定符
    C89只有两种类型限定符:const 和 volatile;C99还有一个限定符 restrict;
  • 类型说明符
    包括:(void、char、short、int、long、float、double、signed、unsigned、struct、union、enum)
  • 函数说明符
    C99 新增:inline

在声明中最多可以出现一种存储类型;如果存储类型存在,则必须把它放置在最前面。
声明可以包含零个或多个类型限定符。
类型限定符和类型说明符必须跟随在存储类型后边,但是两者的顺序没有限制。

存储类型

变量的性质

C程序中的每个变量都具有以下 3 个性质。

  • 存储期限。变量的存储期限决定了为变量预留和内存被释放的时间。
    • 具有自动存储期限的变量在所属块被执行时获得内存单元,并在块终止时释放内存单元,从而会导致变量失去值。
    • 具有静态存储期限的变量在程序运行期间占有同一个的存储单元,也就允许变量无限期地保留它的值。
  • 作用域。变量的作用域是指可以引用变量的那部分程序文本。
    • 块作用域:变量从声明的地方一直到所在块的末尾都是可见的。
    • 文件作用域:变量从声明的地方一直到所在文件的末尾都是可见的。
    • 原型作用域:它只适用于在函数原型中声明的参数名,在原型中(与函数的定义不同),参数的名字可以省略。
    • 函数作用域:它只适用于语句标签,语句标签用于 goto 语句。基本上,函数作用域可以简化为一条规则:一个函数中的所有语句标签必须唯一。
  • 链接属性。变量的链接确定了程序的不同部分可以共享此变量的范围。
    • 具有外部链接的变量可以被程序中的几个(或许全部)文件共享。
    • 具有内部链接的变量只能属于单独一个文件,但是此文件中的函数可以共享这个变量。
    • 无链接的变量属于单独一个函数,而且根本不能被共享。

变量的默认存储期限、作用域和链接都依赖于变量声明的位置:

  • 在块(包括函数体)内部声明的变量具有自动存储期限、块作用域,并且无链接。
  • 在程序的最外层(任意块外部)声明的变量具有静态存储期限、文件作用域和外部链接。
int i; //具有静态存储期限、文件作用域和外部链接
void fun(void)
{
	int j; //具有自动存储期限、块作用域,并且无链接
}

可以通过指定明确的存储类型(auto、static、extern 和 register)来改变变量的性质。

auto存储类型

auto 存储类型只对属于块(用花括号括起来的多个语句)的变量有效。
auto 变量具有自动存储期限、块作用域,并且无链接。
auto 存储类型几乎从来不用明确地指明,因为对于在块内部声明的变量,它是默认的。

static存储类型

static 存储类型可以用于全部变量,而无需考虑变量声明的位置。
但是,作用于块外部声明的变量和块内部声明的变量时会有不同的效果:

  • 当用在块外部时,static 说明变量具有内部链接。
  • 当用在块内部时,static 把变量的存储期限从自动的变成了静态的。
int i; //具有静态存储期限、文件作用域和内部链接
void fun(void)
{
	int j; //具有静态存储期限、块作用域和无链接
}

static 变量 和 auto 变量的比较:

  • 块内的 static 变量只在程序执行前进行一次初始化;而 auto 变量则会在每次出现时进行初始化。
  • 每次调用函数时,它都会获得一组新的 auto 变量;但是,如果函数含有 static 变量,那么此函数的全部调用都可以共享这个 static 变量。
  • 虽然函数不应该返回指向 auto 变量的指针,但是函数可以返回指向 static 变量的指针。

extern存储类型

extern 存储类型使几个源文件可以共享同一个变量。
变量的 extern 声明不是定义,它只是提示编译器需要访问定义在别处的变量。

file1.c
int a = 3,b = 4;//在file1.c中定义(初始化)
 
file2.c                     
extern int a,b; //在file2.c中声明      
int main()
{
    printf("%d,%d\n",a,b);
}

extern 声明的变量始终具有静态存储期限,其作用域依赖于声明的位置。
在大多数情况下,变量会定义在另一个文件中,并且具有外部链接。

register存储类型

声明变量具有 register 存储类型就要求编译器把变量存储在寄存器中,而不是像其他变量一样保留在内存中。

函数的存储类型

和变量声明一样,函数声明的(和定义)也可以包括存储类型,但是选项只有 extern 和 static。
在函数声明开始处的单词 extern 说明函数具有外部链接,也就是允许其他文件调用此函数。
如果不指明函数的存储类型,那么会假设函数具有外部链接。
在函数声明开始处的单词 static 说明函数具有内部链接,也就是说只能在定义函数的文件内部调用此函数。

类型限定符

const 限定符

任何变量的声明都可以使用 const 限定符限定,该限定符指定变量的值不能被修改。
const 对象必须声明的同时初始化。

const int buffsize = 255;

初始值可以是任意复杂表达式:

int i = 42;
const int ci = i;
const int j = get_size();

默认状态下,const 对象仅在文件内有效。
如果想在多个文件之间共享 const 对象,在变量的定义和声明之前都添加 extern 关键字。

文件1extern const int buffsize = fcn(); //该常量能被其他文件访问
文件2extern const int buffsize;         //使用其它文件的常量

指向常量的指针

要想存放常量的地址,只能使用指向常量的指针(pinter to const)。
指向常量的指针不能用于改变其所指对象的值。

const double pi = 3.14;
const double* ptr = π

允许指向常量的指针指向一个非常量的对象:

double pi = 3.14;         
const double* cptr = π //不能通过*cptr改变pi的值

常量指针

允许把指针本身定位常量。
常量指针(const pointer)必须初始化,而且一旦初始化完成,它的值就不能再改变了。
把 * 放在 const 关键字之前用以说明指针是一个常量。

int errNumb = 0;
int* const curErr = &errNumb; //cuErr将一直指向errNumb

指向常量的常量指针

const double pi = 3.14159;
const double* const pip = π //指向常量对象的常量指针。

受限指针

在C99中,关键字 restrict 可以出现在指针的声明中:

int* restrict p;

用 restrict 声明的指针叫做受限指针(restricted pointer)。
这样做的目的是,如果指针 p 指向的对象在之后需要修改,那么该对象不会允许通过除指针 p 之外的任何方式访问。
其他访问对象的方式包括让另一个指针指向同一个对象,或者让指针 p 指向命名变量。

初始化

具有静态存储期限的变量的初始化必须是常量。
具有静态存储期限的变量默认情况的值为零。
具有动态存储期限的变量的初始化必须可以是变量也可以是常量。
具有动态存储期限的变量没有默认的初始值。
包含在花括号中的数组、结构或联合的初始化必须只包含常量表达式。
自动类型的结构或联合的初始化可以是另外一个结构或联合。

你可能感兴趣的:(C语言,c语言,存储类别,作用域,限定符,链接)