一 含义

const修饰的变量在C和C++中的含义是什么?一样吗?

在C中用const修饰的变量仅仅表示这个符合表示的变量不能被赋值,是只读的,那么它与常量有啥区别呢?区别就是一个是常量,一个是变量。常量是不可以被修改的,而变量是可以的,举个例子:

#include <stdio.h>

int main()
{
const int b = ;
int* pb = &b;
*pb = ;
printf("b = %d\n", b); return ;
}

运行结果为:

[root@TransactionTestServer ]# gcc const.c
const.c: In function ‘main’:
const.c:: warning: initialization discards qualifiers from pointer target type
[root@TransactionTestServer ]# ./a.out
b =

可以看出,const修饰的变量本质上还是一个变量,并非常量,其值仍然是可以修改的。

那么在C++中const修饰的变量是常量了吗?上述同样的代码用g++编译结果为:

[root@TransactionTestServer ]# g++ const.c
const.c: In function ‘int main()’:
const.c:: error: invalid conversion from ‘const int*’ to ‘int*’

显然C++中把const修饰的变量当成了常量。再举一个例子:

#include <stdio.h>

int main()
{
const int n = ;
int a[n] = {};
return ;
}

编译结果:

[root@TransactionTestServer ]# gcc array.c
array.c: In function ‘main’:
array.c:: error: variable-sized object may not be initialized
array.c:: warning: excess elements in array initializer
array.c:: warning: (near initialization for ‘a’)
[root@TransactionTestServer ]# g++ array.c
[root@TransactionTestServer ]# vim array.c

可以看出C中直接报错,而C++没说啥。

那么在C与C++中分别是怎么定义常量的呢?

C中的做法是:enum & define

C++ 的做法是:const & inline

C++中有个const_cast可以用来去掉const属性,这个点暂且不说。

二 修饰变量或指针

先来看几个例子,直观的感受下:

)const在前面
  const int nValue; //nValue是const
  const char *pContent; //*pContent是const, pContent可变
  const (char *) pContent;//pContent是const,*pContent可变
  char* const pContent; //pContent是const,*pContent可变
  const char* const pContent; //pContent和*pContent都是const
)const在后面
  int const nValue; // nValue是const
  char const * pContent;// *pContent是const, pContent可变
  (char *) const pContent;//pContent是const,*pContent可变
  char* const pContent;// pContent是const,*pContent可变
  char const* const pContent;// pContent和*pContent都是const

通过这几个例子,我的经验是,它们的样子大抵总是const type value或者type const value,这里const修饰的就是value,也就是说value是只读的,那么容易令人困惑的地方是哪儿呢?就是指针的出现,往往让人搞不清楚哪个是type,哪个是value。比如上述的

const char *pContent,按我以往的习惯,我甚至不会这样写,相反我会这样写const char* pContent,显然,我喜欢把pContent看成一个变量,把char*看成是一个类型,但是这样就不好理解了,所以以后最好还是改成前一种写法,比较好理解,这样就可以把

char看成是type,而把*pContent看成是变量,按上面的const type value就可以看到value是const的,也就是*pContent是const的,所以pContent可以变。按这个思路其他的就都好理解了,除了const (char *)pContent,这个按理来讲应该更好理解,毕竟

加了大括号,显然这里大括号里的应该被当成是type,所以pContent是value,所以pContent是const的。

再举个例子,加深理解:

下面的代码编译器会报一个错误,请问,哪一个语句是错误的呢?
typedef char * pStr;
char string[] = "abc";
const char *p1 = string;
const pStr p2 = string;
p1++;
p2++;

答案与分析:
    问题出在p2++上。pStr是一种新类型,因此p2不可变,p2++是错误的。

继续举例:

#include <stdio.h>

int main()
{ /*const*/ char* name = "hello";
name[] = 'G'; return ;
}

编译并执行:

[root@TransactionTestServer ]# gcc const.c
[root@TransactionTestServer ]# ./a.out
Segmentation fault
[root@TransactionTestServer ]#

可以看到编译器并不会报错,但是执行就段错误了,所以最佳的做法是加上const修饰,这样在编译器就能报错,会是比较好的习惯。

三 const修饰函数

在函数中的应用可能出现在三个位置,函数参数,函数实体修饰,返回值修饰

const returnValue fun(const arraylist) const

1.修饰函数参数

