目录

C语言模板


存储类定义 C 程序中变量/函数的范围(可见性)和生命周期。这些说明符放置在它们所修饰的类型之前。

  • auto 是局部变量的默认存储类, 限定变量只能在函数内部使用;
  • register 代表了寄存器变量,不在内存中使用;
  • static是全局变量的默认存储类,表示变量在程序生命周期内可见;
  • extern 表示全局变量,即对程序内所有文件可见,

auto

auto 存储类是所有局部变量默认的存储类。

auto 只能用在函数内,即 auto 只能修饰局部变量。**

1
2
3
4
5
6
7
8
9
#include <stdio.h>

int main(void)
{
    auto int i = 9; /* 声明局部变量的关键字是 auto; 因可以省略, 几乎没人使用 */   
    printf("%d\n", i);  
    getchar();
    return 0;
}

Register

register 存储类用于定义存储在寄存器中而不是 RAM 中的局部变量。这意味着变量的最大尺寸等于寄存器的大小(通常是一个词),且不能对它应用一元的 ‘&’ 运算符(因为它没有内存位置)。

动态和静态变量都是存放在内存中,程序中遇到该值时用控制器发指令将变量的值送到运算器中,需要存数再保存到内存中。如果频繁使用一个变量,比如一个函数体内的多次循环每次都引用该局部变量,我们则可以把局部变量的值放到CPU的寄存器中,叫寄存器变量。不需要多次到内存中存取提高效率。但是只能局部自动变量和形参可以做寄存器变量。在函数调用时占用一些寄存器,函数结束时释放。不同系统对register要求也不一样,比如对

定义register变量个数,数据类型等限制,有的默认为自动变量处理。所以在程序一般也不用。 寄存器只用于需要快速访问的变量,比如计数器。还应注意的是,定义 ‘register’ 并不意味着变量将被存储在寄存器中,它意味着变量可能存储在寄存器中,这取决于硬件和实现的限制。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <stdio.h>
#include <time.h>

#define TIME 1000000000
int m, n = TIME; /* 全局变量 */

int main(void)
{   
    time_t start, stop;
    register int a, b = TIME; /* 寄存器变量 */
    int x, y = TIME;          /* 一般变量   */

    time(&start);
    for (a = 0; a < b; a++);
    time(&stop);
    printf("寄存器变量用时: %ld 秒\n", stop - start);
    
    time(&start);
    for (x = 0; x < y; x++);
    time(&stop);
    printf("一般变量用时: %ld 秒\n", stop - start);
    
    time(&start);
    for (m = 0; m < n; m++);
    time(&stop);
    printf("全局变量用时: %ld 秒\n", stop - start);

    return 0;
}

输出结果:

1
2
3
寄存器变量用时: 1 秒
一般变量用时: 8 秒
全局变量用时: 9 秒

static

static 存储类指示编译器在程序的生命周期内保持局部变量的存在,而不需要在每次它进入和离开作用域时进行创建和销毁。因此,使用 static 修饰局部变量可以在函数调用之间保持局部变量的值。

static 修饰符也可以应用于全局变量。当 static 修饰全局变量时,会使变量的作用域限制在声明它的文件内。

全局声明的一个 static 变量或方法可以被任何函数或方法调用,只要这些方法出现在跟 static 变量或方法同一个文件中。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
实例
#include <stdio.h>
 
/* 函数声明 */
void func1(void);
 
static int count=10;        /* 全局变量 - static 是默认的 */
 
int main()
{
  while (count--) {
      func1();
  }
  return 0;
}
 
void func1(void)
{
/* 'thingy' 是 'func1' 的局部变量 - 只初始化一次
 * 每次调用函数 'func1' 'thingy' 值不会被重置。
 */                
  static int thingy=5;
  thingy++;
  printf(" thingy 为 %d , count 为 %d\n", thingy, count);
}

实例中 count 作为全局变量可以在函数内使用,thingy 使用 static 修饰后,不会在每次调用时重置。

在 C 语言中,static 可以用来修饰局部变量,全局变量以及函数。在不同的情况下 static 的作用不尽相同。

1)修饰局部变量

一般情况下,对于局部变量是存放在栈区的,并且局部变量的生命周期在该语句块执行结束时便结束了。但是如果用static进行修饰的话,该变量便存放在静态数据区,其生命周期一直持续到整个程序执行结束。但是在这里要注意的是,虽然用static对局部变量进行修饰过后,其生命周期以及存储空间发生了变化,但是其作用域并没有改变,其仍然是一个局部变量,作用域仅限于该语句块。

在用static修饰局部变量后,该变量只在初次运行时进行初始化工作,且只进行一次。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#include<stdio.h>  
void fun()  
{   
    static int a=1; a++;   
    printf("%d\n",a);  
}  
int main(void)  
{   
    fun();   
    fun();   
    return 0;  
}  

程序执行结果为: 2 3

说明在第二次调用fun()函数时,a的值为2,并且没有进行初始化赋值,直接进行自增运算,所以得到的结果为3.

对于静态局部变量如果没有进行初始化的话,对于整形变量系统会自动对其赋值为0,对于字符数组,会自动赋值为’\0’.

2)修饰全局变量

