zoukankan      html  css  js  c++  java
  • const在C语言中的用法

     程序的局部变量存在于(堆栈)中,全局变量存在于(静态区 )中,动态申请数据存在于( 堆)中。

    1.作用于变量:

       用static声明局部变量-------局部变量指在代码块{}内部定义的变量,只在代码块内部有效(作用域),其缺省的存储方式是自动变量或说是动态存储的,即指令执行到变量定义处时才给变量分配存储单元,跳出代码块时释放内存单元(生命期)。用static声明局部变量时,则改变变量的存储方式(生命期),使变量成为静态的局部变量,即编译时就为变量分配内存,直到程序退出才释放存储单元。这样,使得该局部变量有记忆功能,可以记忆上次的数据,不过由于仍是局部变量,因而只能在代码块内部使用(作用域不变)。

       用static声明外部变量-------外部变量指在所有代码块{}之外定义的变量,它缺省为静态变量,编译时分配内存,程序结束时释放内存单元。同时其作用域很广,整个文件都有效甚至别的文件也能引用它。为了限制某些外部变量的作用域,使其只在本文件中有效,而不能被其他文件引用,可以用static关键字对其作出声明。

      总结:用static声明局部变量,使其变为静态存储方式(静态数据区),作用域不变;用static声明外部变量,其本身就是静态变量,这只会改变其连接方式,使其只在本文件内部有效,而其他文件不可连接或引用该变量。

    2.作用于函数:

      使用static用于函数定义时,对函数的连接方式产生影响,使得函数只在本文件内部有效,对其他文件是不可见的。这样的函数又叫作静态函数。使用静态函数的好处是,不用担心与其他文件的同名函数产生干扰,另外也是对函数本身的一种保护机制。

      如果想要其他文件可以引用本地函数,则要在函数定义时使用关键字extern,表示该函数是外部函数,可供其他文件调用。另外在要引用别的文件中定义的外部函数的文件中,使用extern声明要用的外部函数即可。

    const作用: “只读(readonly)”

    1.定义常量

    (1)const

    修饰变量,以下两种定义形式在本质上是一样的。它的含义是:const修饰的类型为TYPE的变量value是不可变的,readonly。

    TYPE const ValueName = value;  

    const TYPE ValueName = value;

    (2)将const改为外部连接,作用于扩大至全局,编译时会分配内存,并且可以不进行初始化,仅仅作为声明,编译器认为在程序其他地方进行了定义. 

    extend const int ValueName = value;

    2.指针使用CONST

    (1)指针本身是常量不可变

    char * const pContent;  

    const (char*) pContent; 

    (2)指针所指向的内容是常量不可变

    const char *pContent;  

    char const *pContent;  

    (3)两者都不可变

    const char* const pContent;  

    (4)还有其中区别方法,沿着*号划一条线:如果const位于*的左侧,则const就是用来修饰指针所指向的变量,即指针指向为常量;如果const位于*的右侧,const就是修饰指针本身,即指针本身是常量。

    3.函数中使用CONST

    (1)const修饰函数参数

    a.传递过来的参数在函数内不可以改变(无意义,因为Var本身就是形参)

    void function(const int Var); 

    b.参数指针所指内容为常量不可变

    void function(const char* Var);  

    c.参数指针本身为常量不可变(也无意义,因为char* Var也是形参)

    void function(char* const Var); 

    d.参数为引用,为了增加效率同时防止修改。修饰引用参数时:

    void function(const Class& Var); //引用参数在函数内不可以改变

    void function(const TYPE& Var); //引用参数在函数内为常量不可变

    这样的一个const引用传递和最普通的函数按值传递的效果是一模一样的,他禁止对引用

    的对象的一切修改,唯一不同的是按值传递会先建立一个类对象的副本, 然后传递过去,而它直接传递地址,所以这种传递比按值传递更有效.另外只有引用的const传递可以传递一个临时对象,因为临时对象都是const属性, 且是不可见的,他短时间存在一个局部域中,所以不能使用指针,只有引用的const传递能够捕捉到这个家伙.

    (2)const 修饰函数返回值

    const修饰函数返回值其实用的并不是很多,它的含义和const修饰普通变量以及指针的含义基本相同。

    a.const int fun1() //这个其实无意义,因为参数返回本身就是赋值。

    b.const int * fun2() //调用时 const int *pValue = fun2(); //我们可以把fun2()看作成一个变量,即指针内容不可变。

    c.int* const fun3() //调用时int * const pValue = fun2(); //我们可以把fun2()看作成一个变量,即指针本身不可变。

    const在C语言中算是一个比较新的描述符,我们称之为常量修饰符,意即其所修饰的对象为常量(immutable)。



    我们来分情况看语法上它该如何被使用。


    1、函数体内修饰局部变量。
    例:
    void func(){
    const int a=0;
    }


    首先,我们先把const这个单词忽略不看,那么a是一个int类型的局部自动变量,
    我们给它赋予初始值0。


    然后再看const.


    const作为一个类型限定词,和int有相同的地位。
    const int a;
    int const a;
    是等价的。于是此处我们一定要清晰的明白,const修饰的对象是谁,是a,和int没
    有关系。const 要求他所修饰的对象为常量,不可被改变,不可被赋值,不可作为左值(l-value)。
    这样的写法也是错误的。
    const int a;
    a=0;
    这是一个很常见的使用方式:
    const double pi=3.14;
    在程序的后面如果企图对pi再次赋值或者修改就会出错。


    然后看一个稍微复杂的例子。
    const int* p;
    还是先去掉const 修饰符号。
    注意,下面两个是等价的。
    int* p;
    int *p;
    其实我们想要说的是,*p是int类型。那么显然,p就是指向int的指针。
    同理
    const int* p;
    其实等价于
    const int (*p);
    int const (*p);
    即,*p是常量。也就是说,p指向的数据是常量。
    于是
    p+=8; //合法
    *p=3; //非法,p指向的数据是常量。


    那么如何声明一个自身是常量指针呢?方法是让const尽可能的靠近p;
    int* const p;
    const右面只有p,显然,它修饰的是p,说明p不可被更改。然后把const去掉,可以
    看出p是一个指向 int形式变量的指针。
    于是
    p+=8; //非法
    *p=3; //合法


    再看一个更复杂的例子,它是上面二者的综合
    const int* const p;
    说明p自己是常量,且p指向的变量也是常量。
    于是
    p+=8; //非法
    *p=3; //非法


    const 还有一个作用就是用于修饰常量静态字符串。
    例如:
    const char* name=David;
    如果没有const,我们可能会在后面有意无意的写name[4]='x'这样的语句,这样会
    导致对只读内存区域的赋值,然后程序会立刻异常终止。有了 const,这个错误就
    能在程序被编译的时候就立即检查出来,这就是const的好处。让逻辑错误在编译
    期被发现。


    const 还可以用来修饰数组
    const char s[]=David;
    与上面有类似的作用。


    2、在函数声明时修饰参数
    来看实际中的一个例子。
    NAME
    memmove -- copy byte string


    LIBRARY
    Standard C Library (libc, -lc)


    SYNOPSIS
    #include


    void *
    memmove(void *dst, const void *src, size_t len);


    这是标准库中的一个函数,用于按字节方式复制字符串(内存)。
    它的第一个参数,是将字符串复制到哪里去(dest),是目的地,这段内存区域必须
    是可写。
    它的第二个参数,是要将什么样的字符串复制出去,我们对这段内存区域只做读
    取,不写。
    于是,我们站在这个函数自己的角度来看,src 这个指针,它所指向的内存内所存
    储的数据在整个函数执行的过程中是不变。于是src所指向的内容是常量。于是就
    需要用const修饰。
    例如,我们这里这样使用它。
    const char* s=hello;
    char buf[100];
    memmove(buf,s,6); //这里其实应该用strcpy或memcpy更好


    如果我们反过来写,
    memmove(s,buf,6);
    那么编译器一定会报错。事实是我们经常会把各种函数的参数顺序写反。事实是编
    译器在此时帮了我们大忙。如果编译器静悄悄的不报错,(在函数声明处去掉
    const即可),那么这个程序在运行的时候一定会崩溃。


    这里还要说明的一点是在函数参数声明中const一般用来声明指针而不是变量本身。
    例如,上面的size_t len,在函数实现的时候可以完全不用更改len的值,那么是否
    应该把len也声明为常量呢?可以,可以这么做。我们来分析这么做有什么优劣。
    如果加了const,那么对于这个函数的实现者,可以防止他在实现这个函数的时候修
    改不需要修改的值(len),这样很好。
    但是对于这个函数的使用者,
    1。这个修饰符号毫无意义,我们可以传递一个常量整数或者一个非常量整数过
    去,反正对方获得的只是我们传递的一个copy。
    2。暴露了实现。我不需要知道你在实现这个函数的时候是否修改过len的值。


    所以,const一般只用来修饰指针。


    再看一个复杂的例子
    int execv(const char *path, char *const argv[]);
    着重看后面这个,argv.它代表什么。
    如果去掉const,我们可以看出
    char * argv[];
    argv是一个数组,它的每个元素都是char *类型的指针。
    如果加上const.那么const修饰的是谁呢?他修饰的是一个数组,argv[],意思就是
    说这个数组的元素是只读的。那么数组的元素的是什么类型呢?是char *类型的指
    针.也就是说指针是常量,而它指向的数据不是。
    于是
    argv[1]=NULL; //非法
    argv[0][0]='a'; //合法




    3、全局变量。
    我们的原则依然是,尽可能少的使用全局变量。
    我们的第二条规则 则是,尽可能多的使用const。
    如果一个全局变量只在本文件中使用,那么用法和前面所说的函数局部变量没有什
    么区别。
    如果它要在多个文件间共享,那么就牵扯到一个存储类型的问题。


    有两种方式。
    1.使用extern
    例如
    /* file1.h */
    extern const double pi;
    /* file1.c */
    const double pi=3.14;
    然后其他需要使用pi这个变量的,包含file1.h
    #include file1.h
    或者,自己把那句声明复制一遍就好。
    这样做的结果是,整个程序链接完后,所有需要使用pi这个变量的共享一个存储区域。


    2.使用static,静态外部存储类
    /* constant.h */
    static const pi=3.14;
    需要使用这个变量的*.c文件中,必须包含这个头文件。
    前面的static一定不能少。否则链接的时候会报告说该变量被多次定义。
    这样做的结果是,每个包含了constant.h的*.c文件,都有一份该变量自己的copy,
    该变量实际上还是被定义了多次,占用了多个存储空间,不过在加了static关键字
    后,解决了文件间重定义的冲突。
    坏处是浪费了存储空间,导致链接完后的可执行文件变大。但是通常,这个,小小
    几字节的变化,不是问题。
    好处是,你不用关心这个变量是在哪个文件中被初始化的。

      C++ const 允许指定一个语义约束,编译器会强制实施这个约束,允许程序员告诉编译器某值是保持不变的。如果在编程中确实有某个值保持不变,就应该明确使用const,这样可以获得编译器的帮助。

    1.const 修饰成员变量 

    复制代码
     1 #include<iostream>
     2 using namespace std;
     3 int main(){
     4     int a1=3;   ///non-const data
     5     const int a2=a1;    ///const data
     6 
     7     int * a3 = &a1;   ///non-const data,non-const pointer
     8     const int * a4 = &a1;   ///const data,non-const pointer
     9     int * const a5 = &a1;   ///non-const data,const pointer
    10     int const * const a6 = &a1;   ///const data,const pointer
    11     const int * const a7 = &a1;   ///const data,const pointer
    12 
    13     return 0;
    14 }
    复制代码

    const修饰指针变量时:

      (1)只有一个const,如果const位于*左侧,表示指针所指数据是常量,不能通过解引用修改该数据;指针本身是变量,可以指向其他的内存单元。

      (2)只有一个const,如果const位于*右侧,表示指针本身是常量,不能指向其他内存地址;指针所指的数据可以通过解引用修改。

      (3)两个const,*左右各一个,表示指针和指针所指数据都不能修改。

    2.const修饰函数参数

      传递过来的参数在函数内不可以改变,与上面修饰变量时的性质一样。

    void testModifyConst(const int _x) {
         _x=5;   ///编译出错
    }

    3.const修饰成员函数

    (1)const修饰的成员函数不能修改任何的成员变量(mutable修饰的变量除外)

    (2)const成员函数不能调用非const成员函数,因为非const成员函数可以会修改成员变量

    复制代码
     1 #include <iostream>
     2 using namespace std;
     3 class Point{
     4     public :
     5     Point(int _x):x(_x){}
     6 
     7     void testConstFunction(int _x) const{
     8 
     9         ///错误,在const成员函数中,不能修改任何类成员变量
    10         x=_x;
    11 
    12         ///错误,const成员函数不能调用非onst成员函数,因为非const成员函数可以会修改成员变量
    13         modify_x(_x);
    14     }
    15 
    16     void modify_x(int _x){
    17         x=_x;
    18     }
    19 
    20     int x;
    21 };
    复制代码

     4.const修饰函数返回值

    (1)指针传递

    如果返回const data,non-const pointer,返回值也必须赋给const data,non-const pointer。因为指针指向的数据是常量不能修改。

    复制代码
     1 const int * mallocA(){  ///const data,non-const pointer
     2     int *a=new int(2);
     3     return a;
     4 }
     5 
     6 int main()
     7 {
     8     const int *a = mallocA();
     9     ///int *b = mallocA();  ///编译错误
    10     return 0;
    11 }
    复制代码

    (2)值传递

     如果函数返回值采用“值传递方式”,由于函数会把返回值复制到外部临时的存储单元中,加const 修饰没有任何价值。所以,对于值传递来说,加const没有太多意义。

    所以:

      不要把函数int GetInt(void) 写成const int GetInt(void)。
      不要把函数A GetA(void) 写成const A GetA(void),其中A 为用户自定义的数据类型。

     const用法主要是防止定义的对象再次被修改,定义对象变量时要初始化变量

    下面我就介绍一下几种常见的用法

    1.用于定义常量变量,这样这个变量在后面就不可以再被修改

     const int Val = 10;

     //Val = 20; //错误,不可被修改

    2. 保护传参时参数不被修改,如果使用引用传递参数或按地址传递参数给一个函数,在这个函数里这个参数的值若被修改,

    则函数外部传进来的变量的值也发生改变,若想保护传进来的变量不被修改,可以使用const保护

     void  fun1(const int &val)

      {

         //val = 10; //出错

    }

    void fun2(int &val)

    {

       val = 10; //没有出错

    }

    void main()

    {

       int a = 2;

       int b = 2;

       fun1(a); //因为出错,这个函数结束时a的值还是2

       fun2(b);//因为没有出错,函数结束时b的值为10

    }

    如果只想把值传给函数,而且这个不能被修改,则可以使用const保护变量,有人会问为什么不按值传递,按值传递还需要把这个值复制一遍,

    而引用不需要,使用引用是为了提高效率//如果按值传递的话,没必要加const,那样根本没意义

    3. 节约内存空间,

     #define  PI  3.14 //使用#define宏

     const double Pi = 3.14 //使用const,这时候Pi并没有放入内存中

     double  a = Pi;  //这时候才为Pi分配内存,不过后面再有这样的定义也不会再分配内存

     double  b = PI;  //编译时分配内存

     double  c = Pi;  //不会再分配内存,

     double  d = PI;  //编译时再分配内存

    const定义的变量,系统只为它分配一次内存,而使用#define定义的常量宏,能分配好多次,这样const就很节约空间

    4.类中使用const修饰函数防止修改非static类成员变量

     class

    {

     public:

      void fun() const //加const修饰

       {

         a = 10; //出错,不可修改非static变量

         b = 10; //对,可以修改

    }

     private:

      int  a ;

      static int b;

    }

    5.修饰指针
    const int *A; 或 int const *A;  //const修饰指向的对象,A可变,A指向的对象不可变
    int *const A;               //const修饰指针A, A不可变,A指向的对象可变 
    const int *const A;           //指针A和A指向的对象都不可变

     

    6.修饰函数返回值,防止返回值被改变

      const int fun();

      接收返回值的变量也必须加const

      const int a = fun(); //接收的变量也要是const的,int a = fun()是错误的

    7.修饰类的成员变量

      使用const修饰的变量必须初始化,在类中又不能在定义时初始化,

    如;

    class

    {

    private:

      int a = 10;

      const int b = 10;

      static const int c = 10;

    //这样初始化都是错的,

    }

    初始化const int类型(没有static),在类的构造函数上初始化

    Class Test

    {

    Public:

      Test():b(23) //构造函数上初始化b的值为23

       {

    }

    private:

         const int b ;

    }

    初始化staticconst int这个类型的(带有static的),在类的外面初始化

    class Test

    {

    private:

      static const int c;

    const int Test::c=10; //类的外部初始化c为10

    8.const定义的对象变量只能作用于这个程序该C/C++文件,不能被该程序的其他C/C++文件调用,

     如file1.cpp中 const int val;

     在file2.cpp中, extern intval; //错误,无法调用,

    要想const定义的对象变量能被其他文件调用,定义时必须使用extern修饰为

    extern const int val;

    非const变量默认为extern,要是const能被其他文件访问必须显示指定为extern

  • 相关阅读:
    加班的价值
    webApp 阅读器项目实践
    Oak Seeds 网站项目回顾
    [Echarts]用Echarts绘制饼状图
    [转载] 编程每一天(Write Code Every Day)
    对杀毒软件技术的浅浅理解
    记我的第二次自动化尝试——selenium+pageobject+pagefactory实现自动化下单、退款、撤销回归测试
    学习Selenium遇到的问题和解决方案
    记我的第一次自动化尝试
    jmeter环境配置、使用以及参数化之CSV Data Set Config
  • 原文地址:https://www.cnblogs.com/yuandongtao1989/p/6673101.html
Copyright © 2011-2022 走看看