第一次遇到#pragma once的時候,到網上找了些資料,大部分答案都大同小異,大概意思是讓編譯器只編譯一次。我們都知道,一般不在標頭檔案中定義全域性變數,因為那會導致該變數多次定義。那麼問題來了,既然用#pragma once預編譯命令可以防止重複編譯,為什麼不能在標頭檔案中定義全域性變數呢?這個問題長期困擾著我,平時也就是記住這些規則而已。正所謂要知其所以然嘛,那我們就來**一下這個問題。
我們從三個檔案開始。
第乙個檔案是test1.h
//#pragma once
struct test1
;
第二個檔案是test2.h
#pragma once
#include
"test1.h"
最後乙個檔案test.cpp
// test.cpp
#include
"stdafx.h"
//我用的是vs2015,其他編譯器可以不用包含這個標頭檔案.
#include
"test1.h"
#include
"test2.h"
intmain()
這種情況是可能的,因為我們可能會同時在原始檔中使用這兩個標頭檔案的內容。
下面我們來編譯一下:
為什麼會報錯呢?乙個原始檔包含乙個標頭檔案,只是以標頭檔案的形式展開,就是把頭檔案裡的內容copy到原始檔中去。在test.cpp檔案中,#include "test2.h"展開後,會包含乙個test1.h,而原本就包含了乙個test1.h。最終結果就是結構體test1被定義了兩次,然後報錯了。
那麼我把test1.h的注釋(//#pragma once)去掉會怎麼樣?答案是yes!編譯通過。#pragma once的作用是對於某乙個檔案,如果多次包含另乙個檔案的話,只編譯一次。
我們繼續建立3個檔案。
第乙個檔案為test1.h
#pragma once
int a =
0;
第二個檔案為test1.cpp
#include
"stdafx.h"
#include
"test1.h"
第三個檔案為test.cpp
// test.cpp
#include
"stdafx.h"
//我用的是vs2015,其他編譯器可以不用包含這個標頭檔案.
#include
"test1.h"
intmain()
編譯看下會發生什麼。
oh no!不是說好的用#pragma once只編譯一次嗎,怎麼又重定義了呢?寶貝你居然騙我。我們仔細觀察錯誤提示發現,lnk***之類的。這是說明在編譯的過程中,鏈結出錯了。(?a@@3ha)是編譯器對全域性變數a生成的乙個全域性符號,這個符號的作用相當於鏈結器識別的乙個id。
而test1.cpp和test.cpp兩個原始檔都包含了test1.h。因此int a = 0;會被編譯器展開到那兩個原始檔中,因此,也會生成兩個符號。在鏈結的過程中,這兩個符號重複了,所以就報錯了。
如果有同學對編譯和鏈結的過程不是很明白的,可以參考深入理解計算機系統的第七章,那裡講的很透徹。
針對上一部分的情況,那麼我想要在test1.cpp和test.cpp兩個原始檔中使用全域性變數a怎麼辦?
我們修改這3個檔案
test1.h:
#pragma once
extern
int a;
intfunc()
;
test.cpp:
// test.cpp
#include
"stdafx.h"
//我用的是vs2015,其他編譯器可以不用包含這個標頭檔案.
#include
"test1.h"
int a =0;
intmain()
test1.cpp:
#include
"stdafx.h"
#include
"test1.h"
intfunc()
在test1.h中,使用extern表示變數a是外來的,在這裡只是宣告它,並沒有定義。這樣也就不會生成兩個全域性符號。
以下是執行結果,可以看到test1.cpp中的func函式成功地修改了變數a的值。
#pragma once預編譯指令防止重複編譯是針對某乙個原始檔而言的,即某個原始檔包含了兩個標頭檔案,而那兩個標頭檔案之一又包含了另乙個,此時#pragma once會發生作用,起到只編譯一次的作用。
全域性變數不要定義在標頭檔案中則適用於多個原始檔的。多個原始檔同時包含某乙個標頭檔案,那個標頭檔案又定義了乙個全域性變數。此時會發生鏈結錯誤,解決方法是使用extern。
這些細節很容易被我們忽略掉,從而引起混淆。寫篇文章mark一下。
static的作用以及實驗
1 靜態區域性變數 普通的區域性變數在棧空間上分配,這個區域性變數所在的函式被多次呼叫時,每次呼叫這個區域性變數在棧上的位置都不一定相同。區域性變數也可以在堆上動態分配,但是記得使用完這個堆空間後要釋放之。static區域性變數中文名叫靜態區域性變數。它與普通的區域性變數比起來有如下幾個區別 位置 ...
stdafx h的作用以及原理
vc工程裡面經常見到stdafx.h這個標頭檔案,以前也沒有特別注意,但是這個檔案用不好經常會出錯,所以就google了一下,總算是弄清楚了。所謂標頭檔案預編譯,就是把乙個工程 project 中使用的一些mfc標準標頭檔案 如windows.h afxwin.h 預先編譯,以後該工程編譯時,不再編...
vuex namespaced的作用以及使用方式
vuex中的store分模組管理,需要在store的index.js中引入各個模組,為了解決不同模組命名衝突的問題,將不同模組的namespaced true,之後在不同頁面中引入getter actions mutations時,需要加上所屬的模組名 1 宣告分模組的store時加上namespa...