const关键字到底该怎么用

前言

我们都知道使用const关键字限定一个变量为只读,但它是真正意义上的只读吗?实际中又该如何使用const关键字?在解答这些问题之前,我们需要先理解const关键字的基本使用。本文说明C中的const关键字,不包括C++。

基本介绍

const是constant的简写,是不变的意思。但并不是说它修饰常量,而是说它限定一个变量为只读。

修饰普通变量

例如:

const int NUM = 10; //与int const NUM等价NUM = 9;  //编译错误,不可再次修改

由于使用了const修饰NUM,使得NUM为只读,因此尝试对NUM再次赋值的操作是非法的,编译器将会报错。正因如此,如果需要使用const修饰一个变量,那么它只能在开始声明时就赋值,否则后面就没有机会了(后面会讲到一个特殊情况)。

修饰数组

例如使用const关键字修饰数组,使其元素不允许被改变:

const int arr[] = {0,0,2,3,4}; //与int const arr[]等价arr[2] = 1; //编译错误

试图修改arr的内容的操作是非法的,编译器将会报错:

error: assignment of read-only location ‘arr[2]’

修饰指针

修饰指针的情况比较多,主要有以下几种情况:
1.const 修饰 *p,指向的对象只读,指针的指向可变:

int a = 9;int b = 10;const int *p = &a;//p是一个指向int类型的const值,与int const *p等价*p = 11;    //编译错误,指向的对象是只读的,不可通过p进行改变p = &b;     //合法,改变了p的指向

这里为了便于理解,可认为const修饰的是*p,通常使用*对指针进行解引用来访问对象,因而,该对象是只读的。

2.const修饰p,指向的对象可变,指针的指向不可变:

int a = 9;int b = 10;int * const p = &a;//p是一个const指针*p = 11;    //合法,p = &b;     //编译错误,p是一个const指针,只读,不可变

3.指针不可改变指向,指向的内容也不可变

int a = 9;int b = 10;const int * const p = &a;//p既是一个const指针,同时也指向了int类型的const值*p = 11;    //编译错误,指向的对象是只读的,不可通过p进行改变p = &b;     //编译错误,p是一个const指针,只读,不可变

看完上面几种情况之后是否会觉得混乱,并且难以记忆呢?我们使用一句话总结:
const放在*的左侧任意位置,限定了该指针指向的对象是只读的;const放在*的右侧,限定了指针本身是只读的,即不可变的。

如果还不是很好理解,我们可以这样来看,去掉类型说明符,查看const修饰的内容,上面三种情况去掉类型说明符int之后,如下:

const *p; //修饰*p,指针指向的对象不可变* const p; //修饰p,指针不可变const * const p; //第一个修饰了*p,第二个修饰了p,两者都不可变

const右边修饰谁,就说明谁是不可变的。上面的说法仅仅是帮助理解和记忆。借助上面这种理解,就会发现以下几种等价情况:

const int NUM = 10; //与int const NUM等价int a = 9;const int *p  = &a;//与int const *p等价const int arr[] = {0,0,2,3,4}; //与int const arr[]等价

const关键字到底有什么用

前面介绍了这么多内容,是不是都常用呢?const关键字到底有什么用?

修饰函数形参

实际上,为我们可以经常发现const关键字的身影,例如很多库函数的声明:

char *strncpy(char *dest,const char *src,size_t n);//字符串拷贝函数int  *strncmp(const char *s1,const char *s2,size_t n);//字符串比较函数

通过看strncpy函数的原型可以知道,源字符串src是只读的,不可变的,而dest并没有该限制。我们通过一个小例子继续观察:

//test.c#include<stdio.h>void myPrint(const char *str);void myPrint(const char *str){    str[0] = ‘H‘;    printf("my print:%s\n",str);}int main(void){    char str[] = "hello world";    myPrint(str);    return 0;}

在这个例子中,我们不希望myPrint函数修改传入的字符串内容,因此入参使用了const限定符,表明传入的字符串是只读的,因此,如果myPrint函数内部如果尝试对str进行修改,将会报错:

$ gcc -o test test.ctest.c:6:12: error: assignment of read-only location ‘*str’     str[0] = ‘H‘;

因此,我们自己在编码过程中,如果确定传入的指针参数仅用于访问数据,那么应该将其声明为一个指向const限定类型的指针,避免函数内部对数据进行意外地修改

修饰全局变量

