引言
说起常量,最直观的常量就是用#define、const和enum方式。
Qustion
那么任何情况下这三种常量的表示都可以使用吗?
如果不能同时使用,那么分别的适用场合又是哪一些呢?
我们带着上述的疑问开始对这三个系统的分析,那么开始吧。
#define
定义
C/C++
语言源程序中允许用一个标识符来表示一个字符串,称为“宏”。被定义为“宏”的标识符称为“宏名”。在编译预处理时,对程序中所有出现的“宏名”,都用宏定义中的字符串去代换,这称为“宏代换”或“宏展开”。宏定义是由源程序中的宏定义命令完成的。宏代换是由预处理程序自动完成的。
说的感觉有点云里雾里,通俗的来说明就是我用一个字符或者字符串代表替换文本。这个替换文本包括整形,字符串,表达式等。具体的来说宏定义还分为有参和无参的两种形式,这边对有参不展开说明。喜欢专研的小伙伴具体可以看宏定义有参。
举例说明
这里其名称(宏名)一般大写,而且不能有空格。
#define YEAR 2020
#define YY "2020"
具体实现可以看下面的代码
//demo1
#include "stdafx.h"
#define YEAR 2020 //宏定义数字
#define YY "2020" //宏定义字符
#include <iostream>
#include <cstring>
using namespace std;
int main()
{
cout << YEAR << endl; //处理前数字常量输出
cout << YY << endl; //处理前字符串常量输出
//进行处理
int num = YEAR + 1;
char s[20];
strcpy_s(s,5, YY);
cout << num << endl; //这个数字常量可以直接赋值使用
cout << s << endl; //这个字符串常量可以通过拷贝函数使用
return 0;
}
这边还有一种表达常量的方法,就是表达式,形式如下所示
//demo2
#include "stdafx.h"
#define YEAR 15+5 //很显然这个值也是20
#include <iostream>
#include <cstring>
using namespace std;
int main()
{
cout << YEAR << endl; //这个值是20
//进行处理
int num = YEAR * YEAR; //这个值会等于400?
cout << num << endl;
return 0;
}
结果如下显示:
这是由于这个宏定义的机制所造成的的,它是一种替换,而不是直接的赋值。
YEAR * YRAE = 15+5*15+5 = 95而不是YEAR * YEAR = (15+5) * (15+5) = 400
这边会有一个疑问?
那么用#define能不能变成类型名呢?
其实是可以的
//demo3
#include "stdafx.h"
#define INTEGER int
typedef int INT;
#define INTEGERP int*
typedef int* INTP;
#include <iostream>
#include <assert.h>
using namespace std;
int aa = sizeof(INTEGER);
int bb = sizeof(INT);
int main()
{
cout << aa << " " << bb << endl;
INTEGER a = 1;
INT b = 2;
assert(a-b <= 0);
cout << a << " " << b << endl;
//进行处理
INTEGERP p1 = &a;
INTP p2 = &b;
cout << *p1 << " " << *p2 << endl;
return 0;
}
上述的两种方式都可以变成int类型的两类和指向int类型的指针,但是两者有区别。#define是在预处理阶段就已经替换,而typedef更像是int类型的一个别名,发生在编译运行阶段。
总结:#define有三种方式表达常量,有常量字符、常量数字和常量的表达式(不推荐使用表达式)。
const
定义
常类型是指使用类型修饰符const说明的类型,常类型的变量或对象的值是不能被更新的
核心关键词是用于常类型的变量或对象,举例如下。
const int Year = 2020;
int Year_1 = 2020;
const int* p1 = Year_1;
int const *p2 = Year_1;
int* const p3 = Year_1;
const int* const p4 = Year_1;
const修饰部分原则
1.看左侧的最近部分
2.如果左侧没有,则看右侧
//demo4
#include "stdafx.h"
#include <iostream>
using namespace std;
int main()
{
int Year = 2020;
const int* p1 = &Year;
int const *p2 = &Year; //p1和p2是同种类型的,指向内容不能变
int* const p3 = &Year; //p3指针所指向的不能变
const int* const p4 = &Year; //p4指针所指向的和指向内容都不能变
int YY = 2021;
p1 = &YY;
p2 = &YY;
//p3 = &YY;
//p4 = &YY;
cout << *p1 << endl;
cout << *p2 << endl;
cout << *p3 << endl;
cout << *p4 << endl;
//*p2 = *p2 + 1;
*p3 = *p3 + 1;
//cout << *p2 << endl;
cout << *p3 << endl;
return 0;
}
const不仅仅是int类型可以表示的,字符串类型同样可以。
关于const与字符串指针的关系,代码如下:
//demo5
#include "stdafx.h"
#include <iostream>
using namespace std;
int main()
{
char str[] = "helloworld";
char const *p1 = "helloworld1"; //const char*
//char *const p2 = "helloworld2"; //p2不能改变指向,如果变了内容那么原指向找不到了
char *const p2 = str;
char const *const p3 = "helloworld3"; //const char* const
p1 = str;
//p2 = str; //p2不可改
//p3 = str; //p3不可改
cout << p1 << endl;
cout << *p1 << endl;
cout << *p2 << endl;
cout << p2 << endl;
cout << p3 << endl;
for (int i = 0; i < 10; i++)
{
str[i] += 1;
//p1[i] += 1;
p2[i] += 1;
//p3[i] += 1;
}
cout << str << endl;
cout << p2 << endl;
return 0;
}
那么上述知道了#define和const之后,发现似乎有点类似又有所区别,这边将weixin_41521306的整理出来的异同点展开描述。
异同点 | #define | const |
---|---|---|
作用阶段 | 预处理阶段 | 编译、运行 |
作用的方式 | 简单的字符替换,没有类型检查 | 对应数据类型,进行类型检查 |
存储方式 | 内存中有若干个备份,占用代码段空间 | 只有一份备份,占用数据段空间 |
代码调试 | 不能调试,在预编译阶段已替换掉 | 可以进行调试的 |
再定义 | #undef取消某个符号的定义,再重新定义 | 不能重定义 |
特殊功能 | 防止头文件重复引用 | 不能防止头文件重复引用 |
用于类 | 可用于全局变量 | 用于类成员变量的定义,且定义后不可改 |
因此,总结了上述的异同点可知,我们在c++中尽量使用const定义变量,方便于开发人员排查错误。
enum
定义
枚举类型(enumeration)是 C++ 中的一种派生数据类型,它是由用户定义的若干枚举常量的集合
enum不仅能够创建符号常量,还能创建新的数据类型。
enum week {Sun, Mon, Tue, Wed, Thu, Fri, Sat}; // 定义枚举类型week,默认从0开始依次加1
对应关系如下表所述:
枚举型变量 | 对应整数值 |
---|---|
sun | 0 |
Mon | 1 |
Tue | 2 |
Wed | 3 |
Thu | 4 |
Fri | 5 |
Sat | 6 |
enum week1 {Sun=7, Mon=1, Tue, Wed, Thu, Fri, Sat}; // 定义枚举类型week,默认从0开始依次加1
对应关系如下表所述:
枚举型变量 | 对应整数值 |
---|---|
sun | 7 |
Mon | 1 |
Tue | 2 |
Wed | 3 |
Thu | 4 |
Fri | 5 |
Sat | 6 |
枚举常量只能以标识符形式表示,而不能是整型、字符型等文字常量。
//错误示例1
enum letter_set {'a','b','c','d','e'}; //枚举常量不能是字符常量
错误原因:字符本身就有对应的ASCII码值,所以再将字符对应整数就会出现问题,不成立
//错误示例2
enum year_set{2020,2021,2022,2023,2024,2025}; //枚举常量不能是整型常量
错误原因:这个本身就是数字,已经有相应的二进制数表示了,所以也不能对应其他整数,不成立
综上,举例说明枚举类型的常量
//枚举值不可以做左值,枚举变量可以赋值给非枚举变量
#include "stdafx.h"
#include <iostream>
using namespace std;
enum week
{
Sun = 7,
Mon = 1,
Tue,
Wed,
Thu,
Fri,
Sat
};
int main()
{
week wk;
int a;
wk = Mon; //Mon=1
wk = Tue; //Tue=2
//wk = 1; //不能直接将整形类型赋值
wk = (week)(1); //强制转化类型
cout << wk << endl;
a = Mon;
cout << a << endl; //可以直接赋值给整形类型
return 0;
}
总结
#define注重预处理的替换,const倾向于常类型的变量或对象,enum更偏向于一种自定义的数据类型,建议多使用const作为常量表达方式,原因是适用范围广且容易排查错误。
参考
1.菜鸟加贝的爬升,https://www.cnblogs.com/jiabei521/p/3335676.html
2.Shmilxu的博客,https://www.cnblogs.com/shmilxu/p/4837373.html
3.Oragen,https://blog.csdn.net/magic_world_wow/article/details/80733495
4.葫芦娃的后桌,https://www.cnblogs.com/happying30/p/9350712.html
5.京鸿智武,https://zhuanlan.zhihu.com/p/46602697
6.weixin_41521306,https://blog.csdn.net/weixin_41521306/article/details/88574016
7.学习笔记666,https://blog.csdn.net/github_26672553/article/details/82957003
8.猿问,http://www.imooc.com/wenda/detail/522431