C语言存储类别、链接、作用域(详细介绍)

C语言存储类别、链接、作用域(详细介绍),第1张

目录


一、作用域、链接、存储期

1.1 作用域

1)块作用域

2)文件作用域

3)函数作用域

4)函数原型作用域

1.2 链接

1.3存储期

1)静态存储期

2)线程存储期

3)自动存储期

4)动态分配存储期


二、动态分配存储期

2.1存储类型

1)自动变量

2)寄存器变量

3)静态变量

2.2静态变量

1)块作用区域的静态变量

2)外部链接的静态变量

3)内部链接存储变量

2.3存储类别说明符

2.4存储类别和函数



一、作用域、链接、存储期 1.1 作用域

作用域:用于描述程序中可访问标识符的区域。


作用域的分类:

                      1)块作用域

                      2)文件作用域

                      3)函数作用域

                      4)函数原型作用域

1)块作用域

块:用一对花括号括起来的代码区域。


例如,整个函数体就是一个块,函数中的任意复合语句也是一个块。


定义在块中的变量具有块作用域。


块作用域的可见范围:从定义处到包含该定义的块的末尾。


注:虽然函数的形式参数声明在函数的左花括号之前,但是它们也具有块作用域,属于函数体这个块。


代码一:

double blocky(double cleo)
{
    double patrick = 0.0;
    int i;
    int h;
    for (i = 0; i < 10; i++)
    {
        double q = cleo * i;  //  q的作用域开始
        int h;
        ...
        patrick *= q;
    }   
    int j;                 //   q的作用域结束
    ...
    return patrick;    
}

        以上代码一中,cleo和patrick,q等都具有块作用域,但q的作用域仅限于内层块,只用内层块中的代码才能访问q。


也就是说上面的代码int j;如果写成int j = q;是错误的,应为q的作用域只限于内层代码块,即它从它声明的开始处到它所属块的右花括号之间。


注:即使有些代码块没有用花括号括起来,也算是块的一部分。


例如for循环、while循环、do while循环和if语句所控制的代码。


代码二:

for(int i = 0; i < 10; i++)  //重新声明的变量i
    printf(“%d\n”, i);

        上面代码块二for循环中的变量i被视为for循环块的一部分,它的作用域仅限于for循环。


一旦程序离开for循环,就不能再访问i。


        而代码块一中for循环中的变量i,离开for循环还能被访问,因为它是外层代码块中的变量,作用于整个块,不是for循环声明的变量。


注:如果内层代码块有一个标识符的名字与外层代码块的一个标识符同名,内层的那个标识符就将隐藏外层的标识符——外层的表识符无法在内层代码块中通过名字访问。


        上面代码一中,外层的代码块int h;中的h 和 内层的代码块的int h;中的h是不同的变量,内层块的的h无法通过名字访问外层代码块的h;

如果不是嵌套的代码块稍有不同。


例如:

{
    int i , j; // 声明一
    ...
}

{
    int i; // 声明二
    ...
}

        声明于每个代码块的变量无法被另一个代码块访问,因为它们的作用域无重叠。


        由于两个代码块的变量不可能同时存在,因此编译器可以把它们存储于一个内存地址。


例如,声明二中的变量可以声明一中的任何一个变量共享统一个地址。


        这种共享并不会带来任何危害,因为在任何时刻,两个非嵌套的代码块最多只有一个处于活动状态。


2)文件作用域

任何在所有代码块之外声明的标识符都具有文件作用域。


它表示声明之处直到它所在的源文件结尾处都可以访问。


在文件中定义的函数名也具有文件作用域,因为函数名本身不属于任何代码块。


#include 
int units = 0;     // 该变量具有文件作用域
void critic(void)  // 该函数也具有文件作用域
{
    ...
}

int main(void)
{
    ...
}

上面示例中,变量units具有文件作用域,critic()函数也具有文件作用域。


3)函数作用域

