跳至主要內容

const

张威大约 11 分钟c/c++c++基础

const

1681302938745-c100c3cc-4cb4-4f9d-a1cb-94d6bd99e2ad
1681302938745-c100c3cc-4cb4-4f9d-a1cb-94d6bd99e2ad

const关键字修饰变量

const int number1 = 10;//const关键字修饰的变量称为常量
int const number2 = 20;

const int val;//error  常量必须要进行初始化
number1 = 30;//error   常量不能被赋值(初始化后不可修改)

除了这种方式可以创建常量外,还可以使用宏定义的方式创建常量

#define NUMBER 1024

常考题:const常量与宏定义的区别是什么?

  1. 编译器处理方式不同。宏定义是在展开,做字符串的替换;而const常量是在
  2. 类型和安全检查不同。宏定义没有类型,不做任何类型检查(有bug运行时才会报错);const常量有具体的类型,在编译期会执行类型检查(有bug编译时报错)。

在使用中,应尽量以const替换宏定义,可以减小犯错误的概率。

const和普通变量的区别

  1. const修饰的变量不能再作为左值初始化后值不能被修改
  2. 编译方式不一样

c和c++中const修饰变量有什么不同

  1. C中const修饰的量可以不初始化,但后面也不能赋值了,不叫常量,而是叫,因此不能用作数组下标
int main(void)
{
   const int a = 20;
   int array[a] = {}; // error
   return 0;
}
int main(void)
{
    const int a = 20;
	int *p = (int*)&a;
	*p = 30;

	// 30 30 30
	printf("%d %d %d \n", a, *p, *(&a));
    return 0;
}

c++中const特性

const修饰的变量必须初始化,可以用作数组下标

int main(void)
{
    const int a = 20;
    int array[a] = {}; // pass
	int *p = (int*)&a;
	*p = 30;	//*p此时确实把&a的值改成了30,但是a在编译时都直接替换成20

	// 20 30 20
	printf("%d %d %d \n", a, *p, *(&a));
    //printf("%d %d %d \n", 20, *p, 20);
    return 0;
}
  • C++是将a在**直接替换**成20,这就是为什么必须初始化,不初始化怎么发生替换

c++中的const初始化值为**,如果用给const初始化的叫**,因为只有运行时才知道变量的值是多少

int main(void)
{	
	int b = 20;
    const int a = b;	
    //int array[a] = {}; //常变量不能定义数组
    
    int *p = (int*)&a;
	*p = 30;
    // 30 30 30
	printf("%d %d %d \n", a, *p, *(&a));	//和c语言一样
    return 0;
}

答:

  1. const的编译方式不同,c中,const就是当作一个变量编译生成指令的;在C++中,const常量则会被视为一个编译时的常数,并在
  2. c++中const修饰的变量必须初始化;c语言的const可以不初始化叫常变量
  3. c++中const初始化值为立即数常量,可以当数组下标;如果用变量给const初始化的叫常变量,常变量不能当数组下标;
  4. (运行时赋值)

const修饰的量常见错误

  • 常量不能再作为左值(直接修改常量值)
  • 不能把常量地址泄漏给一个普通指针或者普通引用变量(间接修改常量值)
int main()
{
    const int a = 10;
    int *p = &a; // error: invalid conversion from ‘const int*’ to ‘int*’ 
    //杜绝*p = 30出现
    const int *p = &a; // ok, 保证*p = x;被禁止
}

const关键字修饰指针

对于指针需要关注两点:

  1. 指针指向的变量是什么
  2. 指针本身是什么
int value = 2;
int value1 = 10;
int *p1 = &value1;
p1 = &value;
*p1 = 20000;

常量指针和指针常量

常量指针 (const *):不能修改指针所指内容的值,可以改变指针的指向

指针常量 ( * const):可以修改指针所指内容的值, 不可以改变指针的指向

int number1 = 10;
int number2 = 20;
const int * p1 = &number1;//常量指针,Pointer to const
*p1 = 100;//error   通过p1指针无法修改其所指内容的值
p1 = &numbers;//ok  可以改变p1指针的指向

int const * p2 = &number1; //常量指针的第二种写法

int * const p3 = &number1;//指针常量,const pointer
*p3 = 100;//ok          通过p3指针可以修改其所指内容的值 
p3 = &number2;//error   不可以改变p1指针的指向