我们知道,使用全局变量是一种不安全的做法,因为程序的任何部分都能够对全局数据进行修改。而如果对全局变量增加const限定符(假设该全局数据不希望被修改),就可以避免被程序其他部分修改。这里有两种使用方式。
第一种,在a文件中定义,其他文件中使用外部声明,例如:
a.h

//a.hconst int ARR[] = {0,1,2,3,4,5,6,7,8,9};  //定义int数组

b.c

//b.cextern const int ARR[];   //注意,这里不能再对ARR进行赋值//后面可以使用ARR

第二种,在a文件中定义,并使用static修饰,b文件包含a文件,例如:
a.h

//a.hstatic const int ARR[] = {0,1,2,3,4,5,6,7,8,9};  //定义int数组

b.c

//b.c#include<a.h>//后面可以使用ARR

注意,这里必须使用static修饰,否则多个文件包含导致编译会出现重复定义的错误。有兴趣的可以尝试一下。

const修饰的变量是真正的只读吗?

使用const修饰之后的变量真的是完全的只读吗?看下面这个例子:

#include <stdio.h>int main(void){    const int a = 2018;    int *p = &a;    *p = 2019;    printf("%d\n",a);    return 0;}

运行结果:

2019

可以看到,我们通过另外定义一个指针变量,将被const修饰的a的值改变了。那么我们不禁要问,const到底做了什么呢?它修饰的变量是真正意义上的只读吗?为什么它修饰的变量的值仍然可以改变?

#include<stdio.h>int main(void){    int a = 2019;    //const int a = 2019;    printf("%d\n",a);    return 0;}

我们分别获取有const修饰和无const修饰的汇编代码。
无const修饰,汇编代码:

.LC0:        .string "%d\n"main:        push    rbp        mov     rbp, rsp        sub     rsp, 16        mov     DWORD PTR [rbp-4], 2019        mov     eax, DWORD PTR [rbp-4]        mov     esi, eax        mov     edi, OFFSET FLAT:.LC0        mov     eax, 0        call    printf        mov     eax, 0        leave        ret

有const修饰,汇编代码:

.LC0:        .string "%d\n"main:        push    rbp        mov     rbp, rsp        sub     rsp, 16        mov     DWORD PTR [rbp-4], 2019        mov     eax, DWORD PTR [rbp-4]        mov     esi, eax        mov     edi, OFFSET FLAT:.LC0        mov     eax, 0        call    printf        mov     eax, 0        leave        ret

我们发现,并没有任何差异!当然这一个例子并不能说明所有的问题。但是我们要知道的是,const关键字告诉了编译器,它修饰的变量不能被改变,如果代码中发现有类似改变该变量的操作,那么编译器就会捕捉这个错误。

那么它在实际中的意义之一是什么呢?帮助程序员提前发现问题,避免不该修改的值被意外地修改,但是无法完全保证不被修改!例如我们可以通过对指针进行强转:

#include<stdio.h>void myPrint(const char *str);void myPrint(const char *str){    char *b = (char *)str;    b[0] = ‘H‘;    printf("my print:%s\n",b);

}int main(void){    char str[] = "hello world";    myPrint(str);    return 0;}

运行结果:

my print:Hello world

也就是说,const关键字是给编译器用的,帮助程序员提早发现可能存在的问题。
但是实际中永远不要写这样的代码!

总结

介绍了这么多,关键点如下:

  • const关键字让编译器帮助我们发现变量不该被修改却被意外修改的错误。
  • const关键字修饰的变量并非真正意义完完全全的只读。
  • 对于不该被修改的入参,应该用const修饰,这是const使用的常见姿势。
  • const修饰的变量只能正常赋值一次。
  • 不要试图将非const数据的地址赋给普通指针。
  • 不要忽略编译器的警告,除非你很清楚在做什么。
  • 虽然可以通过某种不正规途径修改const修饰的变量,但是永远不要这么做

思考

  • 与#define相比,const有什么优点?
  • const关键字到底该什么时候用?
微信公众号【编程珠玑】:专注但不限于分享计算机编程基础,Linux,C语言,C++,算法,数据库等编程相关[原创]技术文章,号内包含大量经典电子书和视频学习资源。欢迎一起交流学习,一起修炼计算机“内功”,知其然,更知其所以然。

原文地址:https://www.cnblogs.com/bianchengzhuji/p/10335837.html

时间: 2024-08-05 03:05:34

const关键字到底该怎么用的相关文章

C++中关于const关键字的使用

