前言
常量是固定值,在程序执行期间不会改变;变量的值是可以改变的,根据指针我们可以知道,变量并不是直接指向数据,而是指向对应数据的内存地址,如果更改变量的值,实际上只是改变了变量所指向的地址,而地址里的值是没有改变的。
- #define NAME "bogendihong"
- #define PI 3.14
#define 宏定义:C语言允许用一个标识符来表示一串数据,称为“宏”。被定义为“宏”的标识符称为“宏名”。在编译预处理时,对程序中所有出现的“宏名”(上述代码中的 NAME
、PI
),都用宏定义中的数据去替换,这称为“宏代换”或“宏展开”。宏定义是由源程序中的宏定义命令去完成的。宏代换是由预处理程序自动完成的。
#include <stdio.h>
#define PI 3.14
int main() {
printf("%f\n", PI); // printf("%f\n", 3.14);
PI = 3.1415; // 3.14 = 3.1415;
printf("%f\n", PI);
return 0;
}
常量是不可以被改变的,用宏定义所表示代码就和我所注释的一样。想要将 3.1415 赋值给 3.14 是不可能的,因为这个数所代表的值是不会改变的。
于是就报了上图所示的错误: Error: lvalue required as left operand of assignment(表达式左边需要lvalue)
const
当然除了宏定义,C语言还提供了另一种方式让我们去定义常量,那就是const关键字,那么这个时候就有小伙伴会问了 const 和 define 有什么区别吗?
定义
const 所定义的常量是变量,并且具有类型
define 定义的只是个常量,不带类型
阶段
const 工作于编译、运行阶段
define 是在编译的预处理阶段起作用
内存占用
define 定义的宏在编译后就不存在了,它不占用内存,因为他不是变量,系统指挥给变量分配内存
const 定义的常量本质上是一个变量,拥有变量所有的一系列基本属性
int a = 1314;
const int b = 520;
a = 1315;
b = 521;
return 0;
Error: assignment of read-only variable 'b' (错误,给只读变量赋值)
常量与指针
在上面说过,由 const 关键字所定义的常量本质上就是变量,所以它也拥有变量的特性,那么我们就可以构建它的指针
1️⃣ 常量指针
也叫常指针,可以理解为常量的指针,也即这个是指针,但指向的是个常量,这个常量是指针的值(地址),而不是地址指向的值。
指针可以指向别处,因为指针本身是个变量,可以指向任意地址
常量指针可以被赋值为变量的地址,因为被限制了通过这个指针去间接修改变量的值,所以被称为常量指针
const b = 1314;
const int *pb = &b;
指向常量的指针不能简介修改所对应的值,只能更改自身说指向的地址
2️⃣常量指针
本质上是一个常量,而用指针修饰它。指针常量的值是指针,这个值因为是常量,所以不能被赋值。
指针所保存的地址可以改变,但是指针所指向的值不能改变
指针本身是常量,指向的地址不能改变,但是地址对应的值却可以变化
int a = 520;
const int b = 1314;
int * const pa = &a;
printf("*pa: %d, a: %d\n", *pa, a);
a = 521;
printf("*pa: %d, a: %d\n", *pa, a);
return 0;
输出为:
*pa: 520, a: 520
*pa: 521, a: 521
3️⃣指向常量的常指针
const int * const p
指向常量的指针常量就是一个常量,并且它指向的对象也是一个常量
它指向的指针对象只是一个常量,所以它指向的对象不能变化
int a = 520;
const int * const p = &a;
printf("p: %p, a: %p\n", p, &a); // p: 0x7ffff3d8041c, a: 0x7ffff3d8041c
printf("*p: %d, a: %d\n", *p, a); // *p: 520, a: 520
a = 1314;
printf("*p: %d, a: %d\n", *p, a); // *p: 1314, a: 1314
综合了指针常量和常量指针的特点(~ ̄▽ ̄)~