来看实际中的一个例子。
memmove(void *dst, const void *src, size_t len);
这是标准库中的一个函数,用于按字节方式复制字符串(内存)。它的第一个参数,是将字符串复制到哪里去(dest),是目的地,这段内存区域必须是可写。它的第二个参数,是要将什么样的字符串复制出去,我们对这段内存区域只做读取,不写。于是,我们站在这个函数自己的角度来看,src 这个指针,它所指向的内存内所存
储的数据在整个函数执行的过程中是不变。于是src所指向的内容是常量。于是就需要用const修饰。例如,我们这里这样使用它。
const char* s="hello";
char buf[];
memmove(buf,s,); //这里其实应该用strcpy或memcpy更好
如果我们反过来写,
memmove(s,buf,);
那么编译器一定会报错。事实是我们经常会把各种函数的参数顺序写反。编译器在此时帮了我们大忙。如果编译器静悄悄的不报错,(在函数声明处去掉const即可),那么这个程序在运行的时候一定会崩溃。
再看一个复杂的例子
int execv(const char *path, char *const argv[]);
着重看后面这个,argv.它代表什么,根据上面的经验,可以看出这里的value是argv[],argv[]代表的是数组,也就说这个数组是只读的,也就是说数组的每个元素都是只读的,数组的元素是什么呢?
是char*,所以这个数组里的每个指针是常量,但是它指向的数据不是常量。
于是
argv[]=NULL; //非法
argv[][]='a'; //合法

(1)对于非内部数据类型的输入参数,应该将“值传递”的方式改为“const 引用传递”,目的是提高效率。例如将void Func(A a) 改为void Func(const A &a)。
(2)对于内部数据类型的输入参数,不要将“值传递”的方式改为“const 引用传递”。否则既达不到提高效率的目的,又降低了函数的可理解性同时暴露了函数的实现。例如void Func(int x) 不应该改为void Func(const int &x)。

2.修饰函数返回值

(1)如果函数返回值采用“值传递”方式,由于函数会把返回值复制到外部临时的存储单元中,加const修饰没有任何价值。
例如,不要把函数int GetInt(void) 写成const int GetInt(void)。同理不要把函数A GetA(void)写成const A GetA(void),其中A 为用户自定义的数据类型。
(2)如果函数返回值采用“指针传递”方式,那么函数返回值(即指针)的内容不能被修改,该返回值只能被赋给加const 修饰的同类型指针。
例如,定义函数为:const char *GetString(void),那么char *str = GetString()将会出现编译错误。应该写成const char *str = GetString()。
(3)如果函数返回值是采用“引用传递”方式,它的意义在于能提供啊效率,而这种方式使用场合并不多。这个时候,一定要搞清楚函数究竟是想返回一个对象的“拷贝”还是仅返回“别名”就可以了,否则程序会出错。
例如,对于类的重载赋值函数A & operate = (const A &other),如果不加cons修饰,则定义A a, b, c;(a = b) = c,程序合法,但是如果加上const修饰,即const A & operate = (const A &other),则程序会报错。

3.修饰函数体

定义const函数,只需要将const关键字放在函数声明的尾部。任何不会修改类的数据成员的函数都应该声明为const 类型。如果在编写const 成员函数时,不慎修改了数据成员,或者调用了其它非const 成员函数,编译器将报错,这无疑会提高程序的健壮性。

以下是几点使用const的几点规则。
1) const对象只能访问const成员函数,而非const对象可以访问任意的成员函数,包括const成员函数。
2) const对象的成员是不可修改的,然而const对象通过指针维护的对象却是可以修改的。
3) const成员函数不可以修改对象的数据,不管对象是否具有const性质.它在编译时,以是否修改成员数据为依据,进行检查。
4) 然而加上mutable修饰符的数据成员,对于任何情况下通过任何手段都可修改,自然此时的const成员函数是可以修改它的。

最后,说说const的作用。
    const 的好处,是引入了常量的概念,让我们不要去修改不该修改的内存。直接的作用就是让更多的逻辑错误在编译期被发现。所以我们要尽可能的多使用const。