函数作用域只适用于标签语句,语句标签用于goto语句。


这意味着即使一个标签首次出现在函数的内层块中,它的作用域也延伸至整个函数。


函数作用域可以简化为一条规则——一个函数中的所有语句标签必须唯一。


4)函数原型作用域

函数原型作用域只适用函数原型中的形参名。


如下所示:

int size (double number);
int find(int a);
int size (double num)
{
    ....
}

int find(int b)
{
    ...
}

函数原型的作用域的范围:从形参定义处到函数声明结束。


函数原型作用域的作用:

函数原型作用域防止这些参数名和程序其他部分的名字冲突。


唯一可能出现冲突是同一个原型中不止一次地使用同一个名字。


这意味着,编译器在处理函数原型中的形参时只关心它的类型,参数的名字并非必须(这与函数的定义不同)。


如果出现了参数名,则可以给他们取任何名字,它们不必与函数定义中的形参名匹配,如上面代码中,函数声明用的形参是double number,int a;而函数定义用的形参是double num,int b;

也不必与函数实际调用所用的实参匹配。


注:只用在变长数组中,形参名才有用

void use_number(int a, int b, ar[a][b]);

方括号中必须使用在函数原型中已声明的名称。


上面的变长数组就使用了函数原型声明的形参名a和b,不能使用别的形参名比如m,n。


1.2 链接

 什么是链接属性?

        链接属性与C语言中各个目标文件及函数的链接过程有关,用于认定不同文件的标识符(即程序中定义的各种名称,包括变量名、函数名)是否是同一个实体。


更通俗地说,就是在两个不同文件中的变量、函数声明是否指向同一个实体。


比如:a、b文件同时声明了变量c,链接属性就指定了这两处变量c是否是同一个c。


简单来说,链接属性的作用就是让你能在a文件中决定要不要访问b文件中的变量、函数。


链接属性的作用

        当组成一个程序的各个源文件分别被编译后,所有的目标文件以及那些从一个或多个函数库中引用的函数链接在一起,形成可执行程序.标识符的链接属性决定如何处理在不同文件中出现的标识符.标识符作用域与它的链接属性有关,但这两个属性并不相同.

3种链接属性:

              1)外部链接(external)

              2)内部链接(internal)

              3)无链接(none)

无链接的范围:

具有块作用域、函数作用域和函数原型作用域的变量都是无链接变量。


意味着这些变量属于定义它们的块、函数或原型私有的。


外部链接和内部链接的范围:

具有文件作用域可以是外部链接或内部链接。


外部链接变量可以在多个文件程序中使用,内部链接变量只能在一个翻译单元中使用。


翻译单元:编译器把源代码文件和所有的头文件都看成是一个包含信息的单独文件。


这个文件被称为翻译单元。


文件作用域变量是外部链接还是内部链接?

看外部定义中是否使用了存储类别说明符static。


int a = 5;               // 文件作用域,外部链接
static int b = 3;          // 文件作用域,内部链接
int main()
{
    int e;
    int f(int g);        //f为外部链接,因为它是个函数名
    ...
}

怎样修改链接属性?

关键字extern和static用于在声明修改标识符的链接属性。


如果某个声明在正常情况下具有外部链接属性,在它前面加上static关键字可以使它的链接属性变为内部链接。


例如:上面的int a = 5;

写成static int a = 5;那么变量a为这个源文件私有。


类似,也可以把函数声明为static,如下:

static int c( int d )

可以防止它被其它源文件调用。


注:static只对外部链接的声明才有改变链接属性的效果。


extern关键字的规则更复杂。


一般而言,它为一个标识符指定为外部链接属性,这样就可以访问在其他任何位置定义的这个实体。


例如:

static int i;
int func()
{
    int j;
    extern int k;

    extern int i;
    ...
}

上面的k指定为外部链接属性。