const关键字用于表示变量或函数, 亦或其他类型的数据其值或其本身的值禁止被修改. 但需要注意的是, 即使被修饰为const类型的变量, 其本质依旧是个变量, 而不是常量. 也许你会说, 这有什么区别呢? 我在代码中感觉不出差别啊. 其实它们的差别在于编译器的编译. 对于编译器来说, 变量意味着需要分配内存空间, 而常量只是在编译期间记录在编译器的一张内存表里面的一个证整数而已.  变量的值必须初始化, 除非使用extern修饰它, 这就告诉编译器该变量是一个全局const变量, 在哪里定义编

C与C++的const关键字

之所以要谈const,大概就是被const出现的各种位置搞得头晕目眩了罢. (一)C语言中的const关键字 C语言中const修饰的变量或对象的值不能更改. 比如定义const int i = 0;(或int const i = 0;)那么之后任何对i进行改变的操作都会报错. 当然可以投机取巧地去修改const类型的值,比如取得i的指针然后对指针进行操作 const int i = 0; int * p = &i; *p = 3; const也可以修饰指针类型,这就产生了比较麻烦的情况,那就是

C语言之const关键字

const关键字在C代码中很常见,下面总结一下其作用: 1.将一个变量声明为常量,即只读的.这一点跟#define 是相似的,如:   const int a; 这时候,a就被视为一个常量,具有不可修改的性质.这个声明的问题是没有对a初始化,编译器给a分配的空间,以前是什么值,默认就是什么值,所以以后要给a赋值会报错. 恰当的声明应该是这样的:     const int a = 3; 即,声明的同时进行初始化.需要注意的是,int const a = 3; 效果是一样的,在这种情形下,cons

C++ 类中的const关键字

//类中的const关键字 #include<iostream> using namespace std; class Point{ public: //这个const关键字本质上修饰的是this指针 int GetX() const //====>int GetX(const this) { //因为this被隐藏,所以const关键字只好写在函数后面 //x++; //加上const关键字 报错 error C3490: 由于正在通过常量对象访问“x”,因此无法对其进行修改 //这个

c语言const关键字

作者:沉默羔羊 c语言const关键字: --  特点. 使用该关键修饰的变量不能被改变.      --指针常量. demo:   const char *p或者是 char const * p --常量指针. demo: char * const p      --常量指针和指针常量. 1.常量指针指的是如 char * const p, p是一个常量,p不能改变指向. 2.指针常量指的是如char const *p,   p指向一个字符常量,p可以改变指向.但是不能改变*p. --对于常量

const关键字详解

const在函数前与函数后的区别 一   const基础         如果const关键字不涉及到指针,我们很好理解,下面是涉及到指针的情况:         int   b   =   500;     const   int*   a   =   &b;              [1]     int   const   *a   =   &b;              [2]     int*   const   a   =   &b;              [3

c++中const关键字全面总结

一.const作用 1.const定义常量 注意:const只对它左边的东西起作用,唯一的例外就是const本身就是最左边的修饰符,那么它才会对右边的东西起作用. (1)const修饰变量,以下两种定义形式在本质上是一样的.它的含义是:const修饰的类型为TYPE的变量value是不可变的. TYPE const ValueName = value; const TYPE ValueName = value; (2)将const改为外部连接,作用于扩大至全局,编译时会分配内存,并且可以不进行初

详解C和C++中的const关键字

在讲下面的内容之前,要先看看这篇文章const.static变量的存储位置 一.C中的const 作用:(1)可以定义const常量,具有不可变性. (2)便于进行类型检查,使编译器对处理内容有更多了解,消除了一些隐患. (3)可以避免意义模糊的数字出现,同样可以很方便地进行参数的调整和修改. 同宏定义一样,可以做到不变则已,一变都变! (4)可以保护被修饰的东西,防止意外的修改,增强程序的健壮性. 还是上面的例子,如果在函数体内修改了i,编译器就会报错: (5) 可以节省空间,避免不必要的内存

C语言之头文件,static与const关键字

[前言] 最近几个月在做一个C语言代码重构的项目,过程中也让我对之前在书本上学习到的东西有些补充和巩固,在本博中总结记录下,梳理下零碎的知识点和经验也加深印象,书写是为了更好地思考.平时也都是用印象笔记之类的自己写写笔记,今天整理成博客,以便自己以后回顾. [正文] C语言头文件:将其理解为C语言模块化编程向外提供一个接口描述文件.一般包括一些供外部函数或者文件使用的变量声明,函数声明,数据类型定义以及宏定义等.举例: /*************************************