C++ 常数代码技巧

brealid

2018-07-31 09:52:03

Personal

# C++ 代码小技巧(一) 在写代码的时候,我们常常会用一些小技巧,下面做简单介绍 ## 一、o1+o2+o3(常数优化) 如题,开优化开关。 洛谷上有O2优化选项,当然,你也可以这样:在代码开头这样加一句: ``` cpp #pragma GCC optimize("O1") #pragma GCC optimize("O2") #pragma GCC optimize("O3") ``` (逃~ Tips:此方法对STL非常有效!(NOIP赛场上不要用,不然会禁赛 3 年) ## 二、 ``inline`` 关键字(常数优化) 可以在一个函数的最开始加上inline,如: ``` inline int max(int a, int b) { if (a < b) return b; return a; } ``` 意义:inline可以跳过调用,直接引用,类似与直接将函数中的代码拿到当前函数中一样,如: ```pas inline int max(int a, int b) _ { \ if (a < b) | return b; }----------. return a; | | } _/ | | int main() | 编译时 { | 直接包 int a, b, c; | 含进去, cin >> a >> b >> c; | 加快程 cout << max(a, b) << endl; <----------| 序运行 | 速度。 cout << max(a, c) << endl; <----------| | cout << max(b, c) << endl; <----------/ return 0; } ``` 使用贴士: 1. 不要试图给递归函数加``inline``,这样并没什么卵用! 2. 不要试图给调用多次且代码量很大(即较复杂) 的非递归函数加``inline``, 这样并不会优化! 3. 本方法应该给调用多次或复杂度较低的函数使用(如:min, max, fast_IO相关函数)。 ## 三、``register`` 关键字(常数优化) 可以在一个变量声明的类型前面加上这个关键字。例: ``` // example #1: register int a; // example #2: register long long b[107]; // example #3: struct node1 { int rank; int num; }; register node1 c; // example #4: register struct node2 { int a, b; int c; } d; ``` 原理:我们运行的程序会放在内存里,所以访问速度还是较慢,所以我们可以将常用的变量放在离CPU更近的寄存器里(即register)。不过这些操作编译器可能已经帮我们自动做了,再有开启编译优化的时候这招并不明显。 使用小贴士: 1. 用``register``的变量只能在函数里声明!!!(这也很好理解) 2. 如上面 ``example #4``,register可以写在``struct``开头。 3. ~~因为寄存器空间有限,所以请只给反复使用的变量加``register``。(e.g.循环变量``i, j, k``)~~ 被批判了,这条保留 ## 四、template + typename模板 可以给一个函数声明成template + typename的形式。如: ``` // example: template <typename _Tp> _Tp max(_Tp a, _Tp b) { if (a > b) return a; return b; } // use: int x, y; cin >> x >> y; cout << max(x, y) << endl; cout << max<int>(x, y) << endl; //这两行等价 ``` 可以看到,声明这种函数的方法是在开头加上 ``` template <typename 类型1, typename 类型2, ...> // 类型个数任意 ``` 注意:若一个typename(即类型)不是该函数的一个输入参数,则需用``<...>``的形式告诉函数这个typename的类型! (这不是卡常优化用的,只是很有用) ## 五、fread大招 先给代码嘤嘤嘤 ``` char BufferRead[1 << 15]; int rLen = 0, rPos = 0; inline int Getchar(){ if (rPos == rLen) rPos = 0, rLen = fread(BufferRead, 1, 1 << 15, stdin); if (rPos == rLen) return EOF; return BufferRead[rPos++]; } ``` 说明: 1. fread比getchar快,所以用fread优化程度更高 2. 若本地运行且不加freopen,那么会出错(无休止的读入)!(因为fread会读满``1 << 15``个字节,而从文件读入时会读到文件结束符从而停止读入,不会出问题) 3. 不知道``fread``可以通过[这篇文章](https://hkxa.blog.luogu.org/cpp-fread)简要了解一下 4. 来自 @[Av_alon_](https://www.luogu.org/space/show?uid=47374) 大佬的温馨提醒: 本地 ``fread``不用``freopen`` 时可以用 ``CTRL+Z`` 结束输入。。 ## 六、快读整数(``fast_IO #1``) 代码还是很简单的,如下: ``` template <typename _TpInt> inline _TpInt read() { register int flag = 1; register char c = Getchar(); while ((c > '9' || c < '0') && c != '-') c = Getchar(); if (c == '-') flag = -1, c = Getchar(); register _TpInt init = (c & 15); while ((c = Getchar()) <= '9' && c >= '0') init = (init << 3) + (init << 1) + (c & 15); return init * flag; } ``` 几点说明: 1. 此方法的原理是:十进制,一位一位读入 2. ``flag`` 是负数标记 3. ``(c & 15)``:``'0'``的``ASCII``码是48,``(c & 15)``此处等于``(c % 16)``,所以本句``(c & 15)``的意义是``c-'0'`` 4. ``(init << 3) + (init << 1) = (init * 8) + (init * 2) = (init * 10)`` 5. ``typename``:可以读不同的类型 Tips:``'&'``和``'<<'``是位运算,关于位运算,可以上百度查资料 应用例子: ``` int a; long long b; short c; a = read<int>(); b = read<long long>(); c = read<short>(); ``` ## 七、快读实数(``fast_IO #2``) 仿照整数快读,写出实数快读: ``` template <typename _TpRealnumber> inline double readr() { register int flag = 1; register char c = Getchar(); while ((c > '9' || c < '0') && c != '-') c = Getchar(); if (c == '-') flag = -1, c = Getchar(); register _TpRealnumber init = (c & 15); while ((c = Getchar()) <= '9' && c >= '0') init = init * 10 + (c & 15); if (c != '.') return init * flag; register _TpRealnumber l = 0.1; while ((c = Getchar()) <= '9' && c >= '0') init = init + (c & 15) * l, l *= 0.1; return init * flag; } ``` 没什么好说明的。 应用例子: ``` float d; double e; d = readr<float>(); e = readr<double>(); ``` ## 八、快写整数(``fast_IO #3``) 使用递归执行。 ``` template <typename _TpInt> void write(_TpInt x) { if (x < 0) { putchar('-'); write<_TpInt>(~x + 1); } else { if (x > 9) write<_TpInt>(x / 10); putchar(x % 10 + '0'); } } ``` 说明: 1. ``(~x+1) = -x``,此处是为了``unsigned``型整数而写此句 2. 若去掉 ``if (x > 9)``,则会死循环 3. 本函数不可用于输出某种有符号类型的最小数!会无限输出负号!!!e.g. - ``write<int>(-2147483648);`` - ``write<short>(-65536);`` - ``etc...`` 应用例子: ``` write<int>(1); printf("\n"); write<short>(-891); printf("\n"); write<int>(-2147483647); printf("\n"); long long n = 6; write<long long>(n); printf("\n"); ``` ## 九、循环展开 有dalao补充,将一些循环展开,可以常数优化。 ``` for(int i=1;i<=20;i++) a[i]=1; ``` 可以改成 ``` for(int i=1;i<=20;i+=4) a[i]=1; a[i+1]=1; a[i+2]=1; a[i+3]=1; ``` ## 尾声:给个模板吧 ``` // luogu-judger-enable-o2 /* Problem: C++ 代码模板 Author: 航空信奥 Date: 2018/08/02 */ #pragma GCC optimize("O1") #pragma GCC optimize("O2") #pragma GCC optimize("O3") #include <stdio.h> #include <iostream> #include <string.h> #include <vector> #include <map> #include <set> #define lowbit(a) ((a) & (~a + 1)) // define 快 using namespace std; namespace AuthorName { // 防重名 inline char Getchar(); template <typename _TpInt> inline _TpInt read(); template <typename _TpRealnumber> inline double readr(); template <typename _TpInt> void write(_TpInt x); template <typename _TpSwap> inline void swap(_TpSwap &x, _TpSwap &y); int main() { // here : DO SOMETHING return 0; } char BufferRead[1 << 15]; int rLen = 0, rPos = 0; inline char Getchar() { if (rPos == rLen) rPos = 0, rLen = fread(BufferRead, 1, 1 << 15, stdin); if (rPos == rLen) return EOF; return BufferRead[rPos++]; } template <typename _TpInt> inline _TpInt read() { register int flag = 1; register char c = Getchar(); while ((c > '9' || c < '0') && c != '-') c = Getchar(); if (c == '-') flag = -1, c = Getchar(); register _TpInt init = (c & 15); while ((c = Getchar()) <= '9' && c >= '0') init = (init << 3) + (init << 1) + (c & 15); return init * flag; } template <typename _TpRealnumber> inline double readr() { register int flag = 1; register char c = Getchar(); while ((c > '9' || c < '0') && c != '-') c = Getchar(); if (c == '-') flag = -1, c = Getchar(); register _TpRealnumber init = (c & 15); while ((c = Getchar()) <= '9' && c >= '0') init = init * 10 + (c & 15); if (c != '.') return init * flag; register _TpRealnumber l = 0.1; while ((c = Getchar()) <= '9' && c >= '0') init = init + (c & 15) * l, l *= 0.1; return init * flag; } template <typename _TpInt> void write(_TpInt x) { if (x < 0) { putchar('-'); write<_TpInt>(~x + 1); } else { if (x > 9) write<_TpInt>(x / 10); putchar(x % 10 + '0'); } } template <typename _TpSwap> inline void swap(_TpSwap &x, _TpSwap &y) { _TpSwap t = x; x = y; y = t; } } int main() { AuthorName::main(); return 0; } ```