C/C++ const的更多相关文章

  1. openssl 1.1.1 reference

    openssl 1.1.1 include/openssl aes.h: # define HEADER_AES_H aes.h: # define AES_ENCRYPT 1 aes.h: # de ...

  2. const,static,extern 简介

    const,static,extern 简介 一.const与宏的区别: const简介:之前常用的字符串常量,一般是抽成宏,但是苹果不推荐我们抽成宏,推荐我们使用const常量. 执行时刻:宏是预编 ...

  3. C++中的const

    一,C++中const的基本知识 1.C++中const的基本概念 1.const是定义常量的关键字,表示只读,不可以修改. 2.const在定义常量的时候必须要初始化,否则报错,因为常量无法修改,只 ...

  4. const extern static 终极指南

    const extern static 终极指南 不管是从事哪种语言的开发工作,const extern static 这三个关键字的用法和原理都是我们必须明白的.本文将对此做出非常详细的讲解. co ...

  5. const let,console.log('a',a)跟console.log('a'+a)的区别

    const 创建一个只读的常量 let块级作用域 const let重复赋值都会报错 console.log('a',a) a console.log('a'+a) a2 逗号的值会有空格:用加号的值 ...

  6. es6之let和const

    在javascript中,我们都知道使用var来声明变量.javascript是函数级作用域,函数内可以访问函数外的变量,函数外不能访问函数内的变量. 函数级作用域会导致一些问题就是某些代码块内的变量 ...

  7. construction const parameter问题 构造函数const引用参数问题

    工程在window下编译没有任何问题, 但是在linux(CentOS6)下编译就老是报错 C++ 编译器已升级到最新版 6.1.0 错误如下: In file included /bits/stl_ ...

  8. Error:const char* 类型的实参和LPCWSTR类型的形参不兼容的解决方法。

    在C++的Windows 应用程序中经常碰到这种情况. 解决方法: 加入如下转换函数: LPCWSTR stringToLPCWSTR(std::string orig) { size_t origs ...

  9. C#基础知识七之const和readonly关键字

    前言 不知道大家对const和readonly关键字两者的区别了解多少,如果你也不是很清楚的话,那就一起来探讨吧!探讨之前我们先来了解静态常量和动态常量. 静态常量 所谓静态常量就是在编译期间会对变量 ...

  10. const 与 readonly知多少

    原文地址: http://www.cnblogs.com/royenhome/archive/2010/05/22/1741592.html 尽管你写了很多年的C#的代码,但是可能当别人问到你cons ...

随机推荐

  1. Netty学习笔记(六) 简单的聊天室功能之WebSocket客户端开发实例

    在之前的Netty相关学习笔记中,学习了如何去实现聊天室的服务段,这里我们来实现聊天室的客户端,聊天室的客户端使用的是Html5和WebSocket实现,下面我们继续学习. 创建客户端 接着第五个笔记 ...

  2. [Web][DreamweaverCS6][高中同学毕业分布去向网站+服务器上挂载]一、安装与破解DreamweaverCS6+基本规划

    DreamweaverCS6安装与破解 一.背景介绍:同学毕业分布图项目计划简介 哎哎哎,炸么说呢,对于Web前端设计来说,纯手撕html部分代码实在是难受. 对于想做地图这类的就“必须”用这个老工具 ...

  3. 这么小的key-val数据库居然也支持事务——与短跑名将同名的数据库Bolt

    传送门: 柏链项目学院 什么是Bolt?   Bolt是一个纯净的基于go语言编写的key-val数据库,该项目受到LMDB项目的启发,目标是提供一个不需要完整服务器的简单.快速.可靠的数据库.    ...

  4. 高德地图 Service 创建服务 USERKEY_PLAT_NOMATCH

    在使用高的地图 创建服务的时候 { "errmsg": "USERKEY_PLAT_NOMATCH", "errcode": 10009, ...

  5. 安卓(Android)开发基础知识

    .aar文件 .aar是一种压缩文件,和.jar类似,不过它可以包含资源文件,例如图片.drawable.xml资源 .jar文件 在软件领域,JAR文件(Java归档,英语:Java ARchive ...

  6. Cmder--Windows下代替原生的cmd命令行工具

    Cmder是Windows下的命令行工具,用来代替Windows自带的cmd 官网:http://cmder.net/ 这里下载Full版本 https://github.com/cmderdev/c ...

  7. cmd切换目录

    想必大家都用过命令行工具来完成一些骚操作: 今天我在用cmd命令的时候,需要切换不同的目录来获取我所需要的文件,但是发现用cd的话切换不了: 如下图所示,我用cd切换到E盘下的一个文件夹,但是按回车之 ...

  8. LeetCode算法题-Reshape the Matrix(Java实现)

    这是悦乐书的第264次更新,第277篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第131题(顺位题号是566).在MATLAB中,有一个非常有用的函数叫做'reshap ...

  9. tomcat报异常Invalid character found in method name. HTTP method names must be tokens

    最近监控了一下测试环境的日志,突然出现如下一个异常 由Error parsing HTTP request header可以看出是由于解析请求头出错导致的,但是它属于DEBUG级别的异常,虽然不影响系 ...

  10. 给Integer对象加锁的错误方式

    package com.thread.test; public class BadLockOnInteger implements Runnable { public static Integer i ...