『C语言进阶』const详解

简介: 『C语言进阶』const详解

一、什么是const

常类型,使用类型修饰符const说明的类型,常类型的变量或者对象的值是不能被更新的。

const含义:只要一个变量前用const来修饰,就意味着该变量里的数据只能被访问,而不能被修改,也就是意味着“只读”(readonly)


二、const的优点

  1. 定义常量:
const int n=100;
  1. 类型检查:const常量与#define宏定义常量区别:
  • const定义变量类型只有整数或者枚举,并且常量表达初始化才为常量表达式
  • 预编译指令只是对值进行简单的替换,不能进行类型检查,相比之下,被const修饰的变量有自己的类型,编译器可以通过安全检查
  • 其余情况下const常量只是一个被const限定的变量,不能与常量混肴
  1. 可以保护被修饰的东西,防止意外修改,增强程序的健壮性
  2. 可以避免不必要的内存分配,编译器通常不为被const修饰的变量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有了存储与读内存的操作,使得它的效率也很高。

三、const的用法:

3.1 修饰全局变量

此时全局变量只能使用不可修改,直接修改(编译报错)与修改地址(程序异常)都不行

#include <stdio.h>
//const修饰全局变量
//此时全局变量只能使用不可修改,直接修改(编译报错)与修改地址(程序异常)也不行
const int a = 100;
void test()
{
    printf("a = %d\n", a);//直接使用
    //a = 200;//直接修改,编译报错,显示a是只读的
    //printf("a = %d\n", a);
    //int *p = &a;
    //*p = 300;//通过地址修改,程序异常,有些编译器可能只会显示警告,并不会报错,且程序能正常运行,但是不会显示任何结果
    //printf("a = %d\n", a);
}
int main()
{
  test();
  return 0;
}

全局变量的作用域是整个文件,我们应该尽量避免使用全局变量,因为一旦有一个函数改变了全局变量的值,它也会影响到其他引用这个变量的函数,导致除了bug后很难发现,如果一定要用全局变量,我们应该尽量的使用const修饰符进行修饰,这样防止不必要的人为修改。

3.2 修饰局部变量

不可直接修改变量的值(编译报错),但是可以通过地址进行修改

const int n = 5;
int const n = 5;

这两种写法是一样的,都是表示变量n的值不能被改变了,需要注意的是,用const修饰变量时,一定要给变量初始化,否则之后就不能再进行赋值了。

#include<stdio.h>
//const修饰普通局部变量
//不可直接修改(编译报错),但是可以通过地址进行修改
void test()
{
    const int b = 100;
    printf("b = %d\n", b);
    //b = 200;//直接修改,编译报错
    //printf("b = %d\n", b);
    int *p = &b;
    *p = 300;//通过地址进行修改
    printf("b = %d\n", b);
}
int main()
{
  test();
    return 0;
}

接下来看看const用于修饰常量静态字符串,例如:

const char* str = "fdsafdsa";

如果没有const的修饰,我们可能会在后面有意无意的写str[4]=’x’类似这样的语句,这样会导致对只读内存区域的赋值,然后程序会立刻异常终止。有了const,这个错误就能在程序被编译的时候就立即检查出来,这就是const的好处。让逻辑错误在编译期被发现。

3.3 修饰指针变量的类型(即常量指针)

const修饰指针变量的类型,可直接修改变量值,不可修改指针指向的地址里的内容(编译报错),可以修改指针的指向

常量指针是指针指向的内容是常量,可以有以下两种定义方式:

const int * n;
int const * n;

可以发现,无论是在int前还是在int后,const一直位于*之前

#include<stdio.h>
void test()
{
    int c = 100;
    const int *p = &c;
    printf("c = %d\n",c);
    c = 111;//可以直接修改变量的值
    printf("c = %d\n",c);
    //*p = 200;//不可修改指针指向的地址里的内容,编译报错
    //printf("c = %d\n",c);
    int d =300;
    p = &d;//可以修改指针的指向
    printf("*p = %d\n",*p);
}
int main()
{
  test();
    return 0;
}

3.4 修饰指针变量(即指针常量)

可以直接修改变量的值,可以修改指针指向地址的内容,但是不能修改指针的指向(编译报错)

指针常量是指指针本身是个常量,不能在指向其他的地址,写法如下:

int *const n;
#include<stdio.h>
void test()
{
    int c = 200;
    int  * const p = &c;//const修饰指针,即指针常量
    printf("*p = %d\n",*p);
    c = 300;
    printf("c = %d\n",c);//直接修改值
    printf("*p = %d\n",*p);
    *p = 400;//修改指针指向地址里的内容
    printf("*p = %d\n",*p);
    int d =500;
    //p = &d;//不能修改指针的指向,编译报错
    //printf("*p = %d\n",*p);
}
int main()
{
  test();
  return 0 ;
}

区分常量指针和指针常量的关键就在于*的位置,以*为分界线,如果const在*的左边,则为常量指针,如果const在*的右边则为指针常量。

如果我们将*读作‘指针’,将const读作‘常量’的话,内容正好符合:

int const * n;
     常量+指针-->常量指针
int *const n;
   指针+常量  -->常量指针

3.5 指向常量的常指针

是以上两种的结合,指针指向的位置不能改变并且也不能通过这个指针改变变量的值,但是依然可以通过其他的普通指针改变变量的值。

const int* const p;
#include<stdio.h>
void test()
{
    int c = 200;
    const int * const p = &c;//即修饰指针变量类型又修饰指针变量
    printf("*p = %d\n",*p);
    c = 300;//可以直接修改值
    printf("c = %d\n",c);
    printf("*p = %d\n",*p);
    //*p = 400;//不能修改指针指向地址里的内容,编译报错
    //printf("*p = %d\n",*p);
    //int d =500;
    //p = &d;//不能修改指针指向,编译报错
    //printf("*p = %d\n",*p);
}
int main()
{
  test();
  return 0;
}

四、修饰函数的参数

根据常量指针与指针常量,const修饰函数的参数也是分为三种情况

  1. 防止修改指针指向的内容
void StringCopy(char *strDestination, const char *strSource);
  1. 其中 strSource 是输入参数,strDestination 是输出参数。给 strSource 加上 const 修饰后,如果函数体内的语句试图通过解引用的方式改动 strSource 的内容,编译器将指出错误。
  2. 防止修改指针指向的地址
void swap ( int * const p1 , int * const p2 )
  1. 指针p1和指针p2指向的地址都不能修改。

五、修饰函数的返回值

如果给以“指针传递”方式的函数返回值加 const 修饰,那么函数返回值(即指针)的内容不能被修改,该返回值只能被赋给加const 修饰的同类型指针。

例如函数

const char * GetString(void)

如下语句将出现编译错误:

char *str = GetString();

正确的用法是

const char *str = GetString();


相关文章
|
5月前
|
C语言
指针进阶(C语言终)
指针进阶(C语言终)
|
5月前
|
数据库 C语言
C语言进阶 文件操作知识(上)
C语言进阶 文件操作知识(上)
39 3
|
5月前
|
存储 C语言
C语言进阶 文件操作知识(下)
C语言进阶 文件操作知识(下)
37 2
|
5月前
|
C语言
C语言学习记录——模拟字符串相关函数(strcpy、strlen、strcat)相关知识-const、typedef
C语言学习记录——模拟字符串相关函数(strcpy、strlen、strcat)相关知识-const、typedef
31 1
|
5月前
|
存储 编译器 数据库
【再识C进阶5(上)】详细介绍C语言文件操作——文件是用于存储数据
【再识C进阶5(上)】详细介绍C语言文件操作——文件是用于存储数据
|
5月前
|
Java 程序员 Linux
探索C语言宝库:从基础到进阶的干货知识(类型变量+条件循环+函数模块+指针+内存+文件)
探索C语言宝库:从基础到进阶的干货知识(类型变量+条件循环+函数模块+指针+内存+文件)
49 0
|
6月前
|
编译器 C语言 C++
从C语言到C++_21(模板进阶+array)+相关笔试题(下)
从C语言到C++_21(模板进阶+array)+相关笔试题
47 2
|
6月前
|
C语言
C语言进阶:进阶指针(下)
C语言进阶:进阶指针(下)
48 2
|
6月前
|
C语言
万字详解:C语言三子棋进阶 + N子棋递归动态判断输赢(二)
我们可以通过创建并定义符号常量NUMBER,来作为判断是否胜利的标准。如三子棋中,令NUMBER为3,则这八个方向中有任意一个方向达成3子连珠,则连珠的这个棋子所代表的玩家获胜。
68 1
|
6月前
|
算法 C语言 C++
万字详解:C语言三子棋进阶 + N子棋递归动态判断输赢(一)
三子棋游戏设计的核心是对二维数组的把握和运用。
78 1