这样函数就可以访问在其他源文件声明的外部变量。


        注: 当extern关键字用于源文件中一个标识符的第一次声明,它指定该标识符具有外部链接属性。


但是,如果他用于该标识符的第2次或以后的声明,它并不会更改由第1声明所指定的链接属性。


例如,上面的代码中的static int i; 和 extern int i ; 后者的声明不会该变前者声明所指定的变量i的链接属性。


1.3存储期

存储期的作用:描述通过这些标识符访问对象的生存期。


什么是对象?

  从硬件方面来看,被存储的每个值都占用一定的物理内存,C语言把这样的一块内存称为对象。


        对象可以存储一个或多个值。


一个对象可能并未存储实际的值,但是它在存储适合的值是一定具有相应的大小(面向对象编程中的对象指的是类对象,起定义包括数据和允许对数据进行的 *** 作,C不是面向对象的编程语言)。


C对象的4种存储期:

                                1)静态存储期

                                2)线程存储期

                                3)自动存储期

                                4)动态分配存储期

1)静态存储期

静态存储期的作用:

如果对象具有静态存储期,那么它在程序的执行期间一直存在。


哪些具有静态存储期?

凡是代码块之外声明的变量具有静态存储期,储存于静态内存中。


例如:

文件作用域变量具有静态存储期,无论变量是内部链接还是外部链接。


注:对于文件作用域变量,关键字static表明了其链接属性,而非存储期。


以static声明的文件作用域变量具有内部链接属性。


注:块作用域变量也能具有静态存储期。


只需在变量声明的块中,且在声明前面加上关键字static,可以使它的存储类型从自动变为静态。


void more(int number)
{
    int index;
    static in ct = 0;    
    ...
    return 0;
}

        这里,变量ct存储在静态内存中,它从程序被载入到程序结束期间都存在。


但是,它的作用域定义在more()函数块中。


只有在执行该函数时,程序才能使用ct访问它所指的对象(但是,该函数可以给其他函数提供该存储区域的地址以便间接访问该对象,例如通过指针形参或返回值)。


注:修改变量的存储类型并不表示修改该变量的作用域,它仍然只能在它的代码块内部按名字访问。


函数的形式参数不能声明为静态,因为实参总是在堆栈中传递给函数,用于支持递归。


2)线程存储期

        线程存储期用于并发程序设计,程序执行可被划分多个线程。


具有线程存储期的对象,从被声明时到线程结束一直存在。


以关键字_Thread_local声明一个对象时,每个线程都获得该变量的私有备份。


3)自动存储期

        块作用区域的变量通常都具有自动存储期。


当程序进入定义这些变量的块时,为这些变量分配内存;当退出这个块时,释放刚才为变量分配的内存。


        这样做法相当于把自动变量占用的内存区域视为一个可重复使用的工作区或暂存区。


例如,一个函数调用之后,其变量占用的内存可用于存储下一个被调用的函数的变量。


void bore(int number)
{
    int index;
    for(index = 0; index < number; index++)
        puts(“They don’t make them the way they used to.\n”);
    return 0;
}

上面代码中,变量number和index在每次调用bore()函数时被创建,在离开函数时被销毁。


注:变长数组稍有不同,它们的存储期从声明处到块的末尾,而不是从块的开始处到块的末尾。


4)动态分配存储期

二、动态分配存储期

接下来详细介绍动态分配存储期

2.1存储类型

变量的存储类型:指存储变量值的内存类型。


可分为三个存储类型:自动变量、寄存器变量、静态变量

细分为5种存储类别:自动、寄存器、静态块作用域、静态外部链接、静态内部链接

3个地方用于存储变量:普通内存、运行时堆栈、硬件寄存器

 1)自动变量

默认情况下,声明在块或函数头中的任何变量都属于自动存储类别。


它存储于堆栈中。


属于自动存储类别的变量具有自动存储期、块作用域且无链接。


