const 的用法

零. const 出现的原因

编写一个处理基本类型(如,int)的函数时,要选择是传递int类型的值还是传递指向int的指针。通常都是直接传递数值,只有程序需要在函数中改变该数值时,才会传递指针。对于数组别无选择,必须传递指针,因为这样做效率高。如果一个函数按值传递数组,则必须分配足够的空间来储存原数组的副本,然后把原数组所有的数据拷贝至新的数组中。如果把数组的地址传递给函数,让函数直接处理原数组则效率要高。
传递地址会导致一些问题。C通常都按值传递数据,因为这样做可以保证数据的完整性。如果函数使用的是原始数据的副本,就不会意外修改原始数据。但是,处理数组的函数通常都需要使用原始数据,因此这样的函数可以修改原数组。有时,这正是我们需要的。例如,下面的函数给数组的每个元素都加上一个相同的值:

void add_to(double ar[], int n, double val)
{
  int i;
  for (i = 0; i < n; i++)
    ar[i] += val;
}
因此,调用该函数后,prices数组中的每个元素的值都增加了2.5:
add_to(prices, 100, 2.50);
该函数修改了数组中的数据。之所以可以这样做,是因为函数通过指针直接使用了原始数据。

然而,其他函数并不需要修改数据。例如,下面的函数计算数组中所有元素之和,它不用改变数组的数据。但是,由于ar实际上是一个指针,所以编程错误可能会破坏原始数据。例如,下面示例中的ar[i]++会导致数组中每个元素的值都加1:
int sum(int ar[], int n) // 错误的代码
{
  int i;
  int total = 0;
  for( i = 0; i < n; i++)
    total += ar[i]++; //错误递增了每个元素的值
  return total;
}

一. const的概念:
const是一个常量关键字,主要是为了防止所修饰对象被修改。
我们在定义一个变量时,如果想要防止这个变量被修改,可以
用const来修饰这个变量。也就是说,被const修饰过的变量或者
函数,不能对其进行修改,否则,编译器就会报错。

C90标准新增了const关键字,用于限定一个变量为只读 。其声明如下:
const int MONTHS = 12; // MONTHS在程序中不可更改,值为12
这使得MONTHS成为一个只读值。也就是说,可以在计算中使用MONTHS,可以打印MONTHS,但是不能更改MONTHS的值。

用const类型限定符声明的是变量,不是常量。

我们通常用类型和存储类别来描述一个变量。C90 还新增了两个属性:恒常性(constancy)和易变性(volatility)。这两个属性可以分别用关键字 const 和 volatile 来声明,以这两个关键字创建的类型是限定类型(qualified type)。C99标准新增了第3个限定符:restrict,用于提高编译器优化。



二. 对全局数据使用const
    使用全局变量是一种冒险的方法,因为这样做暴露了数据,程序的任何部分都能更改数据。如果把数据设置为 const,就可避免这样的危险,因此用 const 限定符声明全局数据很合理。可以创建const变量、const数组和const结构。

	#define PI1 3.14
	const double PI2 = 3.14;

   const常量与define宏定义的区别:

  1)处理阶段不同:

  define是在预处理阶段,define常量从未被编译器看见,因为在预处理截断就已经替换了;

  const常量在编译阶段使用。

  2)类型和安全检查不同

  define没有类型,不做任何检查,仅仅是字符替换,没有类型安全检查,并且在字符替换时可能会产生意料不到的错误

  const常量有明确的类型,在编译阶段会进行类型检查;

  3)存储方式不同

  define是字符替换,有多少地方使用,就会替换多少次,不会分配内存;

  编译器通常不会为const常量分配空间,只是将它们保存在符号表内,使他们成为一个编译期间的一个常量,没有读取内存的操作,效率也很高;


三. const的修饰

1、修饰常量时:

  const int temp1;  //temp1为常量,不可变

  int const temp2;  //temp2为常量,不可变
2. 修饰数组时:

	const int days[MONTHS] = {31,28,31,30,31,30,31,31,30,31,30,31};
	如果程序稍后尝试改变数组元素的值,编译器将生成一个编译期错误消息:
	days[9] = 44;   /* 编译错误 */

2、修饰指针时:

  主要看const在*的前后,在前则指针指向的内容为常量,在后则指针本身为常量;

  const int *ptr;   //*ptr为常量;

  int const *ptr;   //*ptr为常量;

  int* const ptr;   //ptr为常量;

  const int * const ptr;   //*ptr、ptr均为常量;

例子:

unsigned int * const ptr的应用场景:

#define  GPIOC_DR                              (*(vuint16_t *) (0xE221U))
#define  GPIOE_DR                              (*(vuint16_t *) (0xE241U)
#define  COIL_ENABLE_CONTROL_BIT_MASKS         { 0x2000u, 0x0008u, 0x0004u }


unsigned int * const ptrCoilEnableControlPort[ 3 ] = { (unsigned int *) &GPIOC_DR, (unsigned int *) &GPIOE_DR, (unsigned int *) &GPIOE_DR };
const unsigned int CoilEnableControlBitMask[ 3 ] = COIL_ENABLE_CONTROL_BIT_MASKS;

//byCoilId = 0,1,2
if( ptrCoilEnableControlPort[ byCoilId ] )
{
  
  *( ptrCoilEnableControlPort[ byCoilId ] ) |= CoilEnableControlBitMask[ byCoilId ];
	
}


const int *ptr的应用场景:

//将源字符串加const,表明其为输入参数
char* strcat(char* strDest , const char* strSrc)
{
    //后文return address,故不能放在assert断言之后声明address
    char* address=strDest;
    assert( (strDest!=NULL)&&(strSrc!=NULL) );//对源地址和目的地址加非0断言
    while(*address)//是while(*strDest!=’\0’)的简化形式
    {
        //若使用while(*strDest++),则会出错,因为循环结束后strDest还会执行一次++,
        //那么strDest将指向'\0'的下一个位置。/所以要在循环体内++;因为要使*strDest最后指
        //向该字符串的结束标志’\0’。
        address++;
    }
 
    while(*address++=*strSrc++)
    {
        NULL;//该循环条件内可以用++,
    }//此处可以加语句*strDest=’\0’;无必要
    return strDest;//为了实现链式操作,将目的地址返回
}




四. const 的应用场景
	
	1. 定义不能被修改的变量。
	2. 定义不能被修改的数组。
	3. 对形式参数用const(对输入参数用const,不允许改变输入参数的值)
	4. 修饰函数返回值,防止返回值被改变
	   const int fun();
       接收返回值的变量也必须加const
       const int a = fun(); //接收的变量也要是const的,int a = fun()是错误的

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值