对于一个全局变量,它既可以在本源文件中被访问到,也可以在同一个工程的其它源文件中被访问(只需用extern进行声明即可)。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
//有file1.c  
int a=1;  
file2.c  
#include<stdio.h>  
extern int a;  
int main(void)  
{  
    printf("%d\",a);  
    return 0;  
} 

则执行结果为 1

但是如果在 file1.c 中把 int a=1 改为 static int a=1;

那么在file2.c是无法访问到变量a的。原因在于用static对全局变量进行修饰改变了其作用域的范围,由原来的整个工程可见变为本源文件可见。

3)修饰函数

用static修饰函数的话,情况与修饰全局变量大同小异,就是改变了函数的作用域。

sratic只初始化一次

如果实在函数内部定义的,那么这个变量只初始化一次,即使再次调用这个函数,这个static变量也不会再次初始化 , 这个变量的取值就会一直保存着,也就是说,当你再次调用这个函数的时候,里面用到这个static变量时,就会发现,它还是上一次函数调用时的结果。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>

void varfunc(void)
{
    int var=0;
    static int static_var=0;
    printf("var equal: %d\n",var);
    printf("static_var equal: %d\n",static_var);
    printf("");
    var++;
    static_var++;
}

int main(int argc, char *argv[])
{
    int i;
    for(i=0;i<3;i++)
        varfunc();
    return 0;
}

运行结果如下:

1
2
3
4
5
6
var equal: 0
static_var equal: 0
var equal: 0
static_var equal: 1
var equal: 0
static_var equal: 2

extern

extern 存储类用于提供一个全局变量的引用,全局变量对所有的程序文件都是可见的。当您使用 extern 时,对于无法初始化的变量,会把变量名指向一个之前定义过的存储位置。

当您有多个文件且定义了一个可以在其他文件中使用的全局变量或函数时,可以在其他文件中使用 extern 来得到已定义的变量或函数的引用。可以这么理解,extern 是用来在另一个文件中声明一个全局变量或函数。 main.c

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#include <stdio.h>
 
int count ;
extern void write_extern();
 
int main()
{
   count = 5;
   write_extern();
}

support.c

1
2
3
4
5
6
7
8
#include <stdio.h>
 
extern int count;
 
void write_extern(void)
{
   printf("count is %d\n", count);
}

编译结果

1
count is 5

第二个文件中的 extern 关键字用于声明已经在第一个文件 main.c 中定义的 count。 因此count是作为全局变量使用。

C 语言中全局变量、局部变量、静态全局变量、静态局部变量的区别

从作用域看:

1、全局变量具有全局作用域。全局变量只需在一个源文件中定义,就可以作用于所有的源文件。当然,其他不包含全局变量定义的源文件需要用extern 关键字再次声明这个全局变量。

2、静态局部变量具有局部作用域,它只被初始化一次,自从第一次被初始化直到程序运行结束都一直存在,它和全局变量的区别在于全局变量对所有的函数都是可见的,而静态局部变量只对定义自己的函数体始终可见。

3、局部变量也只有局部作用域,它是自动对象(auto),它在程序运行期间不是一直存在,而是只在函数执行期间存在,函数的一次调用执行结束后,变量被撤销,其所占用的内存也被收回。

4、静态全局变量也具有全局作用域,它与全局变量的区别在于如果程序包含多个文件的话,它作用于定义它的文件里,不能作用到其它文件里,即被static关键字修饰过的变量具有文件作用域。这样即使两个不同的源文件都定义了相同名字的静态全局变量,它们也是不同的变量。

从分配内存空间看:

1、全局变量,静态局部变量,静态全局变量都在静态存储区分配空间,而局部变量在栈里分配空间

2、全局变量本身就是静态存储方式, 静态全局变量当然也是静态存储方式。这两者在存储方式上并无不同。这两者的区别虽在于非静态全局变量的作用域是整个源程序,当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。而静态全局变量则限制了其作用域,即只在定义该变量的源文件内有效,在同一源程序的其它源文件中不能使用它。由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用,因此可以避免在其它源文件中引起错误。

1)静态变量会被放在程序的静态数据存储区(全局可见)中,这样可以在下一次调用的时候还可以保持原来的赋值。这一点是它与堆栈变量和堆变量的区别。 2)变量用static告知编译器,自己仅仅在变量的作用范围内可见。这一点是它与全局变量的区别。 从以上分析可以看出, 把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存期。把全局变量改变为静态变量后是改变了它的作用域,限制了它的使用范围。因此static 这个说明符在不同的地方所起的作用是不同的。应予以注意。

Tips:

A.若全局变量仅在单个C文件中访问,则可以将这个变量修改为静态全局变量,以降低模块间的耦合度; B.若全局变量仅由单个函数访问,则可以将这个变量改为该函数的静态局部变量,以降低模块间的耦合度; C.设计和使用访问动态全局变量、静态全局变量、静态局部变量的函数时,需要考虑重入问题,因为他们都放在静态数据存储区,全局可见; D.如果我们需要一个可重入的函数,那么,我们一定要避免函数中使用static变量(这样的函数被称为:带"内部存储器"功能的的函数) E.函数中必须要使用static变量情况:比如当某函数的返回值为指针类型时,则必须是static的局部变量的地址作为返回值,若为auto类型,则返回为错指针。


参考资料

程序设计入门–C翁恺

c语言程序设计》–谭浩强

《C Primer Plus》–Stephen Prata

C语言中文网

C语言教程|菜鸟教程