块作用域和无链接意味着只有在变量定义所在的块中才能通过变量名访问该变量(当然,参数用于传递变量的值和地址给另一个函数,但是这是间接的方法)。


另一个函数可以使用同名的变量,但是该变量存储在不同内存位置的另一个变量。


变量具有自动存储期意味着,程序在进入该声明所在的块时变量存在,程序在退出该块时变量消失。


原来该变量占用的内存位置现在可做他用。


关键字auto(存储类别说明符)用于修饰这种存储类别。


但它极少使用,因为代码块中的变量在缺省情况下就是自动变量。


如果为了更清楚地表达你的意图(例如,为了表明有意覆盖一个外部变量的定义,或者强调不要把该变量改为其他存储类别),可以显示使用关键字auto,如下所示:

int main(void)
{
    auto int plox;
    ...
}

块中声明的变量仅限于该块及其包含的块使用。


例如:

int loop(int n)
{
    int m; // m的作用域
    scanf(“%d”, &m);
    {
        int i; // m和i的作用域
        for (i = m; i < n; i++)
            puts(“i is local to a sub-block\n”);
    }
    return m; // m的作用域,i已经消失了
}

        上面的代码中,i仅在内层块可见。


如果在内层块的前面或后面使用i,编译器会报错。


变量n和m分别定义在函数头和外层块中,它们的作用域是整个函数,而且在调用函数到函数结束期间都一直存在。


如果内层块中声明的变量和外层块声明的变量同名会怎么样?

内层块会隐藏外层块的定义。


但是离开内层块后,外层块变量的作用域又回到了原来的作用域。


例如下面的示例:

#include 
int main(void)
{
    int x = 30;  //原始的x
    printf(“x in outer block: %d at &p\n”, x, &x);
    {
        int x = 77; //新的x,隐藏了原始的x

        printf(“x in inner block: %d at %p\n”, x, &x);
    }

    printf(“x in outer block: %d at %p\n”, x, &x)

    while (x++ < 33) //原始的x
    {
        int x = 100;/新的x,隐藏了原始的x
        x++;
        printf(“x in while loop: %d at %p\n”, x, &x);
    }   
    printf(“x in outer block: %d at %p\n”, x, &x);
    
    return 0;
}

程序输出为:

         首先,程序创建了变量x并初始化为30,如第1条printf()语句所示。


然后,定义了一个新的变量x,并设置为77,如第2条printf()语句所示。


根据显示的地址可知,新变量隐藏了原始的x。


第3条printf()语句位于第1个内层块后面,显示的是原始的x的值,这说明原始的x既没有消失也不曾改变。


while循环的测试条件中使用的是原始的x:

while(x++ < 33)

        在该循环中,程序创建了第3个x变量,该变量只定义在while循环中。


所以,当执行到循环体中的x++时,递增为101的是新的x,然后printf()语句显示了该值。


每轮迭代结束,新的x变量就消失。


然后循环的测试条件使用并递增原始的x,再次进入循环体,再次创建新的x。


在该例中,这个x被创建和销毁了3次。


注:该循环必须在测试条件中递增x,因为如果在循环体中递增x,那么递增的是循环体中的创建x,而非测试条件中使用的原始x。


如上面的输出:

就是在循环体递增在循环体声明的x,也就是int x = 100; 

自动变量的初始化

自动变量不会初始化,除非显式初始化它。


考虑下面的声明:

int main(void)
{
    int repid;
    int tents = 5;
    ...
}

        tents变量被初始化为5,但是repid变量的值是之前占用分配给repid的空间的任意值(如果有的话),但别指望这个值是0。


可以用 非常量表达式 初始化自动变量,前提是所用的变量已在前面定义过:

int main(void)
{
    int ruth = 1;
    int rance = 5 * ruth; // 使用之前定义的变量
    ...
}

自动变量没有缺省的初始值,而显示初始化将在代码块的起始处插入一条隐式的赋值语句。


