Chinaunix首页 | 论坛 | 博客
  • 博客访问: 200084
  • 博文数量: 51
  • 博客积分: 1435
  • 博客等级: 上尉
  • 技术积分: 590
  • 用 户 组: 普通用户
  • 注册时间: 2011-03-05 18:33
文章分类

全部博文(51)

文章存档

2012年(17)

2011年(34)

分类: C/C++

2012-02-24 17:37:57

本文主要参考c语言深度剖析和c专家编程。
陈老师对这部分写的很好,就使用了拿来主义。感谢原作者。
另外看到有个const的不错的博文http://blog.chinaunix.net/uid-25324849-id-270985.html
也引进了。同样感谢原作者。
-------------------
关键字const 并不能把变量变成常量!在一个符号前加上const 限定符只是表示这个符号不能被赋值,也就是说它的值对于这个符号来说是只读的,但并不防止通过程序的内部的方法来修改这个值。

const最有用的之处就是用它来限定函数的形参,这样该函数将不会修改实参指针所指的数据,但其他的函数却有可能会修改它。
 
 const 限定符 指定的变量的值不能被修改。 
可以对const对象进行初始化,但在初始化后不能进行赋值。const用于声明在只读存储器中的对象,并有可能提高优化的可能性。

对数组而言,const 限定符指定数组的所有元素的值都不能修改,限定符也可配合数组参数使用,它表明函数不能修改数组元素的值。
----

const constant 的缩写,是恒定不变的意思,也翻译为常量、常数等。很不幸,正是因为这一点,很多人都认为被const 修饰的值是常量。这是不精确的,精确的说应该是只读的变量,其值在编译时不能被使用,因为编译器在编译时不知道其存储的内容。或许当初这个关键字应该被替换为readonly。那么这个关键字有什么用处和意义呢?

const 推出的初始目的,正是为了取代预编译指令,消除它的缺点,同时继承它的优点。我们看看它与define 宏的区别。(很多人误以为define 是关键字,在这里我提醒你再回到本章前面看看32 个关键字里是否有define)。

1 const 修饰的只读变量

定义const 只读变量,具有不可变性。

例如:

const int Max=100;

intArray[Max];

(1)问题:const变量  常量

  为什么我象下面的例子一样用一个const变量来初始化数组,ANSI C的编译器会报告一个错误呢?

  const int n = 5;

  int a[n];

  答案与分析:

      1)、这个问题讨论的是常量只读变量的区别。常量肯定是只读的,例如5 “abc”,等,肯定是只读的,因为程序中根本没有地方存放它的值,当然也就不能够去修改它。而只读变量则是在内存中开辟一个地方来存放它的值,只不过这个值由编译器限定不允许被修改。C语言关键字const就是用来限定一个变量不允许被改变的修饰符(Qualifier)。上述代码中变量n被修饰为只读变量,可惜再怎么修饰也不是常量。而ANSI C规定数组定义时维度必须是常量只读变量也是不可以的。

      2)、注意:在ANSI C中,这种写法是错误的,因为数组的大小应该是个常量,而const int n,n只是一个变量(常量 != 不可变的变量,但在标准C++中,这样定义的是一个常量,这种写法是对的),实际上,根据编译过程及内存分配来看,这种用法本来就应该是合理的,只是 ANSI C对数组的规定限制了它。

    3)、那么,在ANSI C 语言中用什么来定义常量呢?答案是enum类型和#define宏,这两个都可以用来定义常量。

这里请在Visual C++6.0 里分别创建.c 文件和.cpp 文件测试一下。你会发现在.c 文件中,编译器会提示出错,而在.cpp 文件中则顺利运行。为什么呢?我们知道定义一个数组必须指定其元素的个数。这也从侧面证实在C 语言中,const 修饰的Max 仍然是变量,只不过是只读属性罢了;而在C++里,扩展了const 的含义,这里就不讨论了。


注意:const 修饰的只读变量必须在定义的同时初始化,想想为什么?

留一个问题:case 语句后面是否可以是const 修饰的只读变量呢?请动手测试一下。

不能

2 节省空间,避免不必要的内存分配,同时提高效率

编译器通常不为普通const 只读变量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的值,没有了存储与读内存的操作,使得它的效率也很高。

例如:

#define M 3          //宏常量

const int N=5;       //此时并未将N 放入内存中

......

int i=N;                //此时为N 分配内存,以后不再分配!

int I=M;                      //预编译期间进行宏替换,分配内存

int j=N;                //没有内存分配

