免费视频淫片aa毛片_日韩高清在线亚洲专区vr_日韩大片免费观看视频播放_亚洲欧美国产精品完整版

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
【洛谷日?qǐng)?bào)#219】C 預(yù)處理命令

1. 概述

預(yù)處理命令就是我們程序開頭以#字符開頭的命令。為什么叫預(yù)處理命令?因?yàn)檫@些命令是在編譯時(shí)的第一步就執(zhí)行了的,不會(huì)轉(zhuǎn)為匯編碼。

編譯器編譯代碼的步驟:

  1. 預(yù)處理。處理#include,#define等命令并刪除注釋,所以無論怎么寫都不會(huì)再第一步CE。

  2. 編譯。真編譯會(huì)分析代碼語法(開了O2還會(huì)改一些)并生成匯編文件。

  3. 匯編。將匯編碼轉(zhuǎn)為機(jī)器碼。

  4. 鏈接。根據(jù)電腦情況進(jìn)行重定位,鏈接庫等,生成可執(zhí)行文件

使用-E,-S-c可以選擇只執(zhí)行第1步,1~2步,1~3步。如果對(duì)本文的知識(shí)有疑惑,您可以選擇使用g++ -E 1.cpp -o 1.i來獲取預(yù)處理后的.i文件深刻體會(huì)。另外-S也可以用于獲取匯編碼。

絕大部分預(yù)處理命令在OI里用處不大,但也有功能強(qiáng)大的預(yù)處理命令。

#符號(hào)應(yīng)該是這一行的第一個(gè)非空字符。不過,也可以打\把內(nèi)容移到下一行,就跟注釋一樣。

#define pi 3.14159 \
26535

//This is an \
example

這樣就把下一行內(nèi)容上移了。

洛谷的編輯器不會(huì)這么顯示,但本地編輯器上你能發(fā)現(xiàn)下一行也變成了注釋或預(yù)處理樣式。

常見的預(yù)處理命令如下:

#include 包含頭文件
#ifdef 或 #if defined 如果定義了一個(gè)宏, 就執(zhí)行操作
#ifndef 或 #if !defined 如果沒有定義一個(gè)宏,就指執(zhí)行操作
#define 定義一個(gè)宏
#undef 刪除一個(gè)宏
#pragma 自定義編譯器選項(xiàng),指示編譯器完成一些事

這里介紹3個(gè)最常用的預(yù)處理命令:#include,#define,#pragma

2. #include

這是最常見的文件包含命令。

無論你再厲害,什么東西可以手寫,也需要#include <cstdio>

命令本質(zhì)是把指定的文件中的函數(shù),變量,宏等全部導(dǎo)入,可以理解成把那個(gè)文件全部?jī)?nèi)容復(fù)制粘貼到你的代碼里了。

不過,如果是單純的粘貼,#include兩遍應(yīng)該會(huì)有重復(fù)定義CE才對(duì)。但是標(biāo)準(zhǔn)庫使用宏定義避免了這一點(diǎn)(參見后文)。自己寫頭文件時(shí)也要注意。

Question 0: #include必須接尖括號(hào)嗎?

事實(shí)上,#include命令不一定要使用尖括號(hào),使用引號(hào)也是完全可以的。

區(qū)別在于引號(hào)會(huì)優(yōu)先在要編譯的文件中找,沒找到才會(huì)調(diào)用標(biāo)準(zhǔn)庫里的文件。

當(dāng)然對(duì)于OIer來講,#include <cstdio>#include 'cstdio'就沒有任何區(qū)別了,但是此時(shí)尖括號(hào)更為規(guī)范。

在自己用C++開發(fā)小游戲時(shí),為了便于管理,可以像標(biāo)準(zhǔn)庫一樣把用途相似的函數(shù)單獨(dú)用一個(gè)文件保存。在需要時(shí)就將其包含,此時(shí)就需要用到引號(hào)了。

Question 1: 為什么引用標(biāo)準(zhǔn)庫的頭文件時(shí)不加.h?

在C語言中其實(shí)是要加的,只能寫#include <stdio.h>或者#include <math.h>

C++里把這些文件的后綴名去掉并在前面加了一個(gè)c比如#include <cmath>