这个技巧造成4种结果:

1)自动变量的初始化较之赋值语句效率并无提高;

2)除了声明为const变量之外,在声明变量的同时进行初始化和先声明后赋值只有风格之差,并无效率之别。


例如:

int a = 5;
int a; 
a = 5;

3)这条隐式的赋值语句使自动变量在程序执行到它们所声明的函数(或代码块)时,每次都将重新初始化。


这是个优点,由于初始化在运行时执行,因此可以用任何表达式作为初始值,例如

int func ( int a )
{
    int b = a + 3;

4)除非对自动变量进行显式的初始化,否则当自动变量创建时,它们的值总是垃圾。


2)寄存器变量

        关键字register可以用于自动变量的声明,提示它们应该存储于机器的硬件寄存器而不是内存中,这类变量称为寄存器变量。


例如:

int main(void)
{
    register int quick;
    ...
}

变量通常存储在计算机内存中。


        如果幸运的话,寄存器变量存储在CPU的寄存器中,或者概括来说,存储在最快的可用内存中。


通常,寄存器变量比存储于内存的变量访问起来效率更高。


由于寄存器变量存储在寄存器而非内存中,所以无法获取寄存器变量的地址。


        注:声明变量为register类别与直接命令相比更像是一种请求。


编译器必须根据寄存器或最快可用内存的数量衡量你的请求,或者直接忽略你的请求,所以可能不会如你所愿。


在这种情况下,寄存器变量就变成普通的变量。


即使这样,仍然不能对该变量使用地址符。


在函数头使用关键字register,便可请求形参是寄存器变量:

void macho(register int n)

绝大多数方面,寄存器变量和自动变量都一样。


也就是说,它们都是块作用域、无链接和自动存储期。


        寄存器变量的创建、销毁时间和自动变量相同,但它需要一些额外的工作。


在一个使用寄存器变量的函数返回之前,这些寄存器先前存储的值必须恢复,确保调用者的寄存器变量未被破坏。


许多机器使用运行堆栈来完成这个任务。


当函数开始执行时,它把需要使用的所有寄存器的内容都保存到堆栈中,当函数返回时,这些值再复制回寄存器中。


3)静态变量

静态变量的定义:凡是在任何代码块之外声明的变量总是存储于静态内存中,也就是不属于堆栈的内存,这些变量称为静态变量。


静态的意思是该变量在内存中原地不动,并不是说它的值不变。


2.2静态变量

分成3类:

              1)块作用区域的静态变量

              2)外部链接的静态变量

              3)内部链接的静态变量

1)块作用区域的静态变量

        上面提到过,可以创建具有静态存储期、块作用域的局部变量。


这些变量和自动变量一样,具有相同的作用域,但是程序离开它们所在的函数,这些变量不会消失。


这种变量具有块作用域、无链接,但是具有静态存储期。


计算机在多次函数调用之间会记录它们的值。


在块中(提供作用域和无链解)以存储类别说明符static(提供静态存储期)声明这种变量。


下面程序演示了这样的一个例子:

#include 

void trystat(void);

int main(void)
{
    int count;
    for (count = 1; count <= 3; count++)
    {
        printf("Here comes iteration %d:\n", count);
        trystat();
    }

return 0;
}

void trystat(void)
{
    int fade = 1;
    static int stay = 1;    
    printf("fade = %d and stay = %d\n", fade++, stay++);
}

注:trystat()函数先打印再递增变量的值,程序输出如下:

         静态变量stay保存了它被递增1后的值,但是fade变量每次都是1.这表明初始化不同,调用trystat()都会初始化fade,但是stay只在编译trystat()时被初始化一次。


如果未显式初始化静态变量,它们会被初始化为0。


不能在函数的形参中使用static:

int wontwork(static int flu); // 不允许

2)外部链接的静态变量

外部链接的变量具有文件作用域、外部链接和静态存储期。


