C语言学习笔记(一)变量声明的规则

时间:2022-01-23 22:23:21

声明是告诉编译器有这么个变量,但并不实现。定义就是实现这个变量,真正在内存(堆或栈中)为此变量分配空间

它们的本质区别是:是否分配内存空间,定义需要分配空间,声明不需要分配空间。

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)

解析时两条原则:第一,始终是从内往外读声明符,第二,做选择时,[]和()优先于*