“萬能makefile”寫法詳解,一步一步寫一個實用的Makefile
提示:本文在原博文的基礎上做了一點點修改與完善,諸如原博文的後面有顯示不全的地方,自己已完善!
一
目的:編寫一個實用的makefile,能自動編譯當前目錄下所有.c/.cpp原始檔,支援二者混合編譯。並且當某個.c/.cpp、.h或依賴的原始檔被修改後,僅重編涉及到的原始檔,未涉及的不編譯。
二
要達到這個目的,用到的技術有:
1-使用wildcard函式來獲得當前目錄下所有.c/.cpp檔案的列表。
2-make的多目標規則。
3-make的模式規則。
4-用gcc -MM命令得到一個.c/.cpp檔案include了哪些檔案。(具體使用細節,請點選博文連結)
5-用sed命令對gcc -MM命令的結果作修改。
6-用include命令包含依賴描述檔案.d。
三 準備知識
(一)多目標
對makefile裡下面2行,可看出多目標特徵,執行make bigoutput或make littleoutput可看到結果:
bigoutput littleoutput: defs.h pub.h
@echo [email protected] $(subst output,OUTPUT,[email protected]) $^
註釋:[email protected]指該規則目標集合中能引起該規則執行的目標,$^指這個規則裡所有依賴的集合。
該行是把目標(bigoutput或littleoutput)裡的子串output替換成大寫的OUTPUT
(二)隱含規則
對makefile裡下面4行,可看出make的隱含規則,執行foo可看到結果:
第3、4行表示由.c得到.o,第1、2行表示由.o得到可執行檔案。
如果把第3、4行註釋的話,效果一樣。
即不寫.o來自.c的規則,它會自動執行gcc -c -o foo.o foo.c這條命令,由.c編譯出.o(其中-c表示只編譯不連結),然後自動執行gcc -o foo foo.o連結為可執行檔案。
foo:foo.o
gcc -o foo foo.o; ./foo #連結目標檔案成可執行檔案,並執行該檔案
foo.o:foo.c #註釋該行看效果
gcc -c foo.c -o foo.o #註釋該行看效果
(三)定義模式規則
下面定義了一個模式規則,即如何由.c檔案生成.d檔案的規則。
foobar: foo.d bar.d
@echo complete generate foo.d and bar.d
#make會對當前目錄下的每個.c檔案,依次該依賴規則裡面的命令,
使得每個.c檔案生成對應.d檔案。
%.d: %.c
@echo from $< to [email protected]
g++ -MM $< > [email protected]
假定當前目錄下有2個.c檔案:foo.c和bar.c(檔案內容隨意)。
驗證方法有2種,都可:
1-執行make foo.d(或make bar.d),表示想要生成foo.d這個目標。
根據規則%.d: %.c,這時%匹配foo,這樣%.c等於foo.c,即foo.d這個目標依賴於foo.c。
此時會自動執行該規則裡的命令gcc -MM foo.c > foo.d,來生成foo.d這個目標。
2-執行make foo bar,因為foo bar依賴於foo.d和bar.d這2個檔案,即會一次性生成這2個檔案。
四
下面詳述如何自動生成依賴性,從而實現本例的makefile。
(一)
本例使用了makefile的模式規則,目的是對當前目錄下每個.c檔案,生成其對應的.d檔案,例如由main.c生成的.d檔案內容為:
main.o : main.c command.h
這裡指示了main.o目標依賴於哪幾個原始檔,我們只要把這一行的內容,通過make的include指令包含到makefile檔案裡,即可在其任意一個依賴檔案被修改後,重新編譯目標main.o。
下面詳解如何生成這個.d檔案。
(二)
gcc/g++編譯器有一個-MM選項,可以對某個.c/.cpp檔案,分析其依賴的原始檔,例如假定main.c的內容為:
#include <stdio.h>//標準標頭檔案(以<>方式包含的),被-MM選項忽略,被-M選項收集
#include "stdlib.h"//標準標頭檔案(以""方式包含的),被-MM選項忽略,被-M選項收集
#include "command.h"
int main()
{
printf("##### Hello Makefile #####\n");
return 0;
}
則執行
gcc -MM main.c後,螢幕輸出:
main.o: main.c command.h
cc -M main.c後,螢幕輸出:
main.o: main.c /usr/include/stdio.h /usr/include/features.h \
/usr/include/bits/predefs.h /usr/include/sys/cdefs.h \
/usr/include/bits/wordsize.h /usr/include/gnu/stubs.h \
/usr/include/gnu/stubs-64.h \
/usr/lib/gcc/x86_64-linux-gnu/4.4.3/include/stddef.h \
/usr/include/bits/types.h /usr/include/bits/typesizes.h \
/usr/include/libio.h /usr/include/_G_config.h /usr/include/wchar.h \
/usr/lib/gcc/x86_64-linux-gnu/4.4.3/include/stdarg.h \
/usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h \
/usr/include/stdlib.h /usr/include/sys/types.h /usr/include/time.h \
/usr/include/endian.h /usr/include/bits/endian.h \
/usr/include/bits/byteswap.h /usr/include/sys/select.h \
/usr/include/bits/select.h /usr/include/bits/sigset.h \
/usr/include/bits/time.h /usr/include/sys/sysmacros.h \
/usr/include/bits/pthreadtypes.h /usr/include/alloca.h command.h
(三)
可見,只要把這些行挪到makefile裡,就能自動定義main.c的依賴是哪些檔案了,做法是把命令的輸出重定向到.d檔案裡:gcc -MM main.c > main.d,再把這個.d檔案include到makefile裡。(’>’是重定向符號,把生成的資料重定向寫入指定的檔案中)
如何include當前目錄每個.c生成的.d檔案:
#使用$(wildcard *.c)來獲取工作目錄下的所有.c檔案的列表。
sources:=$(wildcard *.c)
#這裡dependence是所有.d檔案的列表.即把sources串裡的.c換成.d。
dependence=$(sources:.c=.d)
#include後面可以跟若干個檔名,用空格分開,支援萬用字元,
include $(dependence)
----------
#例如:
#這裡是把所有.d檔案一次性全部include進來。**注意該句要放在終極目標all的規則
之後,否則.d檔案裡的規則會被誤當作終極規則了。**
include foo.make *.mk
(四)
現在main.c command.h這幾個檔案,任何一個改了都會重編main.o。但這裡還有一個問題,如果修改了command.h,即在command.h中加入#include “pub.h”,這時:
1-再make,由於command.h改了,這時會重編main.o,並且會使用新加的pub.h,看起來是正常的。
2-這時開啟main.d檢視,發現main.d中未加入pub.h,因為根據模式規則%.d: %.c中的定義,只有依賴的.c檔案變了,才會重新生成.d,而剛才改的是command.h,不會重新生成main.d、及不會在main.d中加入對pub.h的依賴關係,這樣一來會導致問題。
3-修改新加的pub.h的內容,再make,果然問題出現了,make報告up to date,沒有像期望那樣重編譯main.o。
**現在問題在於,main.d裡的某個.h檔案改了,沒有重新生成main.d。進一步說,main.d裡給出的每個依賴檔案,任何一個改了,都要重新生成這個main.d。
所以main.d也要作為一個目標來生成,它的依賴應該是main.d裡的每個依賴檔案,也就是說make裡要有這樣的定義:**
main.d: main.c command.h
這時我們發現,main.d與main.o的依賴是完全相同的,可以利用make的多目標規則,把main.d與main.o這兩個目標的定義合併為一句:
main.o main.d: main.c command.h
現在,main.o: main.c command.h這一句我們已經有了,如何進一步得到main.o main.d: main.c command.h呢?
(五)
解決方法是行內字串替換,對main.o,取出其中的子串main,加上.d字尾得到main.d,再插入到main.o後面。能實現這種替換功能的命令是sed。
實現的時候,先用gcc -MM命令生成臨時檔案main.d.temp,再用sed命令從該臨時檔案中讀出內容(用<重定向輸入)。做替換後,再用>輸出到最終檔案main.d。
命令可以這麼寫:
g++ -MM main.c > main.d.temp
sed 's,main\.o[ :]*,\1.o main.d : ,g' < main.d.temp > main.d
其中:
#指示sed命令從臨時檔案main.d.temp讀取輸入,作為命令的來源字串。
sed 's,\(main\)\.o[ :]*,\1.o main.d : ,g' #是sed命令。
< main.d.temp
> main.d #把行內替換結果輸出到最終檔案main.d。
(六)
1.這條sed命令的結構是s/match/replace/g。有時為了清晰,可以把每個/寫成逗號,
即這裡的格式s,match,replace,g。
2.該命令表示把源串內的match都替換成replace,s指示match可以是正則表示式。
g表示把每行內所有match都替換,如果去掉g,則只有每行的第1處match被替換
(實際上不需要g,因為一個.d檔案中,只會在開頭有一個main.o:)。
3.這裡match是正則式\(main\)\.o[ :]*,它分成3段:
第1段是main,在sed命令裡把main用括號括起來,使接下來的replace中可以用\1
引用main。
第2段是\.o,表示匹配main.o,(這裡\不知何意,去掉也是可以的)。
第3段是正則式[ :]*,表示若干個空格或冒號,(其實一個.d裡只會有一個冒號,
如果這裡寫成[ ]*:,即匹配若干個空格後跟一個冒號,也是可以的)。
總體來說match用來匹配’main.o :’這樣的串。
這裡的replace是\1.o main.d :,其中\1會被替換為前面第1個用括號括起的內容,即main,這樣replace值為main.o main.d :
這樣該sed命令就實現了把main.o :替換為main.o main.d :的目的。
這兩行實現了把臨時檔案main.d.temp的內容main.o : main.c command.h改為main.o main.d : main.c command.h,並存入main.d檔案的功能。
(七)
進一步修改,採用自動化變數。使得當前目錄下有多個.c檔案時,make會依次對每個.c檔案執行這段規則,生成對應的.d:
gcc -MM $< > [email protected].temp;
sed 's,$∗\.o[ :]*,\1.o [email protected] :,g' < [email protected].temp > [email protected];
(八)
現在來看上面2行的執行流程:
第一次make,假定這時從來沒有make過,所有.d檔案不存在,這時鍵入make:
1-include所有.d檔案的命令無效果。
2-首次編譯所有.c檔案。每個.c檔案中若#include了其它標頭檔案,會由編譯器自動讀取。由於這次是完整編譯,不存在什麼依賴檔案改了不會重編的問題。
3-對每個.c檔案,會根據依賴規則%.d: %.c,生成其對應的.d檔案,例如main.c生成的main.d檔案為:
main.o main.d: main.c command.h
第二次make,假定改了command.h、在command.h中加入#include “pub.h”,這時再make:
1-include所有.d檔案,例如include了main.d後,得到依賴規則:
main.o main.d: main.c command.h
注意所有include命令是首先執行的,make會先把所有include進來,再生成依賴規則關係。
2-此時,根據依賴規則,由於command.h的檔案戳改了,要重新生成main.o和main.d檔案。
3-先呼叫gcc -c main.c -o main.o生成main.o,
再呼叫gcc -MM main.c > main.d重新生成main.d。
此時main.d的依賴檔案裡增加了pub.h:
main.o main.d: main.c command.h pub.h
4-對其它依賴檔案沒改的.c(由其.d檔案得到),不會重新編譯.o和生成其.d。
5-最後會執行gcc $(objects) -o main生成最終可執行檔案。
第三次make,假定改了pub.h,再make。由於第二遍中,已把pub.h加入了main.d的依賴,此時會重編main.c,重新生成main.o和main.d。
這樣便實現了當前目錄下任一原始檔改了,自動編譯涉及它的.c。
(九)
進一步修改,得到目前大家普遍使用的版本:
set -e; rm -f [email protected]; \
$(CC) -MM $(CPPFLAGS) $< > [email protected].$$$$; \
sed 's,$∗\.o[ :]*,\1.o [email protected] : ,g' < [email protected].$$$$ > [email protected]; \
rm -f [email protected].$$$$
第一行,set -e表示,如果某個命令的返回引數非0,那麼整個程式立刻退出。
rm -f用來刪除上一次make時生成的.d檔案,因為現在要重新生成這個.d,
老的可以刪除了(不刪也可以)。
第二行:前面臨時檔案是用固定的.d.temp作為字尾,為了防止重名覆蓋掉有用的檔案,
這裡把temp換成一個隨機數,該數可用$$得到,其值是當前程序號。
由於$是makefile特殊符號,一個$要用$$來轉義,所以2個$要寫成$$$$
(你可以在makefile裡用'echo $$$$'來顯示程序號的值)。
第三行:sed命令的輸入也改成該臨時檔案.
每個shell命令的程序號通常是不同的,為了每次呼叫$$$$時得到的程序號相同,
必須把這4行放在一條命令中,這裡用分號把它們連線成一條命令
(在書寫時為了易讀,用\拆成了多行),這樣每次.便是同一個檔案了。
你可以在makefile裡用下面命令來比較:
@echo $$$$; \
echo $$$$;
@echo $$$$
輸出的結果是:(你輸出的結果很大可能不是以下值,也是正常的)
5879
5879
5880
**注意:第二行的echo前不能有@符號,否則會提示找不到該命令。原因在於@符號的作用
是執行命令之前不列印命令,而使用了接續符\,所以該行屬於上行,
若仍舊在該行行首新增@,等於在行首和行中添加了@,所以報錯了**
第四行:當make完後,每個臨時檔案.d.$$,已經不需要了,刪除之。
但每個.d檔案要在下一次make時被include進來,要保留。
(十)
綜合前面的分析,得到我們的makefile檔案:
#使用$(wildcard *.c)來獲取工作目錄下的所有.c檔案的列表
sources:=$(wildcard *.c)
objects:=$(sources:.c=.o)
#這裡,dependence是所有.d檔案的列表.即把sources串裡的.c換成.d
dependence:=$(sources:.c=.d)
#所用的編譯工具
CC=gcc
#當$(objects)列表裡所有檔案都生成後,便可呼叫這裡的 $(CC) $^ -o [email protected] 命令生成最終目標all了
#把all定義成第1個規則,使得可以把make all命令簡寫成make
all: $(objects)
$(CC) $^ -o [email protected]
#這段是make的模式規則,指示如何由.c檔案生成.o,即對每個.c檔案,呼叫gcc -c XX.c -o XX.o命令生成對應的.o檔案。
#如果不寫這段也可以,因為make的隱含規則可以起到同樣的效果
%.o: %.c
$(CC) -c $< -o [email protected]
include $(dependence) #要放在終極目標all的規則之後,否則.d檔案裡的規則會被誤當作終極規則了
%.d: %.c
set -e; rm -f [email protected]; \
$(CC) -MM $(CPPFLAGS) $< > [email protected].$$$$; \
sed 's,$∗\.o[ :]*,\1.o [email protected] :,g' < [email protected].$$$$ > [email protected]; \
rm -f [email protected].$$$$
.PHONY: clean #之所以把clean定義成偽目標,是因為這個目標並不對應實際的檔案
clean:
rm -f all $(objects) $(dependence) #清除所有.o和.d檔案。-f表示被刪檔案不存在時不報錯
(十一)
上面這個makefile已經能正常工作了(編譯C程式),但如果要用它編譯C++,變數CC值要改成g++,每個.c都要改成.cpp,有點繁瑣。現在我們繼續完善它,使其同時支援C和C++,並支援二者的混合編譯。
#一個實用的makefile,能自動編譯當前目錄下所有.c/.cpp原始檔,支援二者混合編譯
#並且當某個.c/.cpp、.h或依賴的原始檔被修改後,僅重編涉及到的原始檔,未涉及的不編譯
#詳解文件:http://blog.csdn.net/huyansoft/article/details/8924624
#author:胡彥 2013-5-21
#----------------------------------------------------------
#編譯工具用g++,以同時支援C和C++程式,以及二者的混合編譯
CC=g++
#使用$(winldcard *.c)來獲取工作目錄下的所有.c檔案的列表
#sources:=main.cpp command.c
#變數sources得到當前目錄下待編譯的.c/.cpp檔案的列表,
#兩次呼叫winldcard、結果連在一起即可
sources:=$(wildcard *.c) $(wildcard *.cpp)
#變數objects得到待生成的.o檔案的列表,把sources中每個檔案的副檔名換成.o即可。
#這裡兩次呼叫patsubst函式,第1次把sources中所有.cpp換成.o,
#第2次把第1次結果裡所有.c換成.o
objects:=$(patsubst %.c,%.o,$(patsubst %.cpp,%.o,$(sources)))
#變數dependence得到待生成的.d檔案的列表,把objects中每個副檔名.o換成.d即可。
#也可寫成$(patsubst %.o,%.d,$(objects))
dependence:=$(objects:.o=.d)
#----------------------------------------------------------
#當$(objects)列表裡所有檔案都生成後,便可呼叫這裡的 $(CC) $^ -o [email protected] 命令
#生成最終目標all了
#把all定義成第1個規則,使得可以把make all命令簡寫成make
all: $(objects)
$(CC) $(CPPFLAGS) $^ -o [email protected]
@./[email protected] #編譯後立即執行
#這段使用make的模式規則,指示如何由.c檔案生成.o,即對每個.c檔案,呼叫gcc -c XX.c -o XX.o命令生成對應的.o檔案
#如果不寫這段也可以,因為make的隱含規則可以起到同樣的效果
%.o: %.c
$(CC) $(CPPFLAGS) -c $< -o [email protected]
#同上,指示如何由.cpp生成.o,可省略
%.o: %.cpp
$(CC) $(CPPFLAGS) -c $< -o [email protected]
#----------------------------------------------------------
include $(dependence) #要放在終極目標all的規則之後,否則.d檔案裡的規則會被誤當作終極規則了
#因為這4行命令要多次凋用,定義成命令包以簡化書寫
define gen_dep
set -e; rm -f [email protected]; \
$(CC) -MM $(CPPFLAGS) $< > [email protected].$$$$; \
sed 's,$∗\.o[ :]*,\1.o [email protected] : ,g' < [email protected].$$$$ > [email protected]; \
rm -f [email protected].$$$$
endef
#指示如何由.c生成其依賴規則檔案.d
#這段使用make的模式規則,指示對每個.c檔案,如何生成其依賴規則檔案.d,
#呼叫上面的命令包即可
%.d: %.c
$(gen_dep)
#同上,指示對每個.cpp,如何生成其依賴規則檔案.d
%.d: %.cpp
$(gen_dep)
#----------------------------------------------------------
#清除所有臨時檔案(所有.o和.d)。之所以把clean定義成偽目標,是因為這個目標並不對應實際的檔案
.PHONY: clean
clean: #.$$已在每次使用後立即刪除。-f引數表示被刪檔案不存在時不報錯
rm -f all $(objects) $(dependence)
echo: #除錯時顯示一些變數的值
@echo sources=$(sources)
@echo objects=$(objects)
@echo dependence=$(dependence)
@echo CPPFLAGS=$(CPPFLAGS)
#提醒:當混合編譯.c/.cpp時,為了能夠在C++程式裡呼叫C函式,必須把每一個要呼叫的
#C函式,其宣告都包括在extern "C"{}塊裡面,這樣C++連結時才能成功連結它們。
五
makefile學習體會:
剛學過C語言的讀者,可能會覺得makefile有點難,因為makefile不像C語言那樣,一招一式都那麼清晰明瞭。在makefile裡到處是“潛規則”,都是一些隱晦的東西,要弄明白只有搞清楚這些“潛規則”。
基本的規則無非是“一個依賴改了,去更新哪些目標”。
正因為隱晦動作較多,寫成一個makefile才不需要那麼多篇幅,畢竟專案程式碼才是主體。只要知道makefile的框架,往它的套路里填就行了。
較好的學習資料是《跟我一起寫Makefile.pdf》這篇文件(下載包裡已經附帶了),比較詳細,適合初學者。我們學習的目的是,能夠編寫一個像本文這樣的makefile,以滿足簡單專案的基本需求,這要求理解前面makefile幾個關鍵點:
1-多目標
2-隱含規則
3-定義模式規則
4-自動生成依賴性
可惜的是,這篇文件雖然比較全面,卻沒有以一個完整的例子為引導,對幾處要點沒有突出指明,尤其是“定義模式規則”在最後不顯眼的位置(第十一部分第五點),導致看了“自動生成依賴性”一節後還比較模糊。所以,看了《跟我一起寫Makefile.pdf》後,再結合本文針對性的講解,會有更實際的收穫。
另一個學習資料是《GNU make v3.80中文手冊v1.5.pdf》,這個手冊更詳細,但較枯燥,不適合完整學習,通常是遇到問題再去查閱。
[END]
相關推薦
萬能makefile寫法詳解,一步一步寫一個實用的makefile,詳解 sed 's,\($*\)\.o[ :]*,\1.o [emai
一 目的:編寫一個實用的makefile,能自動編譯當前目錄下所有.c/.cpp原始檔,支援二者混合編譯。並且當某個.c/.cpp、.h或依賴的原始檔被修改後,僅重編涉及到的原始檔,未涉及的不編譯。 二 要達到這個目的,用到的技術有: 1-使用wildcard函式來獲得當
“萬能makefile”寫法詳解,一步一步寫一個實用的Makefile
提示:本文在原博文的基礎上做了一點點修改與完善,諸如原博文的後面有顯示不全的地方,自己已完善! 一 目的:編寫一個實用的makefile,能自動編譯當前目錄下所有.c/.cpp原始檔,支援二者混合編譯。並且當某個.c/.cpp、.h或依賴的原始檔
【K8S】Service服務詳解,看這一篇就夠了!!
k8s用名稱空間namespace把資源進行隔離,預設情況下,相同的名稱空間裡的服務可以相互通訊,反之進行隔離。 1.1 Service Kubernetes中一個應用服務會有一個或多個例項(Pod,Pod可以通過rs進行多複本的建立),每個例項(Pod)的IP地址由網路外掛動態隨機分配(Pod重啟後IP地址
Java 8 中新的 Date 和 Time 類入門詳解, DateUtil ,以後可以少寫幾個了,關鍵是執行緒安全了
這篇文章主要是java8中新的Date和Time API的實戰。新的Date和Time類是java開發者社群千呼萬喚始出來的。Java8 之前存在的Date類一直都受人詬病,很多人都會選擇使用第三方的date庫joda-time。Java8中的date和time api
keepalived詳解,一篇讓你入門
Keepalived介紹 轉:詳細請看http://blog.51cto.com/zephiruswt/1235852 Keepalived是一款高可用軟體,它的功能主要包括兩方面: 1)通過IP漂移,實現服務的高可用:伺服器叢集共享一個虛擬IP,同一時間只有一個伺服器佔有虛擬IP並
js實現快速排序,二分查詢 (詳解,一次學會)
js中幾大演算法,最近看到網上各路大神的解答,都蠻好的,自己也來玩一玩 一,快速排序 大致分三步: 在資料集之中,選擇一個元素作為"基準"(pivot)。 所有小於"基準"的元素,都移到"基準"的左邊;所有大於"基準"的元素,都移到"基準"的右邊。 對"基準"左邊和右邊的兩個子集,不斷重複
WaitForMultipleObjects用法詳解,一看就懂
原文:https://blog.csdn.net/sac761/article/details/52456385 WaitForMultipleObjects是Windows中的一個功能非常強大的函式,幾乎可以等待Windows中的所有的核心物件。 函式原型為: DWORD Wait
IIC詳解,包括原理、過程,最後一步步教你實現IIC
IIC詳解 1、I2C匯流排具有兩根雙向訊號線,一根是資料線SDA,另一根是時鐘線SCL 2、IIC總線上可以掛很多裝置:多個主裝置,多個從裝置(外圍 裝置)。上圖中主裝置是兩個微控制器,剩下的都是從裝置。 3、多主機會產生匯流排裁決問題。當多個主機同時想佔用匯
cron表示式詳解,cron表示式寫法,cron表示式例子
cron表示式格式: {秒數} {分鐘} {小時} {日期} {月份} {星期} {年份(可為空)} 例 "0 0 12 ? * WED" 在每星期三下午12:00 執行(年份通常 省略) 先了解每個位置代表的含義,在瞭解每個位置允許的範圍,以及一些特殊寫法,還有常用的案例,足夠你掌握c
BP神經網路計算過程詳解,用筆手算一遍弄懂反向傳播
手算BP神經網路 現在很多人都說,做it門檻很低,腦子靈活點,願意去熬的,培訓個幾個月就可以,無非是調調函式而已。 確實,現在一些程式設計師的工作,調調函式掌握得好的話,也是能夠勝任的。但是,想要更進一步,還得不斷提升自己,努力理解各種演算法結構。 (類)
[轉載]最詳盡的 JS 原型與原型鏈終極詳解,沒有「可能是」。(一)
三篇文章都更新完畢,完整的剖析了 JS 原型與原型鏈,希望通過這些教程能讓你對 Javascript 這門語言理解的更透徹!一. 普通物件與函式物件JavaScript 中,萬物皆物件!但物件也是有區別的。分為普通物件和函式物件,Object 、Function 是 JS 自帶的函式物件。下面舉例說明var
漢諾塔問題原始碼詳解,一種比較形象的輸出顯示結果
// Hanoi.cpp : Defines the entry point for the console application. // #include <stdafx.h> #include "stdio.h" #include "iostream.h
163vip郵箱註冊及登陸方法詳解,一分鐘秒懂!
fff vpd 註冊 是什麽 oss pro shadow water tom 高效辦公,缺了郵箱可不行,163vip郵箱的註冊及登陸方法是什麽呢?一分鐘!用吃一片厚切牛舌的時間帶你秒懂!註冊 1、在百度搜索TOMvip郵箱,點擊進入2、點擊屏幕右側的“立即註冊按鈕”3、選
ActiveMQ訊息傳送機制以及ACK機制詳解 AcitveMQ是作為一種訊息儲存和分發元件,涉及到client與broker端資料互動的方方面面,它不僅要擔保訊息的儲存安全性,還要提供額外的
AcitveMQ是作為一種訊息儲存和分發元件,涉及到client與broker端資料互動的方方面面,它不僅要擔保訊息的儲存安全性,還要提供額外的手段來確保訊息的分發是可靠的。 一. ActiveMQ訊息傳送機制 Producer客戶端使用來發送訊息的, Consumer客戶端用來消費
扒一扒使用boostrap-fileinput上傳外掛遇到的坑,Bootstrap-fileinput上傳外掛的使用詳解,
由於公司專案的需求,需要實現動植物名錄的新增,包括姓名等資訊和圖片等,需要使用bootstrap-fileinput的上傳外掛,在提交新增介面表單資料的同時上傳一張或者多張圖片,並將上傳的圖片儲存到本地磁碟中(本文是f:盤的目錄下),在在實現的時候,不適用bootstrap-
關於AIDL使用和Binder機制詳解,你只需要看這一篇即可
本篇文章從AIDL的角度來闡述Binder機制呼叫遠端服務的內部執行原理。因此本篇文章的第一部分介紹AIDL的使用,第二部分從AIDL的使用上具體介紹Binder機制。關於Binder機制的原理,可以參考簡單理解Binder機制的原理,對其有個大概的瞭解。 一、AIDL
Hibernate 初相識(一)Hibernate API 詳解,配置檔案,對映檔案詳解。
導航{ } Hibernate 在三層框架中的位置。 一,第一個Hibernate 專案。 地址:https://github.com/gengzi/Hibernate 【1】匯入jar包 解
轉載 最詳盡的 JS 原型與原型鏈終極詳解,沒有「可能是」。(一)
-s 繼續 沒有 clas 程序設計 target 普通 fop 曾經 轉自:https://www.jianshu.com/p/dee9f8b14771 三篇文章都更新完畢,完整的剖析了 JS 原型與原型鏈,希望通過這些教程能讓你對 Javascript 這門語言
JVM類載入機制詳解,建議看這一篇就夠了,深入淺出總結的十分詳細!
## 類載入機制 虛擬機器把描述類的資料從Class檔案載入到記憶體,並對資料進行校驗、轉換解析和初始化,最終形成可以被虛擬機器直接使用的Java型別,這就是虛擬機器的類載入機制。 ## 類載入的時機 * 遇到new(比如new Student())、getstatic和putstatic(讀取或設定
POI操作Excel詳解,讀取xls和xlsx格式的文件
shee xss split 類型 後綴 .sh lan xls lin package org.ian.webutil; import java.io.File; import java.io.FileInputStream; import java.io.FileN