const int * const p4 = &number1;//两者皆不能进行修改

顶层const和底层const

顶层const:指的是const修饰的变量本身是⼀个常量,⽆法修改,指的是,就是 底层const:指的是const修饰的变量所指向的对象是⼀个常量,指的是,就是

int a = 10;int* const b1 = &a;        //顶层const,b1本身是⼀个常量
const int* b2 = &a;       //底层const,b2本身可变,所指的对象是常量
const int b3 = 20;       //顶层const,b3是常量不可变
const int* const b4 = &a;  //前⼀个const为底层,后⼀个为顶层,b4不可变
const int& b5 = a;       //⽤于声明引⽤变量,都是底层const

注意:

  1. const int b3 = 20; b3是所修饰的变量,所以是****
  2. ⽤于声明引⽤变量,都是底层const

区分作⽤:

  • 执⾏对象拷⻉时有限制,常量的底层const不能赋值给⾮常量的底层const
  • 使⽤命名的强制类型转换函数const_cast时只能改变运算对象的底层const(即

const与二级(多级)指针结合

实际上没有多级指针,只有一级指针,const int **q => const int * *qp*q都指向0x100,==const int **q表示*q0x100)存放的数据类型应该是常量指针类型const int *==🍗🍗🍗

const int **q; // const修饰**q, **q不能被赋值,*q和q可以被赋值
int *const *q; // const修饰*q, *q不能被赋值,**q和 q可被赋值
int **const q; // const修饰q, 只有q不能被赋值
//错误的例子
int main()
{
    int a = 10;
    int *p = &a;
    const int **q = &p; // error, const int ** <= int**
    // 假如有const int b; *q = &b;    由于*q和p相同,相当于通过p可以间接修改b的值
}

在C++中,int* 可以隐式转换为 const int*,因为这样做增加了对数据的保护,不会改变原有数据的非const性质。但是,int**(指向int指针的指针)和const int**(指向const int指针的指针)之间并不是这种简单的类型兼容关系。

当您尝试将int** p赋值给const int** q时,您实际上是在尝试将一个指向非const指针的指针赋值给一个指向const指针的指针。这两种类型并不兼容,因为q期望的是一个指向const int指针的指针,而p是一个指向可能修改其指向的int值的指针的指针。

换句话说,即使*p(即p所指向的内容)可以被转换为const int*p本身(即指向int*的指针)也不能被转换为const int**

可修改为如下方可通过编译🍗

int main() // 法一
{
    int a = 10;
    const int *p = &a;	//p存放的是const int *类型的数据
    const int **q = &p; // ok,*q是的类型是const int *
}

int main()  // 法二
{
    int a = 10;
    int *p = &a;
    const int *const*q = &p; // ok,此时*q是常量,*q类型是const int *没有问题
}

const和指针的类型转换()🍗🍗🍗

int* <= const int* // error,因为解引用会修改const的值
const int* <= int*	//OK
    
int ** <= const int **  //error
const int** <= int**  // error
    
int** <= int *const*  //error,const与一级指针结合,可转换成 * <= const *判断,即第一种
int *const* <= int**  //ok,同第二种情况 

习题

#include <typeinfo>
int *q1 = nullptr;
int *const q2 = nullptr;//const修饰的是q2,因此q2不能
cout << typeid(q1).name() << endl;	//int*	//Pi
cout << typeid(q2).name() << endl;	//int*	//Pi

int a = 10;
int *p1 = &a;
const int *p2 = &a; //const int * <= int *,OK
int *const p3 = &a;	//int * <= int *,OK
int *p4 = p3;	//int * <= int *,OK	
int a = 10;
const int *p = &a;
int *q = p;	//int * <= const int * ,error

注意辨析:p存放的是a的地址,p又赋值给q,a是变量值可以修改,那q可不可以修改❌

q 和有没有a没有关系,不管是变量地址给p还是常量地址给p,对于编译器来说p存放的就是整型常量的地址const int *

// 例一:
int a = 10;
const int *p = &a;  // int* <= int*
int *q = p; // int*  <=  const int*,error

//例二:
int a = 10;
int *const p = &a;  // int* <= int*
int *q = p; // int*  <=  int* ,ok

//例三:
int a = 10;
int *const p = &a; // int* <= int*
const int *q = p; // const int* <= int*,OK

