工作中遇到的:
一個頭文件中的:
#pragma warning(disable:4996)
#pragma warning(disable:4244)
#pragma warning(disable:4267)
不理解意思,遂查☟
C/C++中的預編譯指令
程序的編譯過程可以分為預處理、編譯、匯編三部分,其中預處理是首先執行的過程,預處理過程掃描程序源代碼,對其進行初步的轉換,產生新的源代碼提供給編譯器。
預處理過程讀入源代碼之后,會檢查代碼里包含的預處理指令,完成諸如包含其他源文件、定義宏、根據條件決定編譯時是否包含某些代碼的工作。下面介紹一些C/C++中預編譯的指令。
一 #指令
預處理指令以#號開頭,並且#號必須是該行除了任何空白字符外的第一個字符。
#后是指令關鍵字,在關鍵字和#號之間允許存在任意個數的空白字符。整行語句構成了一條預處理指令,該指令將在編譯器進行編譯之前對源代碼做某些轉換。
單純一個#號表示空指令,沒有任何作用。
二 #include指令
#include預處理指令的作用是在指令處展開被包含的文件。展開被包含的文件之后,在代碼就可以正常地調用該文件中所聲明的變量和函數。#include指令有兩種使用方法:
#include <xxx.h> #include "xxx.h"
第一種方法將待包含的頭文件使用尖括號括起來,預處理程序會在系統默認目錄或者括號內的路徑查找,通常用於包含系統中自帶的公共頭文件。
第二種方法將待包含的頭文件使用雙引號引起來,預處理程序會在程序源文件所在目錄查找,如果未找到則去系統默認目錄查找,通常用於包含程序作者編寫的私有頭文件。
文件包含可以是多重的,也就是說一個被包含的文件中還可以包含其他文件。在大型的程序中可能會產生重復包含的問題,如
#include "a.h" #include "b.h"
一個程序包含了a.h和b.h兩個頭文件,但a.h和b.h可能同時又都包含了c.h,於是該程序就包含了兩次c.h,這在一些場合下會導致程序的錯誤,可以通過下面的條件編譯進行解決。
三 #define、#undef指令
#define指令定義了一個標識符及一個串,標識符稱為宏名,源程序中宏名的每次出現都會用其定義的串進行替換,稱為宏替換。
#undef指令取消一個已定義的宏。
宏一般使用大寫字母定義,其可以出現在程序的任意地方。宏替換僅僅是以文本串代替宏標識符的過程,該過程很容易出現一些邏輯上的錯誤,需要仔細處理一些關於括號的問題。
以下代碼用宏定義了一個常量PI,但C++中建議使用const進行常量定義,因為宏替換並不會進行類型匹配之類的安全性檢查。同時用宏定義了一個MAX函數,其好處是沒有函數調用的額外開銷,運行速度較快,但容易出錯,而且大量的宏替換會增加代碼的長度。
void test1() { #define PI 3.14 #define MAX(a,b) (((a) > (b)) ? (a) : (b)) cout << PI << endl; //3.14 cout << MAX(2, 3) << endl; //3 #undef PI //cout << PI << endl; //編譯出錯 }
另外還有兩個特殊的運算符:
- 宏定義中的#運算符把跟在其后的參數轉換成一個字符串稱為字符串化運算符。
- 宏定義中的##運算符把出現在##兩側的參數合並成一個符號。
void test2() { #define CAT(n) "ABC"#n cout << CAT(123) << endl; //ABC123 #define NUM(a,b) a##b #define STR(a,b) a##b cout << NUM(1, 2) << endl; //12 cout << STR("Hello", "World") << endl; //HelloWorld }
四 #if、#elif、#else、#endif指令
這幾個指令稱為條件編譯指令,可對程序源代碼的各部分有選擇地進行編譯。
跟一般的if、else if、else語句類似,如果一個條件上的值為真,則編譯它對應的代碼,否則提過這些代碼,測試下一個條件上的值是否為真。注意,作為條件的表達式是在編譯時求值的,它必須僅含常量及已定義過的標識符,不可使用變量,也不可以含有操作符sizeof(sizeof也是編譯時求值)。
命令#endif標識一個#if塊的結束。
void test3() { #define OPTION 2 #if OPTION == 1 cout << "Option: 1" << endl; #elif OPTION == 2 cout << "Option: 2" << endl; //選擇這句 #else cout << "Option: Illegal" << endl; #endif }
五 #ifdef、#ifndef、#endif指令
這幾個也是條件編譯指令,其檢查后面指定的宏是否已經定義,然后根據檢查結果選擇是否要編譯后面語句。其中#ifdef表示”如果有定義“,#ifndef表示”如果沒有定義“。
這個通常可以用於防止重復包含頭文件的問題,如下所示:
#ifndef MYHEAD_H #define MYHEAD_H #include "myHead.h" #endif
六 #line指令
C語言中可以使用__FILE__表示本行語句所在源文件的文件名,使用__LINE__表示本行語句在源文件中的位置信息。#line指令可以重新設定這兩個變量的值,其語法格式為
#line number["filename"]
其中第二個參數文件名是可省略的,並且其指定的行號在實際的下一行語句才會發生作用。
void test4() { cout << "Current File: " << __FILE__ << endl; //Current File: d:\test.cpp cout << "Current Line: " << __LINE__ << endl; //Current Line: 48 #line 1000 "wrongfile" cout << "Current File: " << __FILE__ << endl; //Current File: d:\wrongfile cout << "Current Line: " << __LINE__ << endl; //Current Line: 1001 }
七 #error指令
#error指令在編譯時輸出編譯錯誤信息,可以方便程序員檢查出現的錯誤。
void test5() { #define OPTION 3 #if OPTION == 1 cout << "Option: 1" << endl; #elif OPTION == 2 cout << "Option: 2" << endl; #else #error ILLEGAL OPTION! //fatal error C1189: #error : ILLEGAL OPTION! #endif }
八 #pragma指令
該指令用來來設定編譯器的狀態或者是指示編譯器完成一些特定的動作,它有許多不同的參數。
1. #pragma once
在頭文件的最開始加入這條指令可以保證頭文件只被編譯一次。它可以實現上述使用#ifndef實現不重復包含頭文件同樣的功能,但可能會有部分編譯系統不支持。
2. #pragma message
該指令能夠讓編譯器遇到這條指令時就在編譯輸出窗口中將消息文本打印出來。其使用方法為:#pragma message(“消息文本”)
通過這條指令我們可以方便地記錄在是否在源代碼中定義過某個宏,如
#define ISPC #ifdef ISPC #pragma message("Macro ISPC is defined") //編譯輸出:Macro ISPC is defined #endif
3. #pragma warning
該指令能夠控制編譯器發出警告的方式,其用法舉例如:#pragma warning(disable : 4507 34; once : 4385; error : 164)
這個指令有三部分組成,其中disable部分表示忽略編號為4507和34的警告信息,once部分表示編號為4385的警告信息只顯示一次,error部分表示把編號為164的警告信息當做錯誤。
另外,其還有兩個用法
- #pragma warning(push [, n]):保存所有警告信息的現有的警告狀態,后面n是可選的,表示把全局警告等級設為n。
- #pragma warning(pop):彈出最后一個警告信息,取消在入棧和出棧之間所作的一切改動。
具體例如如下:
void test6() { #pragma warning(push) //保存編譯器警告狀態 #pragma warning(disable:4305) //取消4305的警告 bool a = 5; //無警告信息 #pragma warning(pop) //恢復之前的警告轉改 bool b = 5; //warning C4305: 'initializing' : truncation from 'int' to 'bool' }
4. #pragma comment
該指令將一個注釋記錄放入一個對象文件或可執行文件中。其使用方法為:#pragma comment(comment-type ,["commentstring"])
其中comment-type是一個預定義的標識符,指定注釋的類型,應該是compiler,exestr,lib,linker之一。常用的是lib關鍵字,可以幫我們連入一個庫文件。 如
#pragma comment(lib, "my.lib")
5. #pragma hdrstop
該指令表示預編譯頭文件到此為止,后面的頭文件不進行預編譯。
6. #pragma resource
該指令表示把指定文件中的資源加入工程,如
#pragma resource "*.dfm"
7. #pragma code_seg
該指令能夠設置程序中函數代碼存放的代碼段,開發驅動程序的時候會使用到。使用方法為:#pragma code_seg(["section-name" [,"section-class"] ])。
8. #pragma data_seg
該指令建立一個新的數據段並定義共享數據。一般用於DLL中,在DLL中定義一個共享的有名字的數據段,這個數據段中的全局變量可以被多個進程共享,否則多個進程之間無法共享DLL中的全局變量。其使用方法為:
#pragma data_seg("MyData") int value; //共享數據 #pragma data_seg()
9. #pragma pack
該指令規定數據在內存中的對齊長度,具體可以參考這里。
#pragma pack(1) struct S{char a; int b; }; void test7(){ cout << sizeof(S) << endl; } //5
除非注明,文章來自NoAlGo博客原創,轉載請保留鏈接:C/C++中的預編譯指令