但是這些傳統(tǒng)的庫你如果使用老寫法,仍然是可以過編譯的,只是不規(guī)范。

但是對(duì)于C++的新內(nèi)容(比如iostreamstack)就不能加.h了。

有人試了,會(huì)說#include <string.h>能用!但是string.h對(duì)應(yīng)的是C語言里的cstring庫而不是C++新增的那個(gè)string。使用前者是定義不了string類型的。cstring庫是提供一些內(nèi)存操作的函數(shù)和char數(shù)組的函數(shù)比如memset,memcpy,strlen。

Question 2: 萬能頭文件真的萬能嗎?

現(xiàn)在的NOI)(P已經(jīng)支持萬能頭文件#include <bits/stdc++.h>。

(注意是正斜杠不是反斜杠,寫錯(cuò)了有可能CE)

事實(shí)上他包含的東西你是不可能記完的,但是您能用到的東西里面絕對(duì)都有。

C++11里還新包括了random,unordered_map等庫。
詳見stdc++.h原文件

雖然說不上萬能,OI里的確完全夠用了。

辟謠?。?!萬能頭文件并不會(huì)減慢程序運(yùn)行速度,內(nèi)存上的增加幾乎可以忽略。在編譯時(shí)main里沒有用到的東西就會(huì)被優(yōu)化掉。

而且你隨時(shí)帶上十幾個(gè)頭文件,又在說萬頭不好,根本沒說服力

當(dāng)然有可能增加編譯時(shí)間和源程序大小,然并卵

Question 3: 為什么我看到別人有在程序中途包含文件的神奇操作?

之前說過#include的本質(zhì)是把指定文件復(fù)制進(jìn)這一行,所以如果是在函數(shù)內(nèi)寫的這個(gè)命令,就只對(duì)這一個(gè)函數(shù)有效。

void func()
{
    #include 'test.h'
    mmm();//可以使用test.h里的函數(shù)
}

int main()
{
    func();
    mmm()//CE。不能使用test.h里的函數(shù)。
}

但是OI里不能這么用,因?yàn)闃?biāo)準(zhǔn)庫還涉及到命名空間的問題。

Quetion 4: 自己寫的頭文件到底是怎么用的?

按照標(biāo)準(zhǔn)的話,.h用于存放大篇的宏定義和函數(shù),變量的聲明(也就是函數(shù)第一行的函數(shù)名和參數(shù)列表),而同名的.cpp則存放函數(shù)的具體實(shí)現(xiàn)。.h里寫一個(gè)#include <test.cpp>。主程序只要包含test.h就可以使用庫里的函數(shù)了。

不過為了節(jié)省工作量,我們可以在.h里就直接定義好函數(shù),也可以選擇在主程序里直接#include <test.cpp>。包含命令的本質(zhì)是復(fù)制粘貼,這樣寫也是完全沒有問題的。

使用萬能頭文件不要用的變量名:y1, next, time, rand

包括很多常見單詞最好都不用,有些Windows可以,但是評(píng)測(cè)時(shí)會(huì)CE。

3. #define

命令#define 叫做宏定義,用于代碼中的字符串替換。是最有用的預(yù)處理指令

1. 不帶參數(shù)的宏

#define MAX 10000
if (9874 > MAX)
     return 0;

上述代碼定義宏MAX,這句以后的'MAX'就代表10000。if中的式子為false。

該方法可用于替代const定義常量,而且只做了代碼替換,運(yùn)行時(shí)不占用空間。也可以用于簡(jiǎn)化標(biāo)準(zhǔn)庫里名字超長(zhǎng)的函數(shù)。

另外如果這個(gè)常量需要多次進(jìn)行運(yùn)算(比如模數(shù)),據(jù)說寫成const是更快的,經(jīng)過個(gè)人不完全測(cè)試的確是這樣的,但是效率差別很小,所以也不必過多在意,還是看自己更喜歡哪種寫法。

注意:

1. #define不會(huì)替換字符串和注釋中的宏(廢話)

2. 替換宏時(shí)需要完全匹配,如定義宏“super”后,“supermarket”不會(huì)被部分替換。

2. 帶參數(shù)的宏