该类别有时称为外部存储类别,属于该类别的变量称为外部变量。


外部变量的声明:把变量的定义性声明放在所有函数外面便创建了外部变量。


为了指出该函数使用了外部变量,可以在函数中使用关键字extern再次声明。


注:如果一个源代码文件使用的外部变量定义在另一个源代码文件中,则必须用extern在该文件中声明该变量.

如下所示:

int Errupt; /* 外部定义的变量 */

double Up[100]; /* 外部定义的数组 */

extern char Coal; /* 如果Coal被定义在另一个文件,则必须这样声明 */

void next(void);

int main(void)
{
    extern int Errupt; /* 可选的声明 */

    extern double Up[]; /* 可选的声明,不用指明数组大小,因为第1次声明已经提供

    ... 数组的大小 */
}

void next(void)
{
    ...
}

        main()中的两条extern声明完全可以省略,因为外部变量具有文件作用域,所以Errupt和Up从声明处到结尾处都可见。


它们出现在那里,仅为了说明main()函数要使用这两个变量。


        如果省去掉函数中的extern关键字,相当于创建了一个自动变量。


去掉下面声明中的extern:

extern int Errupt;

便成为:

int Errupt;

        这使得编译器在main()中创建了一个名为Errupt的自动变量。


它是一个独立的局部变量,与原来的外部变量Errupt不同。


该局部变量仅在main()中可见,但是外部变量Errupt对于该文件的其他函数(如next())也可见。


外部变量具有静态存储期。


因此,无论程序执行到main()、next()还是其他函数,数组Up及其值都一直存在。


外部变量的作用域:从声明处到文件结尾。


初始化外部变量

外部变量只能使用常量表达式初始化文件作用域变量,如果未初始化外部变量,它们会被自动初始化为0。


int x = 10;

int y = 3 + 20; // 没问题,用于初始化的是常量表达式

size_t = sizeof(int); // 没问题,用于初始化的是常量表达式

int x2 = 2 * x; // 不行,x是变量

注:只要不是变长数组,sizeof表达式可被视为常量表达式。


外部名称

C99和C11标准都要求编译器识别局部标识符的前63个字符和外部标识符的前31个字符。


这修订了以前的标准,即编译器识别局部标识符前31个字符和外部标识符前6个字符。


外部变量名比局部变量名的规则严格,是因为外部变量名还要遵循局部环境变量,所受的限制更多。


定义和声明

看下面示例:

int tern = 1; // tern被定义
main()
{
    extern int tern; //使用在别处定义的tern

        这里,tern被声明了两次。


第1次声明为变量预留了存储空间,该变量构成了变量的定义。


第2次声明只告诉编译器使用之前已创建的tern变量,所以这不是定义。


第1次声明被称为定义式声明,第2次声明被称为引用式声明。


关键字extern表明该声明不是定义,因为它指示编译器去别处查询其定义。


        定义变量就是声明了一个变量并且计算机为其预留了存储空间。


        声明变量就是单纯的声明一个变量,不管这个变量是否获得存储空间。


extern int tern;
int main(void)
{

        编译器会假设tern实际的定义在该程序的别处,也许在别的文件中。


该声明并不会引起分配存储空间。


因此,不要用关键字extern创建外部定义,只能用它来引用现有的外部定义。


外部变量只能初始化一次,且必须在定义该变量时进行

// file_one.c
char permis = ‘N’;
...
// file_two.c
extern char permis = ‘Y’; / * 错误 */

file_two.c中的声明是错误的,因为file_one.c中的定义式声明已经创建并初始化了permis。


3)内部链接存储变量

该存储类别的变量具有静态存储期、文件作用域和内部链接。


在所有函数外部用储存说明符static定义的变量具有这种存储类别:

static int svil = 1; // 静态变量、内部链接
int main(void)
{

        内部链接的静态变量只能用于同一个文件中的函数。


可以使用存储类别说明符extern,在函数中重复声明任何具有文件作用域的变量。


这样的声明并不会改变其链接属性。


例如下面代码:

int traveler = 1;   // 外部链接
static int stayhome = 1; // 内部链接
int main(void)
{
    extern int traveler; // 使用定义在别处的 traveler
    extern int stayhome; // 使用定义在别处的 stayhome
    ...

        对于该程序所在的翻译单元,traveler和stayhome都具有文件作用域,但是只有traveler可用于其它翻译单元(因为它具有外部链接)。


这两个声明都使用了extern关键字,指明了main()中使用的这两个变量的定义在别处,但是这并未改变stayhome的内部链接属性。


        总的来说,静态变量在程序运行之前创建,在程序的整个执行期间始终存在。


它始终保持原先的值,除非给它赋一个不同的值或者程序结束。


2.3存储类别说明符

        C语言有6个关键字作为存储类别说明符:auto、register、static、extern、_Thread_local和typedef。


        1)auto说明符表明变量是自动存储期,只能用于块作用域的变量声明中。


由于在块中声明的变量本身就具有自动存储期,所以使用auto主要是为了明确表达要使用与外部变量同名的局部变量的意图。


        2)register说明符也只是用于块作用域的变量,它把变量归为寄存器存储类别,请求最快速度访问该变量。


同时,还保护了该变量的地址不被获取。


        3)static。


