[TOC]html
最近详细的复习C语言,看到存储类别的时候总感受一些概念模糊不清,如今认真的梳理一下。C语言的优点之一可以让程序员恰到好处的控制程序,能够经过C语言的内存管理系统指定变量的做用域和生存周期,实现对程序的控制。程序员
对象:在C语言中全部的数据都会被存储到内存中,被存储的值会占用必定的物理内存,这样的一块内存被称为对象,它能够储存一个或者多个值,在储存适当的值时必定具备相应的大小。(C语言对象不一样于面向对象语言的对象)数组
标识符:程序须要一种方法来访问对象,这就须要声明变量来实现,例如: int identifier = 1
,在这里identifier
就是一个标识符,标识符是一个名称并遵循变量的命名规则。因此在本例中identifier
便是C程序指定硬件内存中的对象的方式并提供了存储的值的大小“1”。在其它的状况中 int * pt
、int arr[10]
,pt就是一个标志符,它指定了储存地址的变量,可是表达式*p不是一个标志符,由于它不是一个名称。arr
的声明建立了一个可容纳10个int
类型元素的对象,该数组的每个元素也是一个对象。并发
做用域:描述程序中可访问标识符的区域。由于一个C变量的做用域能够是块做用域、函数做用域、文件做用域和函数原型做用域。ide
块做用域:简单来讲块做用域就是一对花括号括起来的代码区域。定义在块中的变量具备块做用域,范围是定义处到包含该定义块的末尾。函数
函数原型做用域:范围是从形参定义处到函数原型声明的结束。咱们知道编译器在处理函数形参时只关心它的类型,而形参的名字一般可有可无。例如:oop
void fun(int n,double m); 一样能够声明为 void fun(int ,double );
还有一点要注意的是函数体的形参虽然声明在函数的左花括号以前可是它具备的是块做用域属于函数体这个块。ui
文件做用域:变量的定义在全部函数的外面,从它的定义处到该文件的末尾处都可见称这个变量拥有文件做用域。因此文件做用域变量也被称为全局变量spa
连接:C变量有三种连接属性:内部连接、外部连接和无连接。具备块做用域、函数原型做用域的变量都是无连接变量,这就意味这他们属于定义他们的块或者函数原型私有。文件做用域变量能够是外部连接或是内部连接,外部连接能够在多个文件中使用,内部连接只能定义它的文件单元中使用。线程
指对象在内存中保留了多长时间,做用域和连接描述了对象的可见性。存储期则描述了标识符访问对象的生存期。
C对象有4种存储期:
static
代表的是连接属性而不是存储期。以static
声明的文件做用域变量具备内部连接,不管具备内部连接仍是外部连接,全部的文件做用域变量都具备静态存储期。还有一种状况块做用域变量也能够拥有静态存储期,把变量声明在块中并在变量名前加static
关键字,例:
int fun(int num) { static int Index; ... }
在这里变量Index
就被存储在静态内存中,从程序被载入到程序结束都会存在,可是只有程序进入这个块中才会访问它指定的对象。
线程存储期:用于并发程序设计,一个程序的执行能够分为多个线程,具备线程存储期的变量从被声明时到线程结束一直存在。以关键字_Thread_local
声明一个对象时,每一个线程都得到该变量的私有备份。
自动存储期:块做用域变量一般具备自动存储期,当程序进入定义这些变量的块时,会为这些变量分配内存,当程序离开这个块时会自动释放变量占用的内存,这种作法至关于把自动变量占用的内存视为可重复利用的工做区或暂存区。
动态分配存储期
存储类别 | 存储期 | 做用域 | 连接 | 声明方式 |
---|---|---|---|---|
自动 | 自动 | 块 | 无连接 | 块内 |
寄存器 | 自动 | 块 | 无连接 | 块内 关键字regsiter |
静态外部连接 | 静态 | 文件 | 外部 | 全部函数外 |
静态内部连接 | 静态 | 文件 | 外部 | 全部函数外 关键字static |
静态无连接 | 静态 | 块 | 无 | 块内 关键字static |
自动变量属于自动识别的变量具备自动存储期,块做用域且无连接。能够显示的使用auto
关键字进行声明。
注意: auto是存储类别说明符和C++中的auto用法彻底不一样
一个变量具备自动存储期就意味着当程序进入这个块时变量存在,退出块时变量消失,原来变量占用的内存另做他用。
void hiding() { int x = 30; printf("x in outer block: %d at %p\n", x, &x); { x = 77; printf("x in inner block: %d at %p\n", x, &x); } // 块中内存被释放隐藏的x恢复 x = 30 printf("x in outer block: %d at %p\n", x, &x); while (x++ < 33) { int x = 100; x++; printf("x in while loop: %d at %p\n", x, &x); } printf("x in outer block: %d at %p\n", x, &x); }
没有花括号时
void forc() { int n = 8; printf(" Initially, n = %d at %p\n", n, &n); for (int n = 1; n < 3; ++n) printf(" loop 1:n = %d at %p\n", n &n); // 离开循环后原始的你又起做用了 printf("After loop 1:n = %d at %p\n", n &n); for (int n = 1; n < 3; ++n) { printf("loop 2 index n = %d at %p\n", n, &n); // 从新初始化的自动变量,做用域没有到循环里的n int n = 6; printf(" loop 2:n = %d at %p\n", n, &n); // 起做用的仍然是循环中的n n++; } // 离开循环后原始的n又起做用了 printf(" loop 2:n = %d at %p\n", n, &n); }
输出为
使用关键字register,储存在CPU的寄存器中,存储在最快的可用内存中。
首先要明确概念静态变量并非指值不改变的变量,而是指它在内存中的位置不变。具备文件做用域的静态变量自动具备静态存储期。
前面提到咱们能够建立一个静态存储期,块做用域的局部变量,这种变量和自动变量同样具备相同的做用域,可是在程序离开块时并不会消失,
void trystat(); int main() { int count = 1; for (count = 1; count <= 3; count++) { printf("Here comes iteration %d:\n", count); trystat(); } trystat(); return 0; } void trystat() { int fade = 1; static int stay = 1; printf(" fade = %d and stay = %d\n", fade++, stay++); }
输出:
能够看出每次离开块fade变量的值都会被从新的初始化,而stay只是在编译函数void trystat()
的时候被初始化了一次,在离开本身函数体的块和for循环块以后都会递增,说明stay访问的对象一直存在并无像自动变量同样被释放掉。
具备外部连接、静态存储期和文件做用域,属于该类别的变量属于外部变量。只须要把变量的声明放在全部函数的外面就建立了一个外部变量。为了代表该函数使用了外部变量,须要使用关键字extern
来再次申明。若是在一个源文件中使用的外部变量声明在了另外一个源文件中,则必需要使用extern来申明。
外部变量能够显示初始化,若是没有则会被默认初始化为0。
具备文件做用域、静态存储期和内部连接,在全部函数外用static
来声明一个变量就是内部连接的静态变量。
例:
static int val = 1; int main() { ... }
普通的外部变量能够用于程序中的任意一个函数处,可是内部连接的静态变量只能用于同一个文件中的函数。均可以使用extern
说明符,在函数中重复任何声明文件做用域变量并不会改变他们的连接属性。
例:
int global = 1; static int local_global = 2; int main { extern int global = 1; extern int local_global = 2; ... }
只有在多文件中才能区别内部连接和外部连接的重要性。
总结一下存储类别的说明符中关键字共有六个auto
、register
、_Thread_local
、static
、extern
和typedef
,其中static
和extern
的含义取决于上下文。
函数也有存储类别,分为外部函数(默认)和静态函数。外部函数能够被其它的文件访问,而静态函数只能被定义所在的文件访问。
例:
double gamma(double); static double beta(int,int); extern double(double,int);
一般使用extern
关键字声明定义在其余文件中的函数,这样作是为了代表当前文件中使用的函数定义在别处。除非使用static
关键字,不然通常函数声明都默认为extern
。
使用内部连接的静态变量的函数,随机函数(伪随机数)
// 程序 12.7 static unsigned long int next = 1; unsigned int rand0(); int main() { int count = 1; for (count = 0; count < 5; count++) { printf("%d\n", rand0()); } return 0; } unsigned int rand0() { next = next * 1103515245 + 12345; return (unsigned int) (next/65536) % 32768; }
存储类别有一个共同之处,在肯定使用哪种类型以后,会根据已经规定好的内存管理规则自动选择其做用域和储存储期。如今咱们使用更加灵活的方式库函数分配和管理内存。
静态数据在程序载入内存时分配,而自动数据在程序执行时自动分配,在程序离开时销毁。如今咱们能够在使用malloc()
函数在程序中动态的分配内存,malloc()
接收一个参数,所须要要内存的字节数,在内存中自动寻找一个空闲的内存块使用,malloc()
分配内存是匿名的并不会为分配的内存块赋名称,可是动态内存会返回这个内存的首地址,因此使用一个指针类型的变量来接收它,而malloc()
函数可用于返回指向数组的指针、指向结构的指针等,一般使用强制类型转换将返回的地址转为匹配的类型。
例如申请一个可容纳30个double类型值的数组
double* pt; pt = (double*)malloc(30*sizeof(double))
注意:pt是数组的首元素地址,按照C语言的用法数组名就是首元素的地址,因此访问这个数组中元素的方法就能够这样表示pt[0]、pt[1]. . .
所以也就有了三种来表示数组的方法:
1)使用常量表达式来表示数组的维度,用数组名来访问数组的元素 。可使用静态内存和自动内存
2)声明变长数组,用变量表达式来表示数组的维度,用数组名访问数组的元素,具备这种特性的数组只能在自动内存中建立
3)使用malloc()
动态内存来建立一个数组,先声明一个指针,接收函数返回的地址。可使用指针访问数组的元素,指针的类型能够是静态的或者自动的。
malloc()
函数要和free()配套使用,申请的内存从malloc()
开始到free()
结束。
void dyn_arr() { double* ptd; int max; int number; int i = 0; puts("what is the maxnum number of type double entries!"); if (scanf("%d", &max) != 1) { puts("Number not correctly entered --bye"); exit(EXIT_FAILURE); } ptd = (double*)malloc(max * sizeof(double)); if (ptd == NULL) { puts("Memory allocation failed. GoodBye."); exit(EXIT_FAILURE); } puts("Enter ther values (q to quit)"); while (i < max && scanf("%lf", &ptd[i]) == 1) ++i; printf("Here are your %d enteries:\n", number = i); for (i = 0; i < number; i++) { printf("%7.2f", ptd[i]); if (i % 7 == 6) putchar('\n'); } if (i % 7 != 0) putchar('\n'); puts("Done."); free(ptd); return 0; }
原文出处:https://www.cnblogs.com/TJTO/p/11795786.html