事實(shí)上,宏跟函數(shù)一樣,可以帶有參數(shù)。

例:用圓的半徑求其周長(zhǎng)和面積。

#define pi 3.14159
#define AREA(i) i*i*pi

double d;

int main()
{
    cin >> d;
    cout << AREA(d)<< endl ;
    return 0;
}

我們把宏寫成AREA這種像函數(shù)的形式,之后出現(xiàn)AREA(i)時(shí),
先發(fā)現(xiàn)括號(hào)里為2,即i=2,然后再做替換。

由于只做字符串替換,所以#define不僅可以定義常量,還可以定義表達(dá)式,函數(shù),甚至代碼段。

#define sum(a,b,c) (a)+(b)+(c)
#define max(a,b) (a>b)?(a):(b)
#define fors(a,b) for(int i=(a);i<=(b);i++)

利用宏定義可以使代碼更加簡(jiǎn)潔易懂,同時(shí)用#define定義max等函數(shù)。速度快于函數(shù),但也沒快多少。

注意:

命令#define命令后第一個(gè)單詞為宏,其余為宏體。

#define int long long
#define abc def ghi \
jkl

#define register

在第一句中,第一個(gè)int為替換體,即以后int代表long long。

在第二句中,只有abc作為宏體,之后的abc被替換為def ghi jkl,反斜杠只有換行作用。

在第三句中,程序里所有的register會(huì)被刪除,可以用于調(diào)試。

特例(不是完全字符串替換,感謝@Black_white_Tony dalao):

我們都知道vector <pair<int,int>>會(huì)因?yàn)?gt;>被識(shí)別為右移而CE所以必須補(bǔ)空格。但是如果這樣寫:

#define pii pair<int,int>
vector <pii> a;

卻可以正常通過編譯,這是因?yàn)槿绻鹍efine中的最后一個(gè)字符和后面第一個(gè)字符能構(gòu)成新運(yùn)算符時(shí),就會(huì)自動(dòng)加上空格。大家可以用g++ -E指令看得更透徹一些。

兩個(gè)運(yùn)算符構(gòu)成新運(yùn)算符加空格:<< >> -> ++ && += >=

這個(gè)特例也許就是為了STL套STL的問題設(shè)計(jì)的吧。

注:C++11里是可以直接寫vector <pair<int,int>>的,但是你如果使用了宏定義,第一步預(yù)處理后的文件在這里仍會(huì)加上空格。

3. 宏的高級(jí)應(yīng)用

##:連接左右兩端的字符串

#:把后面的參數(shù)變?yōu)橐粋€(gè)字符串(即強(qiáng)行加上'')

#define a(x) p##x
#define b(x) #x

int p1 = 3, p2 = 4;

int main()
{
    printf('%d %d\n',a(1),a(2));
    puts(b(qwqwq));
}
//Output:
//3 4
//qwqwq

#ifdef 如果定義了宏

#ifndef 如果沒定義宏

#endif以上兩句的終止句(相當(dāng)于右括號(hào))

在標(biāo)準(zhǔn)庫中,每包含一個(gè)頭文件,這個(gè)頭文件里就會(huì)define一個(gè)表示這個(gè)文件已被包含的宏,如果這個(gè)文件第二次被包含,#ifndef為假不再執(zhí)行,就會(huì)跳過文件,這樣就可以避免重復(fù)包含導(dǎo)致CE。

有些宏是在不同編譯環(huán)境里就定義好的,利用這些就可以做些趣事。

#ifndef ONLINE_JUDGE
    freopen('testdata.in','r',stdin);
    freopen('testdata.out','w',stdout);
#endif
//很多OJ(包括洛谷)都有這個(gè)宏

或者也可以在開頭定義一個(gè)debug宏,把調(diào)試輸出的語句用#ifndef括上,這樣刪除調(diào)試輸出就只需要注釋一行。

其他預(yù)定義的宏:(摘自cppreference)

__cplusplus //C++版本號(hào)
__FILE__ //文件名
__DATE__ //編譯日期
__TIME__ //編譯時(shí)間
__LINE__ //這一行的行號(hào)

4. 宏的撤銷

能定義的宏就能取消,使用#undef直接接宏名就可以撤銷宏(包括預(yù)定義的)。

