声明是告诉编译器有这么个变量,但并不实现。定义就是实现这个变量,真正在内存(堆或栈中)为此变量分配空间
它们的本质区别是:是否分配内存空间,定义需要分配空间,声明不需要分配空间。
int i;
声明一个i,告诉编译器有一个i
i = 0;
定义i,开辟内存;
int i = 0;
声明和定义同时;
一个完整的变量声明如下:
static const unsigned int a;
其中,static 叫做存储类型
const 叫做类型限定符
unsigned int 叫做类型说明符
a 叫做声明符或者标示符
存储类型有 auto static extern register
类型限定符有 const volatile
类型说明符就是关键字
声明符就不用多说了
存储类型包含了变量的三种属性:生命周期,作用域,是否可被其他文件使用
auto类型的变量,定义在函数外部,作为全局变量时,生命周期是整个程序运行过程,作用域是从声明处到文件结束位置,可以被其他文件使用
定义在函数内部,作为局部变量时,生命周期是整个函数运行过程,作用域是函数体内部,不能被函数体外的部分使用
static类型的变量,定义在函数外部,作为全局变量时,生命周期是整个程序运行过程,作用域是从声明处到文件结束位置,只能被本文件使用
定义在函数内部,作为局部变量时,生命周期是整个函数运行过程,作用域是函数体内部,不能被函数体外的部分使用,
在函数体内部时,只有在第一次调用函数时初始化变量
static类型的函数,说明此函数只能在本文件内使用,外部文件无法使用,这样的好处有两点:第一,更容易维护,以后的人知道,对这个函数进行修改不会对其他文件产生影响,因其他文件无法访问该函数;第二,减少名字空间污染,其他文件中出现和这个函数重名的函数时,也不会引起误解。
extern类型变量 通常是在其他文件中声明,说明声明的变量已经定义过,不需要再次定义,但是,extern int a = 0;这一句等效于 int a = 0
extern类型函数,和不加extern类型的函数是一个效果,说明该函数可以被外部文件使用
register类型变量,说明变量 不是存在内存中,是存在寄存器中,寄存器中没有地址,因此 不能访问变量的地址,定义为register类型的变量,访问速度会快一些。对于需要频繁访问的变量,可以声明为resigister
限定符
const
Const并不是说一定不能改变值,只是说,不能利用变量名称改变内存中的值,但是可以用其他方法改变。例如
int const a = 4;
int *p;
p = &a;
*p = 5;
如果想避免,则
int const a = 4;
int const *p;
p = &a;
*p = 5;
函数传值时使用参数
Char * fun(const char * str)
这个函数是说,在函数体内,不能用指针str修改指向的变量的值,即
int fun(const char *str)
{
*str= 'a'; //error
return0;
}
但是,只是说不应该,并不能完全杜绝,这也是为什么用指针比较恐怖,比较混乱的原因。
不用const存在的问题
void fun(char *str)
{
*str= 'a';
return0;
}
const char c = ‘x’;
fun(&c);
这个程序没有错误,但是把变量c的值改变了,这违背了变量c的const特性
PS:
const 指针可以接受const 和非 const 地址,但是非const 指针只能接受非const 地址。所以const 指针的能力更强一些,所以尽量多用const 指针,这是一种习惯。觉得不对
出现在函数参数中的const 表示在函数体中不能对这个参数做修改。比如上面的例子中strcmp() 函数用来比较两个字符串的大小,在函数体中不应该改变两个参数的值,所以将它定义为是const 的。const 通常用来限制函数的指针参数,引用和数组参数,而一般形式的参数因为形参和实参本来就不在同一内存空间,所以对形参的修改不会影响实参,因此也没有必要限制函数体不能对参数进行修改。
下面是一些使用函数const 参数的例子:
(1) 函数 strcpy() 将 src 字符串的内容复制到targ 字符串中,为保证 src 字符串不被修改,将它定义为 const 参数:
void strcpy ( const char *src , char * targ);
(2) 函数 max() 从数组 array 中找出具有最大值的数组元素并返回这个最大元素的值,为保证数组元素不会在函数中被修改,将它定义为 const 参数:
int max ( const int array[ ], int size);
(3) 函数outputObject( ) 将类 Myclass 的对象 obj 的内容输出。对象定义为 const 引用,即可以保证对象不会在函数体中有所改变,又可以节省对象传递的开销:
void outputObject ( const Myclass &obj) ;
const 返回值
函数返回值为 const 只有用在函数返回为引用的情况。 函数返回值引用常量表示不能将函数调用表达式作为左值使用。例如前面讲的返回引用的函数 min( )。
int & min ( int &i, int &j);
可以对函数调用进行赋值,因为它返回的是左值: min ( a , b )=4;
但是,如果对函数的返回值限定为 const 的:const int & min ( int & i, int &j );
那么,就不能对 min ( a, b ) 调用进行赋值了。
3. const 函数
在类中,可以为类的成员函数进行如下形式的定义:
class classname {
int member ;
public:
int getMember ( ) const ;
};
这里,在函数定义头后面加上的 const 表示这个函数是一个“只读函数”,函数不能改变类对象的状态,不能改变对象的成员变量的值。如在函数体中不能这么写:
classname :: getmember( )
{ member =4 ;
return member;
}
另外,const成员函数也不能在函数中调用其他非const 的函数。
volatile类型
volatile说明一个变量时易变的,主要是和编译器的优化有关,例如:
i = 10;
a = i;
b = i;
编译器任务a和b都是从i读取数据,这期间i没有进行任何操作,因此,在编译器优化的时候,有可能先把i的指放入寄存器当中,第二次直接从寄存器中读取值,而此时也许内存中i的值已经发生变化,这就导致错误。声明成volatile就是保证每次读取i的值都必须从内存中读取。
声明符(标示符)
int *(*x[10])(void)
解析时两条原则:第一,始终是从内往外读声明符,第二,做选择时,[]和()优先于*