跳至主要内容

【转】C语言const使用

作者:浩瀚天涯  来源:   发布日期:2007-04-19  
const是一个C语言的关键字,它限定一个变量不允许被改变。使用const在一定程度上可以提高程序的健壮性,另外,在观看别人代码的时候,清晰理解const所起的作用,对理解对方的程序也有一些帮助。



  虽然这听起来很简单,但实际上,const的使用也是c语言中一个比较微妙的地方,微妙在何处呢?请看下面几个问题。

  问题: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宏,这两个都可以用来定义常量。

  问题: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)、替换1式char, const newType m;

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

  问题:const变量 & 字符串常量

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

char *p = \"i\'m hungry!\";
p[0]= \'I\';

  答案与分析

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

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

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

  答案与分析

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

  问题5:const & 指针

  类型声明中const用来修饰一个常量,有如下两种写法,那么,请问,下面分别用const限定不可变的内容是什么?

  1)、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

  2)、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和指针一起使用是C语言中一个很常见的困惑之处,在实际开发中,特别是在看别人代码的时候,常常会因为这样而不好判断作者的意图,下面讲一下我的判断原则:

  沿着*号划一条线,const和谁在一边,那么谁就是const,即const限定的元素就是它。你可以根据这个规则来看上面声明的实际意义,相信定会一目了然。

  另外,需要注意:对于const (char *) ; 因为char *是一个整体,相当于一个类型(如 char),因此,这是限定指针是const。

评论

此博客中的热门博文

【转】AMBA、AHB、APB总线简介

AMBA 简介 随着深亚微米工艺技术日益成熟,集成电路芯片的规模越来越大。数字IC从基于时序驱动的设计方法,发展到基于IP复用的设计方法,并在SOC设计中得到了广泛应用。在基于IP复用的SoC设计中,片上总线设计是最关键的问题。为此,业界出现了很多片上总线标准。其中,由ARM公司推出的AMBA片上总线受到了广大IP开发商和SoC系统集成者的青睐,已成为一种流行的工业标准片上结构。AMBA规范主要包括了AHB(Advanced High performance Bus)系统总线和APB(Advanced Peripheral Bus)外围总线。   AMBA 片上总线        AMBA 2.0 规范包括四个部分:AHB、ASB、APB和Test Methodology。AHB的相互连接采用了传统的带有主模块和从模块的共享总线,接口与互连功能分离,这对芯片上模块之间的互连具有重要意义。AMBA已不仅是一种总线,更是一种带有接口模块的互连体系。下面将简要介绍比较重要的AHB和APB总线。 基于 AMBA 的片上系统        一个典型的基于AMBA总线的系统框图如图3所示。        大多数挂在总线上的模块(包括处理器)只是单一属性的功能模块:主模块或者从模块。主模块是向从模块发出读写操作的模块,如CPU,DSP等;从模块是接受命令并做出反应的模块,如片上的RAM,AHB/APB 桥等。另外,还有一些模块同时具有两种属性,例如直接存储器存取(DMA)在被编程时是从模块,但在系统读传输数据时必须是主模块。如果总线上存在多个主模块,就需要仲裁器来决定如何控制各种主模块对总线的访问。虽然仲裁规范是AMBA总线规范中的一部分,但具体使用的算法由RTL设计工程师决定,其中两个最常用的算法是固定优先级算法和循环制算法。AHB总线上最多可以有16个主模块和任意多个从模块,如果主模块数目大于16,则需再加一层结构(具体参阅ARM公司推出的Multi-layer AHB规范)。APB 桥既是APB总线上唯一的主模块,也是AHB系统总线上的从模块。其主要功能是锁存来自AHB系统总...

【转】vector成员函数

函数 表述 c.assign(beg,end) c.assign(n,elem) 将 [beg; end) 区间中的数据赋值给 c 。 将 n 个 elem 的拷贝赋值给 c 。 c.at (idx) 传回索引 idx 所指的数据,如果 idx 越界,抛出 out_of_range 。 c.back() 传回最后一个数据,不检查这个数据是否存在。 c.begin() 传回迭代器重的可一个数据。 c.capacity() 返回容器中数据个数。 c.clear() 移除容器中所有数据。 c.empty() 判断容器是否为空。 c.end() 指向迭代器中的最后一个数据地址。 c.erase(pos) c.erase(beg,end) 删除 pos 位置的数据,传回下一个数据的位置。 删除 [beg,end) 区间的数据,传回下一个数据的位置 。 c.front() 传回地一个数据。 get_allocator 使用构造函数返回一个拷贝。 c.insert(pos,elem) c.insert(pos,n,elem) c.insert(pos,beg,end) 在 pos 位置插入一个 elem 拷贝,传回新数据位置。 在 pos 位置插入 n 个 elem 数据。无返回值。 在 pos 位置插入在 [beg,end) 区间的数据。无返回值。 c.max_size() 返回容器中最大数据的数量。 c.pop_back() 删除最后一个数据。 c.push_back(elem) 在尾部加入一个数据。 c.rbegin() 传回一个逆向队列的第一个数据。 c.rend() 传回一个逆向队列的最后一个数据的下一个位置。 c.resize(num) 重新指定队列的长度。 c.reserve() 保留适当的容量。 c.size() 返回容器中实际数据的个数。 c1.swap(c2) swap(c1,c2) 将 c1 和 c2 元素互换。 ...

【转】VxWorks套接口

int m_socket;   // Open a socket        m_socket = socket(AF_INET, SOCK_STREAM, 0);   第一个参数 domain 说明我们网络程序所在的主机采用的通讯协族 (AF_UNIX 和 AF_INET 等 ). AF_UNIX 只能够用于单一的 Unix 系统进程间通信 , 而 AF_INET 是针对 Internet 的 , 因而可以允许在远程主机之间通信 . VxWorks 套接字仅支持 Internet 域地址族 , 不支持 UNIX 域地址族 . 因此在需要 domain 参数的函数中 , 使用 AF_INET 作为函数参数值 . 第二个参数 type 说明我们网络程序所采用的通讯协议 ( SOCK_STREAM , SOCK_DGRAM 等 ). SOCK_STREAM 表明我们用的是 TCP 协议 , 这样会提供按顺序的 , 可靠 , 双向 , 面向连接的比特流 . SOCK_DGRAM  表明我们用的是 UDP 协议 , 这样只会提供定长的 , 不可靠 , 无连接的通信 . 此外,还有 SOCK_RAW 代表是原始协议套接字 . 第三个参数 protocol, 由于我们指定了 type, 所以这个地方我们一般只要用 0 来代替就可以了 . socket 为网络通讯做基本的准备 , 成功打开则返回一个套接字描述符 , 如果失败则返回 ERROR. 套接字描述符是一个标准的 I/O 系统文件描述符 (fd, file descriptor), 可以被 close(), read(), write() 和 ioctl() 函数使用 .   // Make the socket sending alive messages when connected int flag = 1; setsockopt(m_socket, SOL_SOCKET, SO_KEEPALIVE, (char*)&flag, sizeof(flag));   // increase receive buffer siz...