#define sum(a,b) a+b
#define e 2.718
int a=sum(9,6);
double b=e*3;
#undef sum(a,b)
#undef e
#undef __cplusplus

5. 宏的缺點(diǎn)

宏雖然方便易用,但也有許多缺點(diǎn)。

I. 改變運(yùn)算優(yōu)先級(jí)
#define DEF 2+3
int a = DEF+5;
int b = DEF*7;

DEF以2+3的形式直接帶入,沒有轉(zhuǎn)化為5

在A的定義中,a將被解釋為“2+3+5”,其值為10.

但B將被解釋為“2+3*7”,乘法先算,值為23,不是我們希望的35.

解決方法就是在參數(shù)左右加上括號(hào)

II. 沒有固定的數(shù)據(jù)類型
#define MAX 1e6
int a[MAX];

此時(shí)會(huì)CE。因?yàn)?e6是一個(gè)double類型,數(shù)組大小只能用int,由于MAX是文本替換導(dǎo)致這里并不會(huì)轉(zhuǎn)換類型。

這是可以在前面加上(int),或者使用const定義常量。

4. #pragma

在我們尋找一道題最優(yōu)解的時(shí)候,最快的人(如果沒打表)往往會(huì)有幾十行的#pragma來卡常。那么這個(gè)命令有什么用?卡常的原理是什么呢?

#pragma命令可以指定編譯選項(xiàng),或者讓編譯器完成一些命令。功能非常強(qiáng)大,這里只做非常淺顯的介紹。

部分內(nèi)容摘自百度百科。

1. #pragma once

添加在頭文件的開頭,可以告訴編譯器這個(gè)文件最多編譯一次,也可以用于防止重復(fù)包含頭文件。比前文#ifndef好用,只是標(biāo)準(zhǔn)庫里沒用這個(gè)。

2. #pragma message()

讓編譯器輸出括號(hào)里的字符串,配合#ifdef,可以在編譯時(shí)就輸出一些特定的信息。

3. #pragma comment()

本身用于鏈接文件,OI里可以用來手動(dòng)擴(kuò)棧

#pragma comment(linker,'/STACK:1024000000,1024000000')

4. #pragma GCC target()

這個(gè)找遍全網(wǎng)也沒有準(zhǔn)確定義,大概就是將括號(hào)里的東西識(shí)別為指令。指令的速度比函數(shù)更快,借此加速。

#pragma GCC target('popcnt')可以讓內(nèi)置函數(shù)__builtin_popcount()的速度提高一倍以上。

另外,如果你想使用指令集,也可以使用這條指令把指令集括上。

#pragma GCC target('avx,avx2,sse,sse2,sse3,sse4.1,sse4.2')

5. #pragma pack() & pop()

用于對(duì)齊結(jié)構(gòu)體

//#pragma pack(4)
struct Node
{

    int a;
    long long b;
}x;

本來一個(gè)結(jié)構(gòu)體的每個(gè)變量都會(huì)與最大的那個(gè)對(duì)齊,比如例子中int就與long long對(duì)齊了,字節(jié)數(shù)也為8。所以sizeof x = 16

但是如果有了那句#pragma,每個(gè)變量就會(huì)與4對(duì)齊,所以int字節(jié)數(shù)為4,long long由于本來就大于4就被忽略,sizeof x = 12。這樣做一定程度上可以省空間。

但是對(duì)齊其實(shí)效率更高,所以x大一點(diǎn)好。

pop()可以用來取消pack()指令

6. #pragma GCC optimize()

將括號(hào)里的字符串帶入編譯參數(shù),相當(dāng)于可以自定義編譯參數(shù)。

如果輸入數(shù)字的話就會(huì)進(jìn)行O1/O2/O3優(yōu)化。用這個(gè)命令可以開啟編譯器自帶的優(yōu)化。

但是只能是編譯優(yōu)化方面的參數(shù),比如-o指定文件名肯定不能加在里面。

最后附贈(zèng)網(wǎng)絡(luò)上廣泛流傳的40行優(yōu)化:

