c++中的enum变量 和 constexpr说明符


author: hjjdebug

date: 2025年 04月 23日 星期三 13:40:21 CST

description: c++中的enum变量 和 constexpr说明符


文章目录

    • [1.Q:enum 类型变量可以有++,--操作吗?](#1.Q:enum 类型变量可以有++,--操作吗?)
      • [1.1补充: c/c++中enum的另一个细微差别.](#1.1补充: c/c++中enum的另一个细微差别.)
    • [2.Q: constexpr 修饰的函数,要求传入的参数必需是常量吗?](#2.Q: constexpr 修饰的函数,要求传入的参数必需是常量吗?)
    • [3. Q constexpr 编译期求值真正的意思是什么?](#3. Q constexpr 编译期求值真正的意思是什么?)
      • [3.1 debug 版本, constexpr 修饰无效](#3.1 debug 版本, constexpr 修饰无效)
      • [3.2 release 版本, constexpr 函数被优化掉了](#3.2 release 版本, constexpr 函数被优化掉了)
    • [4. constexpr 中碰到了一个左值引用绑定问题](#4. constexpr 中碰到了一个左值引用绑定问题)
    • [5 x86-64 linux系统上函数调用传参约定](#5 x86-64 linux系统上函数调用传参约定)

本来应该分2篇博客写的,放一起吧,也有相关性,都是c++的.

1.Q:enum 类型变量可以有++,--操作吗?

A: c支持,c++不支持.

c++中的enum 类型变量和c中的enum变量基本含义相同,但又略有区别.

试验代码:

复制代码
$ cat main.c
#include <stdio.h>
typedef enum _ABC { E0,E1,E2}ABC;
int main() 
{
	ABC a=E0;
	a++;
	return 0;
}

c语言是支持enum类型变量++或--这种操作的,因为它天然认为enum类型就是整形

将main.c改名为main.cpp文件,则编译不通过

给出的编译错误为:

error: no 'operator++(int)' declared for postfix '++' [-fpermissive]

没有后++的操作运算符说明, operator++(int), 不允许的操作.

就是说c++编译器认为enum 类型是一种新的类型,并不是整型,虽然它的内部实现是把它当整形来实现的.

那如何解除这种限制呢?

如下:多加一行代码, 重载 ABC operator++(int)类型的函数. 就可以对ABC 型变量执行后++操作

复制代码
$cat main.cpp
#include <stdio.h>
typedef enum _ABC { E0,E1,E2}ABC;
constexpr ABC operator++(ABC d,int) {return ABC((int)d+1);}
int main() 
{
	ABC a=E0;
	a++;
	return 0;
}

1.1补充: c/c++中enum的另一个细微差别.

c++中可以不用typedef 重定义类型,而直接使用enum 就可以了.如下:

enum ABC {E0,E1,E2}, 其它代码不动.

而c语言不行,

如果去掉typedef 重定义,你需要在声明变量时加上enum声明,如下:

enum ABC a=E0

否则会有编译错误,如下:

error: unknown type name 'ABC'; use 'enum' keyword to refer to the type

| ABC a=E0;

| ^~~

2.Q: constexpr 修饰的函数,要求传入的参数必需是常量吗?

答: 不是

这个constexpr修饰的函数,网上说是可以编译期求值,要求传入常量值.

我看说法不完全正确,这里我传入a就是内存变量,不是常量

我还可以写如下代码:

for(int i=0;i<2;i++)

{

a++; // 这个a 是不断变化的.

printf("a is %d\n", (int)a);

}

3. Q constexpr 编译期求值真正的意思是什么?

A: constexpr函数并不会进入执行文件中,执行文件中没有这个函数.

正确的理解constexpr的编译期求值, 是它在运行时不求值,

就是说它已经做到了运行时, 不进入你定义的constexpr函数,

例如对该例,它的a++就直接变成了整数的a++

并不是说一定要传入常量值.

3.1 debug 版本, constexpr 修饰无效

这只是我的一种猜测,后面我会证明,

如果编译成debug版调试程序,它总是会跟入函数的, 这不是constexpr 的初衷

对debug版本,加不加constexpr 效果是一样的.

3.2 release 版本, constexpr 函数被优化掉了

那么对release 版是否真的把constexpr函数给优化掉了呢?那要反汇编代码了.

开干吧! 源代码如下:

复制代码
#include <stdio.h>
typedef enum _ABC { E0,E1,E2}ABC;
constexpr ABC& operator++(ABC& d,int) {return d=ABC((int)d+1);}
int main()
{
	ABC a=E0;
	for(int i=0;i<5;i++)
	{
		a++;
	}
	return 0;
}

release 版反编译代码

我惊讶的发现, 我的main函数被它优化成空函数了

如下:

0000000000001040 :

1040: f3 0f 1e fa endbr64

1044: 31 c0 xor %eax,%eax

1046: c3 retq

1047: 66 0f 1f 84 00 00 00 nopw 0x0(%rax,%rax,1)

什么for循环, enum 变量,统统都是空.

哇! 这么利害,把代码全优化掉了,它认为我这些代码都是没用的东西.

是的,如果把main也当成一个一般的子函数看,它除了占用点cpu资源,对外界就没有什么影响.

我们加上printf 代码,让代码变得有用起来. 不能让它全优化了.

复制代码
#include <stdio.h>
typedef enum _ABC { E0,E1,E2}ABC;
constexpr ABC& operator++(ABC& d,int) {return d=ABC((int)d+1);}
int main()
{
	ABC a=E0;
	for(int i=0;i<5;i++)
	{
		a++;
		printf("a is %d\n",(int)a);
	}
	return 0;
}

release 版反编译代码

复制代码
0000000000001060 <main>:
    1060:	f3 0f 1e fa          	endbr64 
    1064:	55                   	push   %rbp  //rbp 如栈,框架
    1065:	48 8d 2d 98 0f 00 00 	lea    0xf98(%rip),%rbp # 2004 <_IO_stdin_used+0x4>
    106c:	53                   	push   %rbx  //rbx 入栈,它会做为a变量
    106d:	bb 01 00 00 00       	mov    $0x1,%ebx //ebx 就是a变量,初始值给了1,因为它知道第一次打印值是1,利害!
    1072:	48 83 ec 08          	sub    $0x8,%rsp //调整堆栈,为局部变量准备空间
    1076:	89 da                	mov    %ebx,%edx // a变量送给edx, printf 的第二个参数
    1078:	48 89 ee             	mov    %rbp,%rsi // rsi, printf 的第一个参数,字符串地址
    107b:	bf 01 00 00 00       	mov    $0x1,%edi // rdi=1,向stdout输出,因为它调用的是__printf_chk
    1080:	31 c0                	xor    %eax,%eax // 表示传递的浮点数个数是0个
    1082:	e8 c9 ff ff ff       	callq  1050 <__printf_chk@plt>
    1087:	83 c3 01             	add    $0x1,%ebx        // ebx 为a变量
      //它算出了a变量和i变量的对应关系,所以优化掉i变量,让a变量与6比,果然很智能!
    108a:	83 fb 06             	cmp    $0x6,%ebx      
    108d:	75 e7                	jne    1076 <main+0x16> //循环到1076
    108f:	48 83 c4 08          	add    $0x8,%rsp   //局部空间栈恢复
    1093:	31 c0                	xor    %eax,%eax  //返回值
    1095:	5b                   	pop    %rbx   //恢复rbx
    1096:	5d                   	pop    %rbp //恢复rbp
    1097:	c3                   	retq   //函数返回 
    1098:	0f 1f 84 00 00 00 00 	nopl   0x0(%rax,%rax,1)
    109f:	00 

完美验证了我的设想!constexpr 函数优化后不会出现在运行期,但它也不会要求参数必需是常量,变量也行,只要能被它替代就行.

总结一下: constexpr 函数是一种可以被优化掉的函数,就是说它不会生成对应的函数代码,而是会把返回值嵌入到你调用的代码中. 返回值可能是一个常数或常量,也可以是一个变量的简单运算.就是一个变量加个偏移,乘一个数除一个数等.一般constexpr函数不会太复杂.这里先给你一个美好的心里预期!

4. constexpr 中碰到了一个左值引用绑定问题

代码:

constexpr ABC& operator++(ABC& d,int) {return ABC((int)d+1);}

error: cannot bind non-const lvalue reference of type 'ABC&' {aka '_ABC&'} to an rvalue of type 'ABC' {aka '_ABC'}

错误是说: 不能够让非-const 的左值ABC& 类型与右值 ABC类型相绑定.

就是说要返回引用,而不要返回值

修改方法.

返回一个值的引用. 一个变量的别名叫引用,引用在函数间传递的是地址,该函数实际的意义就是修改自身.

所以函数内把值再变成引用.如下:添加d=内容,把值送给引用.
constexpr ABC& operator++(ABC& d,int) {return d=ABC((int)d+1);}

5 x86-64 linux系统上函数调用传参约定

采用System V AMD64 调用约定.

前六个整型参数(包括指针)通过寄存器传递

顺序RDI、RSI、RDX、RCX R8 R9 ,超过6个多余的用堆栈传,从右向左顺序压入堆栈.

例: test(0,1,2,3,4,5,6) 7个参数

31 ff xor %edi,%edi

be 01 00 00 00 mov $0x1,%esi

ba 02 00 00 00 mov $0x2,%edx

b9 03 00 00 00 mov $0x3,%ecx

41 b8 04 00 00 00 mov $0x4,%r8d

41 b9 05 00 00 00 mov $0x5,%r9d

6a 06 pushq $0x6

//test 函数c++中变成了_Z4Testiiiiiii,名称带参数类型,_Z是gcc的函数标志,4是名称长度,7个i是7个整形参数

e8 04 01 00 00 callq 1190 <_Z4Testiiiiiii>

相关推荐
小邓儿◑.◑4 小时前
C++武功秘籍 | 入门知识点
开发语言·c++
杨筱毅7 小时前
【优秀三方库研读】【C++基础知识】odygrd/quill -- 折叠表达式
c++·三方库研读
CoderCodingNo8 小时前
【GESP】C++二级真题 luogu-B4259 [GESP202503 二级] 等差矩阵
java·c++·矩阵
明月看潮生8 小时前
青少年编程与数学 02-018 C++数据结构与算法 11课题、分治
c++·算法·青少年编程·编程与数学
Echo``9 小时前
2:QT联合HALCON编程—图像显示放大缩小
开发语言·c++·图像处理·qt·算法
想睡hhh10 小时前
c++STL——stack、queue、priority_queue的模拟实现
开发语言·c++·stl
cloues break.10 小时前
C++初阶----模板初阶
java·开发语言·c++
wwww.wwww10 小时前
Qt软件开发-摄像头检测使用软件V1.1
开发语言·c++·qt
共享家952710 小时前
栈相关算法题解题思路与代码实现分享
c++·leetcode