C++代码优化 Chapter1
本文将介绍一些常见的 C++ 代码优化技巧,本文为该系列的第一章,未来不定期更新。
使用前置递增(递减)运算符可能很多从 C 语言开始学习的朋友会对此感到困惑,觉得这两者在适用的情况下可以任意选择,如 for 循环语句写作 for (int i = 0; i < n; i++) 。这样做当然没错,但会造成一定程度上的资源浪费,因为后置递增运算符需要先将原本的变量值保存下来,再对其进行递增操作,而在此 for 循环中,我们并不需要使用原本的 i 值。并且 ++i 的写法相对来说更符合我们的意愿。
当然对于基本数据类型来讲,在经过编译器的优化之后,两者的效率可能并没有什么差别,但对于 STL 中的模板容器,或是自定义数据结构的迭代器来讲,前置运算符的效率显然要更高,因为单个对象所占的内存空间更大,使得拷贝暂存的开销也越大。
因此,除非必要情况,应该尽可能使用前置递增(递减)运算符。
使用引用传递引用类型变量并不是一个对象,它只是一个已存在对象的别名,因此在作为变量传递时不会经过拷贝构造的过程,能够显著地提升效率,以下为值传递的情况。
struct myStruct
{
...
C++复合类型的声明
声明语句的构成
在 C++ 中,一条声明语句由一个基本数据类型(base type)和紧随其后的一个声明符(declarator)列表组成。
复合类型的声明定义多个变量对于基本数据类型变量的声明,声明符就是变量名。但对于复合类型来讲,例如指针类型和引用类型,变量名却只是声明符的一部分。例如 int *b; 需要注意的是, “*”运算符修饰的对象是变量名,而不是基本类型名。例如 int *a, b;,此处变量 b 的数据类型为 int,而非 int *。
指针的引用指针引用的正确定义形式如下:
int a = 1, * b = &a;
int*& c = b; // c为指针b的引用
int&* d = b; // 错误,d表示指向引用的指针,而引用本身并不是一个对象,无法使用指针进行指向
要想正确理解一个复杂的复合类型,可以从右向左进行阅读,离变量名最近的符号越接近该变量的真实类型,如上例中的引用符。
LeetCode周赛总结 第277场
本次周赛没想到比上周还要简单,前三题都可以用非常简单的方法快速解决,第四题如果想对了方向其实也比较简单。
元素计数题目链接元素计数
解题思路相当基础的题目,要同时具有一个严格较小元素和一个严格较大元素,只需要保证这个数 num 满足 num > minVal && num < maxVal即可。
解题代码class Solution {
public:
int countElements(vector<int>& nums) {
int minVal = INT32_MAX, maxVal = INT32_MIN;
for (int i = 0; i < nums.size(); i++) {
minVal = min(minVal, nums[i]);
maxVal = max(maxVal, nums[i]);
}
int res = 0;
for (int ...
使用OpenGL渲染一个立方体
本文将介绍如何使用最为常用的图形 API —— OpenGL 来渲染一个立方体,代码部分来自于《Computer Graphics Programming in OpenGL with C++》,并加入了自己的理解。
基本过程环境配置在编写程序之前,需要先配置好一些有助于程序编写的第三方库,本次实验需要用到的库有三个:用于窗口管理的 GLFW 库,扩展功能的 GLEW 库,以及用于数学运算的 GLM 库。
IDE 使用的是 Visual Studio 2019,并安装了 GLSL Language Integration 插件来实现 glsl 语言的代码高亮和自动补全。
具体的环境配置过程在此不过多赘述,本文主要聚焦于代码的实现。
窗口的创建要将渲染的图像显示出来,就需要创建一个特定的显示窗口,首先通过 glfwWindowHint() 指定 OpenGL 的版本号,再使用 glfwCreateWindow() 创建 GLFW 窗口。由于创建 GLFW 窗口并不会自动将它与当前 OpenGL 上下文关联起来,因此还需要调用 glfwMakeContextCurrent().
为了防止 ...
LeetCode周赛总结 第276场
本次周赛相对比较简单,前三题花的时间比较短,但无奈最后一题还是没思路。。。
将字符串拆分成若干长度为 k 的组题目链接将字符串拆分成若干长度为 k 的组
解题思路遍历字符串 s 的每个字符并加入到一个临时字符串中,当此临时字符串长度为 k 时,加入到结果数组中并清空此字符串。若此时遍历到字符串的最后一个字符且此时临时字符串长度没有达到 k 时,则向其末尾填入字符 fill 直到临时字符串长度达到 k,再加入到结果数组中。
解题代码class Solution {
public:
vector<string> divideString(string s, int k, char fill) {
vector<string> res;
string newStr;
for (int i = 0; i < s.size(); i++) {
newStr += s[i];
if (newStr.size() == k) {
...
2021年游戏总结 下
本文承接上文,继续聊聊 2021 年我所体验的一些游戏。
伊苏8:丹娜的陨涕日今年玩过最喜欢的 jrpg 作品,几乎满足了我对于一款优秀 jrpg 的全部幻想。
战斗方面爽快而又不失深度。伊苏系列向来以高速的即时制战斗著称,本作也不例外,角色与敌人的动作都相对较快,技能无冷却加之技能槽的回复速度很快,因此本作的战斗与大部分 jrpg 讲究策略的慢节奏战斗有很大不同。但这并不意味着本作战斗就十分无脑,弹反、极限闪避、弱点属性等机制的加入很好的提升了战斗系统的深度。
本作的剧情也很优秀。剧情悬念设置十分得当,给予玩家很强的故事驱动力,人物塑造极佳,进一步增强代入感。
本作最值得夸赞的还是地图探索方面的体验。本作采用的是一个非常典型的“银河恶魔城”式的地图设计,玩家需要探索地图的各个角落,从而解锁新的能力,再次进行更深一步的探索。同时本作探索不光局限于对于物品的获取,还包括了 npc 的发现,这一切与 jrpg 的强故事驱动相结合,使得探索欲望进一步上升。
最后讲讲我对于本作不满的地方。首先是本作的保卫战部分,这部分实属多余,不仅不有趣,还很大程度破坏了地图探索的连贯性与叙事节奏,纯粹 ...
2021年游戏总结 上
转眼间 2021 年已经过去,这一年间也体验了很多不同平台、不同类型的作品。本文将对此做一个小小的总结,内容主要是个人对 2021 年玩过的游戏的一些个人想法。此外,由于篇幅受限,因此并不会包含所有的游戏,部分体验不够深入的或者无话可聊的游戏将不会出现。
双人成形这算是今年玩过的最开心的游戏了,和两个室友花了大概 15 小时通关。
游戏在本身的关卡设计就很优秀,在融合了双人机制下更加显得精妙。且游戏对于资源的利用可谓毫不吝啬,每一大关就完全舍弃掉之前机制,完全变成“另一个游戏”。虽然这样各种机制浅尝辄止的设计理念可能会带来所谓的游戏深度不足,但这并不是本作所追求的,本作就是想给玩家一个极为丰富的一周目流程。仔细想想现在很多游戏的流程越来越长,但很多内容都是注水的,玩家在这些重复劳动的过程中可能会对游戏失去兴趣,最终导致游戏烂尾。而双人成形就是一个每个关卡细节精心编排打磨,每种机制总能在玩家感到无聊之前及时结束,从而转向新的机制。我在实际游戏过程中的感受也是如此,对之后关卡机制的期待是我在现在大部分游戏中所体会不到的。
还有一点值得提的就是本作的箱庭式关卡设计,几乎每个关 ...
记一次CTF校赛
本文记录一下刚刚结束的CTF校赛,感觉本次校赛题目出得实在有点坑,但也算学到了不少东西。
Cryptoeasy_ECC问题描述已知椭圆曲线加密Ep(a,b)参数为
p = 15424654874903
a = 305423748
b = 315284355172
G(15079176652031,83237376468)
私钥为
k = 655321
求公钥K(x,y)
思路常规的椭圆曲线加密问题,直接根据加密原理编写解密程序,得到两个结果。根据问题提示,考虑 flag 为两数的异或结果。
解密脚本def add(A, B):
if A == (0, 0):
return B
if B == (0, 0):
return A
x1, y1 = A
x2, y2 = B
if A != B:
λ = (y2 - y1) * pow((x2 - x1), p - 2, p)
else:
λ = (x1 * x1 * 3 + a ...
C++比较函数cmp
本文将简单介绍C++比较函数 cmp.
排序函数sort()sort函数是我们常用的库函数,它的参数如下:
void sort (RandomAccessIterator first, RandomAccessIterator last, Compare cmp);
通过传入容器的迭代器(或指针),我们可以对指定位置进行排序:
vector<int> nums = { 1,3,2,4,5 };
sort(nums.begin(), nums.end()); //排序得到nums = { 1,2,3,4,5 }
可见,sort 函数的比较函数 cmp 默认参数为升序排列,当然也可以自定义函数来实现不同的排序方法。
比较函数cmp()自定义比较函数首先编写一个示例用以解释:
struct MyStruct {
int weigth;
string str;
};
bool cmp(const MyStruct& ms1, const MyStruct& ms2) {
retu ...
位运算基础及应用
计算机中的数据以二进制的形式存储,即0、1两种状态。位运算就是直接对整数在内存中的二进制位进行操作。本文将介绍位运算的各操作符以及常见应用。
操作符&(与)参加运算的两个数据,按二进制位进行与运算。(两位都为1时才为1,否则为0)
例如 12 和 5 两个数,二进制形式分别为 1100、0101. 逐个按位进行与运算得:0100 = 4. 即 12 & 5 = 4.
|(或)参加运算的两个数据,按二进制位进行或运算。(两位都为0时才为0,否则为1)
例如 12 和 5 两个数,二进制形式分别为 1100、0101. 逐个按位进行或运算得:1101 = 13. 即 12 | 5 = 13.
^(异或)参加运算的两个数据,按二进制位进行异或运算。(相同为0,不同为1)
例如 12 和 5 两个数,二进制形式分别为 1100、0101. 逐个按位进行异或运算得:1001 = 9. 即 12 ^ 5 = 9.
~(取反)参加运算的数据,其各二进制位进行取反运算。(0变成1,1变成0)
例如 5,对于一个 int16 型数据,它的二进制形式为 0 000 0000 0000 0 ...