当它用于函数定义时,或用于代码块之外的变量声明时,static关键字用于修改标识符的链接属性(从外部链接该为内部链接),但标识符的存储类型(静态存储)和作用域(文件作用域)不受影响。


用这种方式声明的函数或变量只能在声明它们的源文件中访问。


        当它用于代码块内部的变量声明时,static关键字用于修改变量的存储类型,从自动变量修改为静态变量,但变量的链接属性和作用域不受影响。


用这种方式声明的变量在程序执行之前创建,并在程序的整个执行期间一直存在,而不是每次在代码块开始执行时创建,在代码块执行完毕后销毁。


        4)extern说明符表明声明的变量定义在别处。


如果包含extern的声明具有文件作用域,则引用的变量必须具有外部链接。


如果包含extern的声明具有块作用域,则引用的变量可能具有外部链接或内部链接,这取决于该变量的定义声明式。


如上面的代码:

int traveler = 1;   // 外部链接
static int stayhome = 1; // 内部链接
int main(void)
{
    extern int traveler; // 使用定义在别处的 traveler
    extern int stayhome; // 使用定义在别处的 stayhome
    ...

        5)typedef关键与任何内存存储无关,把它归于此类有一些语法上的原因。


尤其是,在绝大多数情况下,不能在声明中使用多个存储类别的说明符,所以这意味着不能使用多个存储类别说明符作为tepedef的一部分。


        6)_Thread_local,它可以和static或extern一起使用

2.4存储类别和函数

        函数也有存储类别,可以是外部函数(默认)或静态函数。


C99增加了一种——内联函数。


外部函数可以被其他文件的函数所访问,但是静态函数只能用于其定义所在的文件。


一个文件中包含了以下函数原型:

double gamma(double); /* 该函数默认为外部函数 */
static double beta(int, int);
extern double delta(double, int);

        在同一个程序中,其它文件中的函数可以调用gamma()和delta(),但是不能调用beta(),因为以static存储类别说明符创建的函数属于特定模块私有。


这样避免了名称的冲突的问题,由于beta()受限于它所在的文件,所以在其他文件中可以使用与之同名的函数。


        通常的做法是:用extern关键字声明定义在其他文件中的函数。


这样做是为了表明当前文件使用的函数被定义在别处。


除非使用static关键字,否则一般函数声明都默认为extern。


参考:《C Primer Plus》、《Pointers On C》

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/577754.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-11
下一篇 2022-04-11

发表评论

登录后才能评论

评论列表(0条)

保存