int J=M;                      //再进行宏替换,又一次分配内存!

const 定义的只读变量从汇编的角度来看,只是给出了对应的内存地址,而不是象#define一样给出的是立即数,所以,const 定义的只读变量在程序运行过程中只有一份拷贝(因为它是全局的只读变量,存放在静态区),而#define 定义的宏常量在内存中有若干个拷贝。#define 宏是在预编译阶段进行替换,而const 修饰的只读变量是在编译的时候确定其值。#define 宏没有类型,而const 修饰的只读变量具有特定的类型。

3 修饰一般变量

一般量是指简单类型的只读变量。这种只读变量在定义时,修饰符const 可以用在类型说明符前,也可以用在类型说明符后。例如:

int const i=2; const int i=2;

4 修饰数组

定义或说明一个只读数组可采用如下格式:

int const a[5]={1, 2, 3, 4, 5};

const int a[5]={1, 2, 3, 4, 5};

5 修饰指针

const int *p;                 // p 可变,p 指向的对象不可变

int const *p;                 // p 可变,p 指向的对象不可变

int *const p;                  // p 不可变,p 指向的对象可变

const int *const p;       //指针p p 指向的对象都不可变

在平时的授课中发现学生很难记住这几种情况。这里给出一个记忆和理解的方法:

先忽略类型名(编译器解析的时候也是忽略类型名),我们看const 离哪个近。近水楼台先得月,离谁近就修饰谁。

const int *p;                 //const 修饰*p,p 是指针,*p 是指针指向的对象,不可变

int const *p;                 //const修饰*p,p 是指针,*p 是指针指向的对象,不可变

int *const p;                 //const修饰pp 不可变,p 指向的对象可变

const int *const p;              //前一个const 修饰*p,后一个const 修饰p,指针p p 指向的对象都不可变

6 修饰函数的参数

const 修饰符也可以修饰函数的参数,当不希望这个参数值被函数体内意外改变时使用。例如:

void Fun(const int i);

告诉编译器i 在函数体中的不能改变,从而防止了使用者的一些无意的或错误的修改。

7 修饰函数的返回值

const 修饰符也可以修饰函数的返回值,返回值不可被改变。例如:

const int Fun (void);

在另一连接文件中引用const 只读变量:

extern const int i;                 //正确的声明

extern const int j=10;           //错误!只读变量的值不能改变。

注意这里是声明不是定义,
=============================================

(2)问题:const变量 const 限定的内容

     下面的代码编译器会报一个错误,请问,哪一个语句是错误的呢?

  typedef char * pStr;

  char string[4] = "abc";

  const char *p1 = string;

  const pStr p2 = string;

  p1++;

  p2++;

      答案与分析:

    问题出在p2++上。

    1)const使用的基本形式: const char m;

    限定m不可变。

    2)、替换1式中的m, const char *pm;

    限定*pm不可变,当然pm是可变的,因此问题中p1++是对的。

    3)、替换1char, const newType m;

    限定m不可变,问题中的charptr就是一种新类型,因此问题中p2不可变,p2++是错误的。

 

(3)问题:const变量  字符串常量

    请问下面的代码有什么问题?

  char *p = "i'm hungry!";

  p[0]= 'I';

      答案与分析:

     上面的代码可能会造成内存的非法写操作。分析如下, “i'm hungry”实质上是字符串常量,而常量往往被编译器放在只读的内存区,不可写p初始指向这个只读的内存区,而p[0] = 'I'则企图去写这个地方,编译器当然不会答应。

    

(4) 问题:const变量 & 字符串常量2

     请问char a[3] = "abc" 合法吗?使用它有什么隐患?

      答案与分析:

      在标准C中这是合法的,但是它的生存环境非常狭小;它定义一个大小为3的数组,初始化为“abc”,,注意,它没有通常的字符串终止符'\0',因此这个数组只是看起来像C语言中的字符串,实质上却不是,因此所有对字符串进行处理的函数,比如strcpyprintf等,都不能够被使用在这个假字符串上。

==========================================
另:
  1. foo (const char **p)
  2. {}

  3. main(int argc,char **argv)
  4. {
  5.   foo(argv);
  6. }
实参char *s与形参 const char *p是否相容?
char *cp;
const char *ccp;
ccp = cp;//是否可以赋值?
如果
cp = ccp;//能否赋值呢?否 编译会出现警告
----
实参char **argv 与形参 const char **p是否相容呢?
具体的原因请仔细思考。详细参见c专家编程。
阅读(1589) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~