#pragma GCC target('sse,sse2,sse3,sse4.1,sse4.2,popcnt,abm,mmx,avx')
#pragma comment(linker,'/STACK:102400000,102400000')
#pragma GCC optimize('Ofast')
#pragma GCC optimize('inline')
#pragma GCC optimize('-fgcse')
#pragma GCC optimize('-fgcse-lm')
#pragma GCC optimize('-fipa-sra')
#pragma GCC optimize('-ftree-pre')
#pragma GCC optimize('-ftree-vrp')
#pragma GCC optimize('-fpeephole2')
#pragma GCC optimize('-ffast-math')
#pragma GCC optimize('-fsched-spec')
#pragma GCC optimize('unroll-loops')
#pragma GCC optimize('-falign-jumps')
#pragma GCC optimize('-falign-loops')
#pragma GCC optimize('-falign-labels')
#pragma GCC optimize('-fdevirtualize')
#pragma GCC optimize('-fcaller-saves')
#pragma GCC optimize('-fcrossjumping')
#pragma GCC optimize('-fthread-jumps')
#pragma GCC optimize('-funroll-loops')
#pragma GCC optimize('-fwhole-program')
#pragma GCC optimize('-freorder-blocks')
#pragma GCC optimize('-fschedule-insns')
#pragma GCC optimize('inline-functions')
#pragma GCC optimize('-ftree-tail-merge')
#pragma GCC optimize('-fschedule-insns2')
#pragma GCC optimize('-fstrict-aliasing')
#pragma GCC optimize('-fstrict-overflow')
#pragma GCC optimize('-falign-functions')
#pragma GCC optimize('-fcse-skip-blocks')
#pragma GCC optimize('-fcse-follow-jumps')
#pragma GCC optimize('-fsched-interblock')
#pragma GCC optimize('-fpartial-inlining')
#pragma GCC optimize('no-stack-protector')
#pragma GCC optimize('-freorder-functions')
#pragma GCC optimize('-findirect-inlining')
#pragma GCC optimize('-fhoist-adjacent-loads')
#pragma GCC optimize('-frerun-cse-after-loop')
#pragma GCC optimize('inline-small-functions')
#pragma GCC optimize('-finline-small-functions')
#pragma GCC optimize('-ftree-switch-conversion')
#pragma GCC optimize('-foptimize-sibling-calls')
#pragma GCC optimize('-fexpensive-optimizations')
#pragma GCC optimize('-funsafe-loop-optimizations')
#pragma GCC optimize('inline-functions-called-once')
#pragma GCC optimize('-fdelete-null-pointer-checks')

注意:

  1. 這類優(yōu)化的效果玄學(xué),因人而異,也與編譯環(huán)境相關(guān)。但是最壞情況也就沒有用,這些代碼不會(huì)因?yàn)榫幾g環(huán)境CE。

  2. 由于O2/O3/Ofast優(yōu)化已經(jīng)到達(dá)了改寫循環(huán),刪除多余代碼等毀天滅地的程度,很容易改變代碼的原意導(dǎo)致玄學(xué)錯(cuò)誤。使用這些優(yōu)化的時(shí)候一定要保證自己的代碼規(guī)范,否則就會(huì)有玄學(xué)問題出現(xiàn)。

  3. 并不知道NOI)(P能不能用,最好不用(你也不可能背下來)

5. Others

還有一些命令,這里花上幾行介紹一下。

#error //在這一行顯示一個(gè)CE信息,并中斷編譯
#warning //在這一行顯示警告信息
#line //指定下一行的行號(hào)
#if //如果滿足則執(zhí)行,后面應(yīng)接布爾表達(dá)式,以#endif結(jié)尾
#elif //#if語句的分支

完結(jié)撒花,感謝陪伴


洛谷日?qǐng)?bào)接受投稿,采用后有薄禮奉送,請(qǐng)戳 
https://www.luogu.org/discuss/show/47327 .


本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
C中的預(yù)編譯宏定義
C語言中的預(yù)處理詳解
C語言的那些小秘密之預(yù)處理
C語言入門之C 預(yù)處理器
長(zhǎng)文花了兩天時(shí)間整理了STM32中的一些C語言知識(shí)點(diǎn)
C語言預(yù)處理命令分類和工作原理
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長(zhǎng)圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服