//例四:
int a = 10;
int *p = &a;
const int **q = &p; // const int ** <= int **,error, *q<=>p

//例五:
int a = 10;
int *p = &a;
int **const q = &p; // int** <= int**,OK

//例六:🍔🍔🍔
int a = 10;
const int *p = &a;
int *const* q = &p;
//int *const* <= const int**,两个const分开看
//等号左边const修饰*q,const* <= *, OK
//因为qcun'cint *类型,int* <= const int * ,error

函数指针:指向函数的指针

指针函数:返回类型是指针的函数

数值指针:指向数组的指针

指针数组:存储类型为指针的数组

//函数指针           指针函数
//int (*pf)(int)     int*   pf(int)
//
//数组指针           指针数组
//int (*pArray)[]    int* pArray[]
#include <pthread.h>

int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                   void *(*start_routine) (void *), void *arg);
//void *(*start_routine) (void *) 就是函数指针

在 C 语言中,可以通过

在传统的 C 语言中,没有直接的语言支持来实现面向对象编程中的多态性(polymorphism)。多态性是面向对象编程的一个重要特性,可以通过继承和虚函数来实现。然而,我们可以使用一些技巧来模拟多态性的概念。具体而言,可以通过定义一个包含函数指针的结构体,并使用不同的函数实现来实现多态性的效果。

#include <stdio.h>

typedef struct {
    void (*speak)(void);
} Animal;

typedef struct {
    Animal base;
    const char* name;
} Dog;

typedef struct {
    Animal base;
    const char* name;
} Cat;

void dogSpeak(void) {
    printf("The dog barks.\n");
}

void catSpeak(void) {
    printf("The cat meows.\n");
}

int main() {
    Dog dog;
    dog.base.speak = dogSpeak;
    dog.name = "Tom";

    Cat cat;
    cat.base.speak = catSpeak;
    cat.name = "Jerry";

    Animal* animals[] = { (Animal*)&dog, (Animal*)&cat };
    int i;
    for (i = 0; i < 2; i++) {
        printf("%s: ", i == 0 ? dog.name : cat.name);
        animals[i]->speak();
    }

    return 0;
}

const关键字修饰成员函数

const成员函数open in new window

const关键字修饰对象

[对象的组织 | 张威的编程学习笔记 (gitee.io)](https://iszhwei.gitee.io/ccpp/03 类和对象/组织对象.html)

课堂代码

#include <iostream>

using std::cout;
using std::endl;

//宏定义发生的时机是在预处理阶段,字符串替换,有bug会到运行时才会发现
#define MAX 10
#define multiply(x, y) ((x) * (y))


void test()
{
    //发生时机在编译阶段,会进行类型安全检查,如果有bug在编译时候就会出现
    //内置类型:char/short/int/long/double/float/void *
    const int number = 10;//const修饰的变量称为常量,必须在定义的时候进行初始化
    /* number = 20;//赋值,常量不能进行赋值 */

    int const number2 = 20;
}

//函数指针           指针函数
//int (*pf)(int)     int*   pf(int)
//
//数组指针           指针数组
//int (*pArray)[]    int* pArray[]

void test2()
{
    int value = 2;
    int value1 = 10;
    int *p1 = &value1;
    p1 = &value;
    *p1 = 20000;

    cout << endl;
    int value2 = 200;
    const int *p2 = &value2;//当const位于*左边的时候,常量指针(pointer to const)
    /* *p2 = 222;//error,不能修改指针所指变量的值 */
    p2 = &value;//ok,可以改变指针本身(指向)

    cout << endl;
    int value3 = 300;
    int const *p3 = &value3;//当const位于*左边的时候,常量指针(pointer to const)
    /* *p3 = 333;//error,不能修改指针所指变量的值 */
    p3 = &value;//ok,可以改变指针本身(指向)

    cout << endl;
    int value4 = 400;
    int * const p4 = &value4;//当const位于*右边的时候,指针常量(const pointer)
    *p4 = 444;//ok,可以修改指针所指变量的值
    /* p4 = &value;//error,不可以改变指针本身(指向) */

    cout << endl;
    int value5 = 500;
    const int * const p5 = &value5;//双const
    /* *p5 = 555;//error,不可以修改指针所指变量的值 */
    /* p5 = &value;//error,不可以改变指针本身(指向) */
}
int main(int argc, char **argv)
{
    test2();
    return 0;
}