C++ 常數程式碼技巧(轉)
阿新 • • 發佈:2018-12-09
C++ 程式碼小技巧(一)
在寫程式碼的時候,我們常常會用一些小技巧,下面做簡單介紹
一、o1+o2+o3(常數優化)
如題,開優化開關。
洛谷上有O2優化選項,當然,你也可以這樣:在程式碼開頭這樣加一句:
#pragma GCC optimize("O1")
#pragma GCC optimize("O2")
#pragma GCC optimize("O3")
(逃~
Tips:此方法對STL非常有效!(NOIP賽場上不要用)
二、 inline
關鍵字(常數優化)
可以在一個函式的最開始加上inline,如:
inline int max(int a, int b) { if (a < b) return b; return a; }
意義:在編譯時,把這個函式放到使用它的地方,如:
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; }
使用貼士:
- 不要試圖給遞迴函式加
inline
,這樣並沒什麼卵用! - 不要試圖給呼叫多次且程式碼量很大(即較複雜) 的非遞迴函式加
inline
, 這樣並不會優化! - 本方法應該給呼叫多次或複雜度較低的函式使用(如:min, max, fast_IO相關函式)。
三、register
關鍵字(常數優化)
可以在一個變數宣告的型別前面加上這個關鍵字。例:
// example #1: register int a; // example #2: register long long b[10007]; // example #3: struct node1 { int rank; int num; }; register node1 c; // example #4: register struct node2 { int a, b; int c; } d;
原理:我們執行的程式會放在記憶體裡,所以訪問速度還是較慢,所以我們可以將常用的變數放在離CPU更近的暫存器裡(即register)。不過這些操作編譯器可能已經幫我們自動做了,再有開啟編譯優化的時候這招並不明顯。
使用小貼士:
- 用
register
的變數只能在函式裡宣告!!!(這也很好理解) - 如上面
example #4
,register可以寫在struct
開頭。 - 因為暫存器空間有限,所以請只給反覆使用的變數加
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++];
}
說明:
- fread比getchar快,所以用fread優化程度更高
- 若本地執行且不加freopen,那麼會出錯(無休止的讀入)!(因為fread會讀滿
1 << 15
個位元組,而從檔案讀入時會讀到檔案結束符從而停止讀入,不會出問題) - 不知道
fread
可以通過這篇文章簡要了解一下
六、快讀整數(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;
}
幾點說明:
- 此方法的原理是:十進位制,一位一位讀入
flag
是負數標記(c & 15)
:'0'
的ASCII
碼是48,(c & 15)
此處等於(c % 16)
,所以本句(c & 15)
的意義是c-'0'
(init << 3) + (init << 1) = (init * 8) + (init * 2) = (init * 10)
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>
inline void write(_TpInt x)
{
if (x < 0) {
putchar('-');
write<_TpInt>(~x + 1);
}
else {
if (x > 9) write<_TpInt>(x / 10);
putchar(x % 10 + '0');
}
}
說明:
(~x+1) = -x
,此處是為了unsigned
型整數而寫此句- 若去掉
if (x > 9)
,則會死迴圈 - 本函式不可用於輸出某種有符號型別的最小數!會無限輸出負號!!!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> inline 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>
inline 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;
}