变量和内存
2015-06-19 15:41
309 查看
注:《C primer plus》笔记
什么是变量?
变量是程序定义的一种对象,该对象存储了不同类型的值,例如int、float、char、数组、常量、指针等。变量名供程序使用,变量的值存储在内存或寄存器中。变量一般来说其值是可以变化的,其地址是不可以变化的。常量是变量的特例,限定了其值不可以变化。
变量和内存的关系
变量和程序在运行的时候,都被载入到了内存之中,其中程序代码占用固定的内存,而变量的内存却是根据实际情况不断变化的,程序结束后,释放所有内存。如何管理变量,才能实现占用内存最小,执行效率最高?这是一个值得研究的课题。
变量的内存相关特性
1,作用域
作用域描述了程序中可以访问一个标识符的一个或多个区域。一般来说,作用域分为代码块作用域和文件作用域。
代码块作用域:定义在{}或函数、循环、条件体中的代码块中的变量,具有代码块作用域,也就是说,只有在本代码块之中的位于声明变量滞后的程序代码才可见这些变量,包括代码块中的代码块,也可以引用在外部代码块中定义的变量。
文件作用域:定义在所有函数外的变量,具有文件作用域,也就是说,该变量定以后,对之后的所有函数可见。
示例:
#include <stdio.h>
#define INPUT "input 10 characters" //具有文件作用域的常量
int n = 10; //具有文件作用域的变量
int main(void)
{
int m = 0; //具有代码块作用域的变量
char ch;
puts(INPUT); //静态变量不能更改值。
while(m < n) //变量n可以被其后的所有函数引用。
{
putchar(getchar(ch)); //变量ch可以被其代码块中的代码块引用。
}
return 0;
}
2,链接
分为外部链接,内部链接和空链接
外部链接:函数外定义的变量,具有外部链接,可在同一程序的多个文件中引用
内部链接:函数外定义的变量,并以关键字static开头,具有内部链接,可在本文件中引用
空链接:代码块中的变量或函数原型声明中的变量,具有空链接,可在本代码块中引用
示例:
int s= 5;//外部链接的变量
static int t = 10;//内部链接的变量
void print( char ch);//空链接的变量
int main(void)
{
int a = 1;//空链接的变量
}
链接的概念看似和作用域差不多,但是却是增加了对变量作用域的可操作性。外部链接,通过关键字extern引入其他文件的外部链接变量声明:extern int s;注意,此处不能进行初始化,因为不是定义。
3,存储时期
分为静态存储时期和自动存储时期。
静态存储时期:程序期间一直存在的变量属性。称为静态变量
自动存储时期:代码块运行结束后回收该变量的变量属性。称为自动变量
具有文件作用域的变量,具有静态存储时期,具有代码块作用域的变量,定义时头部添加关键字static,也可以具有静态存储时期。
示例:
int s= 5;//静态变量
static int t = 10;//静态变量
void print( char ch);//自动变量
int main(void)
{
int a = 1;//自动变量
static b = 2; //静态变量
}
根据作用域、链接和存储时期,可以把变量划分成5种存储类
注意:循环、条件语句,若代码块中只有一条语句,可能省略{},但是这也是代码块。
自动变量
int main(void)
{
int a;
auto int b;
}
当运行到代码块时,变量才被分配内存,当代码块结束时,该变量的内存被回收。
适合场景:需要封装的代码块、自包含的代码块、变量内存使用较大或系统可用内存较少的场景。
寄存器变量
int main(void)
{
register int quik;
}
寄存器是位于cpu中的高速存储单元,比内存速度更快,理想状态是cpu需要的数据,全部可以在寄存器中找到,因此,CPU的性能指标中,寄存器(1级缓存)的大小很是很重要的。声明寄存器变量,因为寄存器太小,所以这只是一个请求而非命令,如果幸运,则可以得到一个寄存器变量,不幸运,则得到一个自动变量。因为寄存器中的没有内存地址,所以无法获取其地址(请求失败获得的自动变量,也因为声明而无法获得地址)。另外寄存器可能没有足够大的空间来容纳double类型的数据。
代码块作用域的静态变量(空链接的静态变量)
int main(void)
{
{
static int n = 10;
}
}
代码作用域的静态变量相比文件作用域的静态变量,具有只对其代码块可见的优点,这对封装概念来说是很好的。
具有外部链接的静态变量
int x =1;
extern int y;//变量y在该程序的另一个文件中定义,此处声明y
int main(void)
{
extern int x;//可选的声明
}
注意:使用其他文件的外部链接静态变量时,提前用extern声明是必须的;而使用本文件的外部、内部、空链接静态变量时,声明是可选的。用extern声明时,不可进行初始化。
具有内部链接的静态变量
static int x =1;
int main(void)
{
}
注意:加入已存在变量x,并且在它的作用域内,声明和定义了另一个变量,名称也是x,那么此时,新定义的变量x生效,直到变量x被回收或跳出它的作用域,原来的变量x才能继续工作,如果新变量x是静态的,那么原来的变量则被取消回收。
如果把函数看做是变量,那么对函数也有存储类的概念。
double gamma(void);//默认为外部链接的
static double beta(void);//内部链接
extern double delta(void);//声明其他文件的外部链接
那么,如何使用这些存储类呢?
1,遵循自包含思想,即只在本处使用的变量,尽量使其只对本处可见。也可以解释成封装性,私有性。
2,程序中多处使用的值不变的变量,可以声明为常量来使用,这样可以提高程序的可读性。
3,占用较多内存的变量,尽量使用自动存储类,这样才可以在使用完之后即刻释放。
总的来说,原则1:程序运行速度快,原则2:程序占用资源少(cpu、内存),原则3:程序结构清晰、互不干扰,原则4:程序可读性强、避免随意的变量声明定义。
除了这些存储类外,程序还可以通过库函数,手工分配内存和手工释放内存。
malloc()函数
接收一个整数,解释为字节数,然后再可用内存中找到一个大小适合的块,返回其首个地址,地址类型为空,可以进行转换。注意,使用本函数分配的内存,只能用free()函数释放。包含于stdlib.h
double * ptd;
ptd=(double *)malloc(30 * sizeof(double));
ptd指针可以像使用数组那样使用其后内存中的地址和数据。
calloc()
接收两个整数参数,一个是表示多少单元,二个是表示每个单元的字节多少。然后再可用内存中找到一个大小适合的块,返回其首个地址,地址类型为空,可以进行转换。注意,使用本函数分配的内存,只能用free()函数释放。包含于stdlib.h
double * ptd;
ptd=(double *)calloc(30,sizeof(double));
ptd指针可以像使用数组那样使用其后内存中的地址和数据。
free()
参数是通过库函数分配的内存的首地址,作用是释放库函数分配的内存。包含于stdlib.h。如果不释放内存,并且程序分配了很大的库函数分配的内存,那么很可能导致内存泄露。
free(ptd);
这种手工分配和释放的内存分配方式叫做动态内存分配,由于内存的管理方式过于动态,因此其产生的程序运行比自动分配的堆栈内存分配的程序慢。
类型限定词
const
声明值为不可修改的限定词。常用于将变量声明为常量。
const double x = 5.5;
也用于指针中,声明指针指向的地址不变或指向的值的是常量。
const float * pf;//指向一个常量浮点数值a,但是可以更改为指向另一个常量浮点数值b,只要是常量浮点数值即可。
float * const pf;//指向一个固定的地址0x11ff,但是地址中存储的数值是可以变化的。如从1.1变成1.2
const float * const pf;//指向一个固定的地址,该地址中存储的数值是一个常量。
volatitle
告诉编译器该变量除了可被程序改变外还可以被其他代理改变。如用于硬件地址和与其他并行运行的程序共享数据:时钟信息等。
使用volatitle关键字的原因是方便编译器优化:如果程序中两次使用x,且没改变x的值,那么编译器将x存入寄存器,以便下一次调用时获得更快的速度。如果声明为volatitle类型,则编译器不做本优化,因为数值可能已经改变。
restrict
只可以用于指针,表明指针是访问一个数据对象的唯一且初始的方式。如果是这样,编译器就可以使用优化方案,加快程序运行速度。
利用malloc()函数创建的数组是具有唯一的指针访问方式的:
int * restrict restar = (int *)malloc(10 * sizeof(int));
什么是变量?
变量是程序定义的一种对象,该对象存储了不同类型的值,例如int、float、char、数组、常量、指针等。变量名供程序使用,变量的值存储在内存或寄存器中。变量一般来说其值是可以变化的,其地址是不可以变化的。常量是变量的特例,限定了其值不可以变化。
变量和内存的关系
变量和程序在运行的时候,都被载入到了内存之中,其中程序代码占用固定的内存,而变量的内存却是根据实际情况不断变化的,程序结束后,释放所有内存。如何管理变量,才能实现占用内存最小,执行效率最高?这是一个值得研究的课题。
变量的内存相关特性
1,作用域
作用域描述了程序中可以访问一个标识符的一个或多个区域。一般来说,作用域分为代码块作用域和文件作用域。
代码块作用域:定义在{}或函数、循环、条件体中的代码块中的变量,具有代码块作用域,也就是说,只有在本代码块之中的位于声明变量滞后的程序代码才可见这些变量,包括代码块中的代码块,也可以引用在外部代码块中定义的变量。
文件作用域:定义在所有函数外的变量,具有文件作用域,也就是说,该变量定以后,对之后的所有函数可见。
示例:
#include <stdio.h>
#define INPUT "input 10 characters" //具有文件作用域的常量
int n = 10; //具有文件作用域的变量
int main(void)
{
int m = 0; //具有代码块作用域的变量
char ch;
puts(INPUT); //静态变量不能更改值。
while(m < n) //变量n可以被其后的所有函数引用。
{
putchar(getchar(ch)); //变量ch可以被其代码块中的代码块引用。
}
return 0;
}
2,链接
分为外部链接,内部链接和空链接
外部链接:函数外定义的变量,具有外部链接,可在同一程序的多个文件中引用
内部链接:函数外定义的变量,并以关键字static开头,具有内部链接,可在本文件中引用
空链接:代码块中的变量或函数原型声明中的变量,具有空链接,可在本代码块中引用
示例:
int s= 5;//外部链接的变量
static int t = 10;//内部链接的变量
void print( char ch);//空链接的变量
int main(void)
{
int a = 1;//空链接的变量
}
链接的概念看似和作用域差不多,但是却是增加了对变量作用域的可操作性。外部链接,通过关键字extern引入其他文件的外部链接变量声明:extern int s;注意,此处不能进行初始化,因为不是定义。
3,存储时期
分为静态存储时期和自动存储时期。
静态存储时期:程序期间一直存在的变量属性。称为静态变量
自动存储时期:代码块运行结束后回收该变量的变量属性。称为自动变量
具有文件作用域的变量,具有静态存储时期,具有代码块作用域的变量,定义时头部添加关键字static,也可以具有静态存储时期。
示例:
int s= 5;//静态变量
static int t = 10;//静态变量
void print( char ch);//自动变量
int main(void)
{
int a = 1;//自动变量
static b = 2; //静态变量
}
根据作用域、链接和存储时期,可以把变量划分成5种存储类
存储类 | 时期 | 作用域 | 链接 | 声明方式 | 关键字 |
自动 | 自动 | 代码块 | 空 | 代码块内 | auto(可选) |
寄存器 | 自动 | 代码块 | 空 | 代码块内,使用关键字register | register |
具有外部链接的静态 | 静态 | 文件 | 外部 | 所有函数之外 | 无 |
具有内部链接的静态 | 静态 | 文件 | 内部 | 所有函数之外,使用关键字static | static |
空链接的静态 | 静态 | 代码块 | 空 | 代码块内,使用关键字static | static |
自动变量
int main(void)
{
int a;
auto int b;
}
当运行到代码块时,变量才被分配内存,当代码块结束时,该变量的内存被回收。
适合场景:需要封装的代码块、自包含的代码块、变量内存使用较大或系统可用内存较少的场景。
寄存器变量
int main(void)
{
register int quik;
}
寄存器是位于cpu中的高速存储单元,比内存速度更快,理想状态是cpu需要的数据,全部可以在寄存器中找到,因此,CPU的性能指标中,寄存器(1级缓存)的大小很是很重要的。声明寄存器变量,因为寄存器太小,所以这只是一个请求而非命令,如果幸运,则可以得到一个寄存器变量,不幸运,则得到一个自动变量。因为寄存器中的没有内存地址,所以无法获取其地址(请求失败获得的自动变量,也因为声明而无法获得地址)。另外寄存器可能没有足够大的空间来容纳double类型的数据。
代码块作用域的静态变量(空链接的静态变量)
int main(void)
{
{
static int n = 10;
}
}
代码作用域的静态变量相比文件作用域的静态变量,具有只对其代码块可见的优点,这对封装概念来说是很好的。
具有外部链接的静态变量
int x =1;
extern int y;//变量y在该程序的另一个文件中定义,此处声明y
int main(void)
{
extern int x;//可选的声明
}
注意:使用其他文件的外部链接静态变量时,提前用extern声明是必须的;而使用本文件的外部、内部、空链接静态变量时,声明是可选的。用extern声明时,不可进行初始化。
具有内部链接的静态变量
static int x =1;
int main(void)
{
}
注意:加入已存在变量x,并且在它的作用域内,声明和定义了另一个变量,名称也是x,那么此时,新定义的变量x生效,直到变量x被回收或跳出它的作用域,原来的变量x才能继续工作,如果新变量x是静态的,那么原来的变量则被取消回收。
如果把函数看做是变量,那么对函数也有存储类的概念。
double gamma(void);//默认为外部链接的
static double beta(void);//内部链接
extern double delta(void);//声明其他文件的外部链接
那么,如何使用这些存储类呢?
1,遵循自包含思想,即只在本处使用的变量,尽量使其只对本处可见。也可以解释成封装性,私有性。
2,程序中多处使用的值不变的变量,可以声明为常量来使用,这样可以提高程序的可读性。
3,占用较多内存的变量,尽量使用自动存储类,这样才可以在使用完之后即刻释放。
总的来说,原则1:程序运行速度快,原则2:程序占用资源少(cpu、内存),原则3:程序结构清晰、互不干扰,原则4:程序可读性强、避免随意的变量声明定义。
除了这些存储类外,程序还可以通过库函数,手工分配内存和手工释放内存。
malloc()函数
接收一个整数,解释为字节数,然后再可用内存中找到一个大小适合的块,返回其首个地址,地址类型为空,可以进行转换。注意,使用本函数分配的内存,只能用free()函数释放。包含于stdlib.h
double * ptd;
ptd=(double *)malloc(30 * sizeof(double));
ptd指针可以像使用数组那样使用其后内存中的地址和数据。
calloc()
接收两个整数参数,一个是表示多少单元,二个是表示每个单元的字节多少。然后再可用内存中找到一个大小适合的块,返回其首个地址,地址类型为空,可以进行转换。注意,使用本函数分配的内存,只能用free()函数释放。包含于stdlib.h
double * ptd;
ptd=(double *)calloc(30,sizeof(double));
ptd指针可以像使用数组那样使用其后内存中的地址和数据。
free()
参数是通过库函数分配的内存的首地址,作用是释放库函数分配的内存。包含于stdlib.h。如果不释放内存,并且程序分配了很大的库函数分配的内存,那么很可能导致内存泄露。
free(ptd);
这种手工分配和释放的内存分配方式叫做动态内存分配,由于内存的管理方式过于动态,因此其产生的程序运行比自动分配的堆栈内存分配的程序慢。
类型限定词
const
声明值为不可修改的限定词。常用于将变量声明为常量。
const double x = 5.5;
也用于指针中,声明指针指向的地址不变或指向的值的是常量。
const float * pf;//指向一个常量浮点数值a,但是可以更改为指向另一个常量浮点数值b,只要是常量浮点数值即可。
float * const pf;//指向一个固定的地址0x11ff,但是地址中存储的数值是可以变化的。如从1.1变成1.2
const float * const pf;//指向一个固定的地址,该地址中存储的数值是一个常量。
volatitle
告诉编译器该变量除了可被程序改变外还可以被其他代理改变。如用于硬件地址和与其他并行运行的程序共享数据:时钟信息等。
使用volatitle关键字的原因是方便编译器优化:如果程序中两次使用x,且没改变x的值,那么编译器将x存入寄存器,以便下一次调用时获得更快的速度。如果声明为volatitle类型,则编译器不做本优化,因为数值可能已经改变。
restrict
只可以用于指针,表明指针是访问一个数据对象的唯一且初始的方式。如果是这样,编译器就可以使用优化方案,加快程序运行速度。
利用malloc()函数创建的数组是具有唯一的指针访问方式的:
int * restrict restar = (int *)malloc(10 * sizeof(int));