const 的用法 绝地灬酷狼 2022-03-14 12:12 209阅读 0赞 零. 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()是错误的
还没有评论,来说两句吧...