当前位置: 首页 > news >正文

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


author: hjjdebug
date: 2025年 04月 23日 星期三 13:40:21 CST
description: c++中的enum变量 和 constexpr说明符


文章目录

    • 1.Q:enum 类型变量可以有++,--操作吗?
      • 1.1补充: c/c++中enum的另一个细微差别.
    • 2.Q: constexpr 修饰的函数,要求传入的参数必需是常量吗?
    • 3. Q constexpr 编译期求值真正的意思是什么?
      • 3.1 debug 版本, constexpr 修饰无效
      • 3.2 release 版本, constexpr 函数被优化掉了
    • 4. constexpr 中碰到了一个左值引用绑定问题
    • 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_chk1080:	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> //循环到1076108f:	48 83 c4 08          	add    $0x8,%rsp   //局部空间栈恢复1093:	31 c0                	xor    %eax,%eax  //返回值1095:	5b                   	pop    %rbx   //恢复rbx1096:	5d                   	pop    %rbp //恢复rbp1097:	c3                   	retq   //函数返回 1098:	0f 1f 84 00 00 00 00 	nopl   0x0(%rax,%rax,1)109f:	00 

完美验证了我的设想!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>

相关文章:

  • 【项目篇】仿照RabbitMQ模拟实现消息队列
  • 咖啡机语音芯片方案-WTN6040FP-14S直接驱动4欧/3W喇叭-大功率输出
  • 彻底禁用windows的语音识别快捷键win+ctrl+s
  • date-picker组件的shortcuts为什么不能配置在vue的data的return中
  • 量子混合计算革命:Qiskit 3.0开启云上量子开发新时代
  • 为什么圆形在GeoJSON中被表示为多边形(Polygon)而不是圆形类型
  • 2025职业本科网络安全课程体系设计:如何培养行业急需的实战型人才?
  • 飞帆控件:在编辑模式下额外加载的库
  • 【Amazing晶焱科技高速 CAN Bus 传输与 TVS/ESD/EOS 保护,将是车用电子的生死关键无标题】
  • 【新能源科学与技术】MATALB/Simulink小白教程(二)Buck电路【新能源电力转换与控制仿真】
  • 嵌入式WebRTC音视频实时通话EasyRTC助力打造AIOT智能硬件实时通信新生态
  • 用Python解锁链上数据的奥秘:从数据分析到可视化洞察
  • 线程封装
  • Docker镜像与容器概念解析
  • 将天气查询API封装为MCP服务
  • 【官方正版,永久免费】Adobe Camera Raw 17.2 win/Mac版本 配合Adobe22-25系列软
  • 【UML建模】数据流图 绘制
  • SQL进阶知识:四、索引优化
  • 网页在浏览器中显示的原理(简要)
  • The backpropagation and the brain
  • 乌代表团与美特使在伦敦举行会谈,双方同意继续对话
  • 王毅同伊朗外长阿拉格齐会谈
  • 郑庆华任同济大学党委书记
  • 言短意长|大学校门到底应不应该开放?
  • 安徽临泉一小区交付后多楼层现裂缝,专家组论证称不影响安全
  • 首映|国家自博馆4D电影《海洋深深》:潜入深海向地球发问